Improve videos

This commit is contained in:
Andras Schmelczer 2026-06-10 21:28:19 +01:00
parent 4012e4e047
commit d3418c67cc
11 changed files with 988 additions and 869 deletions

View file

@ -59,9 +59,79 @@ function emitScript(storyboard: Storyboard): string {
return path;
}
function main(): void {
/**
* Validate every stubbed/initial filter name and travel-destination slug
* against the LIVE API. Wrong names don't error in the app they silently
* no-op, the map never changes, and you only find out after a full render.
* Fails hard on a mismatch; soft-warns if the API is unreachable (render.sh
* has already health-checked it by the time preflight runs).
*/
async function validateAgainstLiveApi(): Promise<void> {
const apiBase = process.env.API_URL ?? process.env.APP_URL;
if (!apiBase) {
console.warn('[preflight] no API_URL/APP_URL set — skipping live filter validation');
return;
}
let featureNames: Set<string>;
try {
const res = await fetch(`${apiBase}/api/features`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = (await res.json()) as {
groups: { features: { name: string }[] }[];
};
featureNames = new Set(body.groups.flatMap((g) => g.features.map((f) => f.name)));
} catch (err) {
console.warn(`[preflight] could not fetch ${apiBase}/api/features (${err}) — skipping validation`);
return;
}
const problems: string[] = [];
for (const sb of storyboards) {
const filterNames = [
...Object.keys(sb.content.stubbedFilters),
...Object.keys(sb.content.initialFilters ?? {}),
];
for (const name of filterNames) {
if (!featureNames.has(name)) {
problems.push(`[${sb.name}] filter "${name}" is not a live /api/features name`);
}
}
}
const modes = new Set(
storyboards.flatMap((sb) => sb.content.stubbedTravelTimeFilters.map((tt) => tt.mode))
);
for (const mode of modes) {
try {
const res = await fetch(`${apiBase}/api/travel-destinations?mode=${mode}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = (await res.json()) as { destinations: { slug: string }[] };
const slugs = new Set(body.destinations.map((d) => d.slug));
for (const sb of storyboards) {
for (const tt of sb.content.stubbedTravelTimeFilters) {
if (tt.mode === mode && !slugs.has(tt.slug)) {
problems.push(`[${sb.name}] travel destination "${tt.slug}" (${mode}) not on live API`);
}
}
}
} catch (err) {
console.warn(`[preflight] could not validate travel destinations for ${mode}: ${err}`);
}
}
if (problems.length > 0) {
for (const p of problems) console.error(`[preflight] FAIL: ${p}`);
process.exit(1);
}
console.log('[preflight] all stubbed filter names and travel slugs match the live API');
}
async function main(): Promise<void> {
if (!existsSync(OUTPUT_DIR)) mkdirSync(OUTPUT_DIR, { recursive: true });
await validateAgainstLiveApi();
for (const sb of storyboards) emitScript(sb);
// Index for shell loops — each entry has every field render.sh needs to
@ -84,4 +154,7 @@ function main(): void {
console.log(`[preflight] wrote storyboard index → ${indexPath}`);
}
main();
main().catch((err) => {
console.error(err);
process.exit(1);
});