226 lines
6.5 KiB
TypeScript
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] },
|
|
},
|
|
};
|
|
}
|