fix(web): harden spa foundation

refs #95
This commit is contained in:
2026-07-02 18:36:56 +02:00
parent 032b9d8116
commit 97b6c4bee7
10 changed files with 213 additions and 190 deletions
+24 -44
View File
@@ -1,6 +1,7 @@
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import process from 'node:process';
import { fileURLToPath, pathToFileURL } from 'node:url';
const scriptDir = dirname(fileURLToPath(import.meta.url));
const webRoot = resolve(scriptDir, '..');
@@ -8,9 +9,6 @@ const repoRoot = resolve(webRoot, '..');
const inputPath = resolve(repoRoot, 'ErsatzTV/wwwroot/openapi/v1.json');
const outputPath = resolve(webRoot, 'src/api/generated/v1.d.ts');
const document = JSON.parse(await readFile(inputPath, 'utf8'));
const schemas = document.components?.schemas ?? {};
function schemaNameFromRef(ref) {
return ref.replace('#/components/schemas/', '');
}
@@ -92,52 +90,34 @@ function objectTypeFromSchema(schema) {
return lines.join('\n');
}
function operationType(operation) {
if (!operation) {
return 'never';
export function generateTypes(document) {
const schemas = document.components?.schemas ?? {};
const lines = [
'// This file is generated by web/scripts/generate-openapi-types.mjs.',
'// Do not edit by hand.',
'',
'export interface components {',
' schemas: {'
];
for (const [name, schema] of Object.entries(schemas).sort(([a], [b]) => a.localeCompare(b))) {
lines.push(`${formatDescription(schema.description, ' ')} ${JSON.stringify(name)}: ${typeFromSchema(schema)};`);
}
const responses = operation.responses ?? {};
const successResponse = responses['200'] ?? responses['201'] ?? responses['204'];
const jsonContent = successResponse?.content?.['application/json']
?? successResponse?.content?.['text/json']
?? successResponse?.content?.['text/plain'];
lines.push(' };');
lines.push('}');
lines.push('');
if (!jsonContent?.schema) {
return 'void';
}
return typeFromSchema(jsonContent.schema);
return `${lines.join('\n')}\n`;
}
const lines = [
'// This file is generated by web/scripts/generate-openapi-types.mjs.',
'// Do not edit by hand.',
'',
'export interface components {',
' schemas: {'
];
async function main() {
const document = JSON.parse(await readFile(inputPath, 'utf8'));
for (const [name, schema] of Object.entries(schemas).sort(([a], [b]) => a.localeCompare(b))) {
lines.push(`${formatDescription(schema.description, ' ')} ${JSON.stringify(name)}: ${typeFromSchema(schema)};`);
await mkdir(dirname(outputPath), { recursive: true });
await writeFile(outputPath, generateTypes(document));
}
lines.push(' };');
lines.push('}');
lines.push('');
lines.push('export interface operations {');
for (const [path, methods] of Object.entries(document.paths ?? {}).sort(([a], [b]) => a.localeCompare(b))) {
for (const [method, operation] of Object.entries(methods).sort(([a], [b]) => a.localeCompare(b))) {
const name = operation.operationId ?? `${method.toUpperCase()} ${path}`;
lines.push(` ${JSON.stringify(name)}: {`);
lines.push(` response: ${operationType(operation)};`);
lines.push(' };');
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
await main();
}
lines.push('}');
lines.push('');
await mkdir(dirname(outputPath), { recursive: true });
await writeFile(outputPath, `${lines.join('\n')}\n`);