Lumen/pipeline/manifest.ts
Lumen Stage1 fcc18ddcc8
Some checks failed
ci / check (push) Has been cancelled
Checkpoint: current Lumen state
2026-09-23 18:58:21 -05:00

53 lines
1.7 KiB
TypeScript

/**
* Manifest generation — deterministic, per SPIKE-04.
* Trace: SPIKE-04 §2, ARCH 10.2, IMPLEMENTATION-CONTRACT.md §15
*/
import type { FestivalManifest, SectionId } from "../src/data/festival-package/types.js";
import type { PipelineInput, SectionFile } from "./types.js";
export function buildManifest(
input: PipelineInput,
files: ReadonlyMap<string, SectionFile>,
totalBytes: number,
): FestivalManifest {
const counts = {
events: input.content.schedule.events.length,
pois: input.content.map.pois.length,
assets: input.content.assets.assets.length,
};
const sections: Record<
SectionId,
{ file: string; sha256: string; bytes: number; required: boolean }
> = {
emergency: entryFor(files, "emergency.json", true),
schedule: entryFor(files, "schedule.json", true),
map: entryFor(files, "map.json", true),
info: entryFor(files, "info.json", true),
assets: entryFor(files, "assets.json", true),
};
// Include optional sections if present in files and content has them (future)
// For now, only required 5.
const manifest: FestivalManifest = {
format: "lumen.package/1",
edition: input.edition,
packageVersion: input.packageVersion,
schemaVersion: input.schemaVersion,
generatedAt: input.generatedAt,
festival: { ...input.festival },
appCompatibility: { ...input.appCompatibility },
sections,
counts,
limits: { totalBytes },
};
return manifest;
}
function entryFor(
files: ReadonlyMap<string, SectionFile>,
file: string,
required: boolean,
): { file: string; sha256: string; bytes: number; required: boolean } {
const f = files.get(file);
if (!f) throw new Error(`manifest missing file ${file}`);
return { file, sha256: f.sha256, bytes: f.bytes, required };
}