Files
makelore/electron/opencode/works-publish-file.ts
2026-07-29 17:22:35 +08:00

86 lines
2.5 KiB
TypeScript

import { constants } from 'node:fs';
import { access, readFile } from 'node:fs/promises';
import { extname, join } from 'node:path';
export const WORKS_PUBLISH_FILE_NAME = 'works-publish.json';
export type WorksPublishData = {
app_id: string;
title: string;
summary: string;
category: string;
age_band: string;
difficulty: string;
version_name: string;
change_log: string;
zip_file_path: string;
};
export type WorksPublishReadResult =
| { status: 'ready'; filePath: string; publish: WorksPublishData }
| { status: 'missing'; filePath: string }
| { status: 'invalid'; filePath: string; error: string };
function readString(record: Record<string, unknown>, key: string): string {
const value = record[key];
return typeof value === 'string' ? value.trim() : '';
}
function readRequiredString(record: Record<string, unknown>, key: string): string {
const value = readString(record, key);
if (!value) {
throw new Error(`Missing ${key} in ${WORKS_PUBLISH_FILE_NAME}`);
}
return value;
}
function parsePublishRecord(value: unknown): WorksPublishData {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`${WORKS_PUBLISH_FILE_NAME} must contain a JSON object`);
}
const record = value as Record<string, unknown>;
const appId = readRequiredString(record, 'app_id');
const zipFilePath = readRequiredString(record, 'zip_file_path');
if (extname(zipFilePath).toLowerCase() !== '.zip') {
throw new Error('zip_file_path must point to a .zip file');
}
return {
app_id: appId,
title: readRequiredString(record, 'title'),
summary: readRequiredString(record, 'summary'),
category: readRequiredString(record, 'category'),
age_band: readRequiredString(record, 'age_band'),
difficulty: readRequiredString(record, 'difficulty'),
version_name: readRequiredString(record, 'version_name'),
change_log: readRequiredString(record, 'change_log'),
zip_file_path: zipFilePath,
};
}
export async function readWorksPublishFile(projectPath: string): Promise<WorksPublishReadResult> {
const filePath = join(projectPath, WORKS_PUBLISH_FILE_NAME);
try {
await access(filePath, constants.F_OK);
} catch {
return { status: 'missing', filePath };
}
try {
const raw = await readFile(filePath, 'utf8');
return {
status: 'ready',
filePath,
publish: parsePublishRecord(JSON.parse(raw) as unknown),
};
} catch (error) {
return {
status: 'invalid',
filePath,
error: error instanceof Error ? error.message : String(error),
};
}
}