Lumen/pipeline/fixtures.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

226 lines
6.5 KiB
TypeScript

/**
* Synthetic fixtures for Stage 6 — provisional placeholder, not production content.
* Budgets respected: sections ≤3MB, total ≤40MB, floor ≤16KB.
* Trace: IMPLEMENTATION-CONTRACT.md Stage 6 — test edition lumen-2026
*/
import type { PipelineInput } from "./types.js";
import type { EmergencySource } from "./types.js";
import { dayKeyFor } from "../src/domain/clock/logic.js";
const FESTIVAL_TZ = "America/Chicago";
const START_UTC = Date.UTC(2026, 8, 10, 16, 0, 0); // 2026-09-10
const END_UTC = Date.UTC(2026, 8, 13, 22, 0, 0);
function emergencySource(): EmergencySource {
return {
section: "emergency",
emergencySchemaVersion: 1,
contentVersion: 3,
updatedAt: "2026-08-27T09:00:00Z",
services: {
emergencyNumber: "911",
security: { phone: "+1-555-0142", location: "Main Gate Kiosk" },
firstAid: { location: "Behind Stage B", hours: "10:00-02:00" },
},
locations: {
musterPoints: [{ id: "mp-1", name: "North Field", poi: "poi-muster-n" }],
exits: [{ id: "ex-1", name: "East Gate", poi: "poi-exit-e" }],
aeds: [{ poi: "poi-aed-1" }],
},
address: {
lines: ["123 Festival Way", "Austin, TX 78701"],
coordinates: { lat: 30.2672, lon: -97.7431 },
},
procedures: [
{ id: "weather", title: "Severe Weather", steps: ["Seek shelter", "Follow staff"] },
{ id: "medical", title: "Medical Emergency", steps: ["Call 911", "Locate first aid"] },
],
};
}
export function makeValidInput(overrides?: Partial<PipelineInput>): PipelineInput {
const emergency = emergencySource();
const schedule = {
section: "schedule" as const,
stages: [
{ id: "stage-a", name: "Main Stage" },
{ id: "stage-b", name: "Second Stage" },
],
artists: [
{ id: "art-1", name: "The Luminants" },
{ id: "art-2", name: "Solar Echo" },
],
events: [
{
id: "evt-0001",
title: "Opening Ceremony",
stageId: "stage-a",
artistIds: ["art-1"],
startUtc: START_UTC + 2 * 3600_000,
endUtc: START_UTC + 3 * 3600_000,
dayKey: dayKeyFor(START_UTC + 2 * 3600_000, FESTIVAL_TZ),
tags: ["ceremony"],
status: "scheduled" as const,
},
{
id: "evt-0002",
title: "Midday Set",
stageId: "stage-b",
artistIds: ["art-2"],
startUtc: START_UTC + 5 * 3600_000,
endUtc: START_UTC + 6 * 3600_000,
dayKey: dayKeyFor(START_UTC + 5 * 3600_000, FESTIVAL_TZ),
tags: ["live"],
status: "scheduled" as const,
},
{
id: "evt-0003",
title: "Night Finale",
stageId: "stage-a",
artistIds: ["art-1", "art-2"],
startUtc: START_UTC + 10 * 3600_000,
endUtc: START_UTC + 11 * 3600_000,
dayKey: dayKeyFor(START_UTC + 10 * 3600_000, FESTIVAL_TZ),
tags: ["finale"],
status: "scheduled" as const,
},
],
};
const map = {
section: "map" as const,
base: {
levels: [
{ id: "overview", assetId: "map-base-overview", width: 1600, height: 1200 },
{ id: "detail", assetId: "map-base-detail", width: 3072, height: 2304 },
],
},
pois: [
{
id: "poi-muster-n",
name: "Muster North",
category: "muster" as const,
x: 0.2,
y: 0.3,
lat: null,
lng: null,
},
{
id: "poi-exit-e",
name: "East Gate",
category: "exit" as const,
x: 0.9,
y: 0.5,
lat: null,
lng: null,
},
{
id: "poi-aed-1",
name: "AED — Info Tent",
category: "aed" as const,
x: 0.412,
y: 0.633,
lat: null,
lng: null,
},
],
categories: ["muster", "exit", "aed", "stage", "other"] as const as readonly (
"muster" | "exit" | "aed" | "stage" | "other"
)[],
} as unknown as PipelineInput["content"]["map"];
const info = {
section: "info" as const,
blocks: [
{
id: "blk-about",
title: "About",
kind: "festival",
body: [{ kind: "paragraph" as const, text: "Welcome to Lumen 2026" }],
},
{
id: "blk-rules",
title: "Rules",
kind: "rules",
body: [{ kind: "list" as const, items: ["No glass", "Respect neighbors"] }],
},
],
};
const blobs = new Map<string, Uint8Array>([
["map-base-overview", new TextEncoder().encode("fake-overview-webp")],
["map-base-detail", new TextEncoder().encode("fake-detail-webp-larger-but-small")],
]);
const assets = {
assets: [
{
id: "map-base-overview",
file: "assets/map-base-overview.webp",
kind: "map-base" as const,
role: "overview",
sha256: "",
bytes: 0,
},
{
id: "map-base-detail",
file: "assets/map-base-detail.webp",
kind: "map-base" as const,
role: "detail",
sha256: "",
bytes: 0,
},
],
blobs,
};
// sha256/bytes will be recomputed by builder; placeholders ok
const base: PipelineInput = {
edition: "lumen-2026",
packageVersion: 1,
schemaVersion: 1,
generatedAt: "2026-08-28T14:02:11Z",
festival: {
name: "Lumen Festival 2026",
timezone: FESTIVAL_TZ,
startUtc: START_UTC,
endUtc: END_UTC,
},
appCompatibility: { minAppVersion: "1.0.0", maxAppVersion: null },
content: {
emergency,
schedule: schedule as PipelineInput["content"]["schedule"],
map: map as PipelineInput["content"]["map"],
info,
assets,
},
previous: null,
previousPackageVersion: null,
};
if (overrides) {
return {
...base,
...overrides,
content: overrides.content ?? base.content,
festival: overrides.festival ?? base.festival,
appCompatibility: overrides.appCompatibility ?? base.appCompatibility,
};
}
return base;
}
export function makeNextVersion(prev: PipelineInput, newVersion: number): PipelineInput {
// Clone prev content but keep stable IDs; bump version
const nextBase = makeValidInput({
packageVersion: newVersion,
previousPackageVersion: prev.packageVersion,
previous: {
packageVersion: prev.packageVersion,
schedule: prev.content.schedule,
map: prev.content.map,
},
});
// Preserve same events (stable IDs) — caller can mutate via shallow copy
return {
...nextBase,
content: {
...nextBase.content,
schedule: { ...nextBase.content.schedule, events: [...prev.content.schedule.events] },
},
};
}