86 lines
2.5 KiB
TypeScript
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),
|
|
};
|
|
}
|
|
}
|