This commit is contained in:
parent
8e71537d57
commit
fcc18ddcc8
96 changed files with 8074 additions and 214 deletions
226
pipeline/fixtures.ts
Normal file
226
pipeline/fixtures.ts
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
/**
|
||||
* 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] },
|
||||
},
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue