export type ScenarioValue = | null | boolean | number | string | readonly ScenarioValue[] | { readonly [key: string]: ScenarioValue }; export type ScenarioStep = Readonly<{ readonly kind: "step"; readonly id: string; readonly input?: ScenarioValue; readonly dependsOn: readonly string[]; readonly capabilities: readonly string[]; readonly extension?: string; readonly runnerBinding?: string; }>; export type ScenarioBarrier = Readonly<{ readonly kind: "barrier"; readonly id: string; readonly parties: readonly string[]; readonly budget: number; }>; export type ScenarioFault = Readonly<{ readonly kind: "fault"; readonly id: string; readonly phase: | "before-task" | "during-task" | "after-task" | "after-effect-before-journal" | "after-journal-before-ack" | "after-ack"; readonly operation: | "task" | "effect" | "attempt-write" | "event-append" | "completion-cas" | "heartbeat" | "lease" | "resume" | "cancellation"; readonly outcome?: ScenarioValue; }>; export type ScenarioExtension = Readonly<{ readonly kind: "extension"; readonly name: string; readonly value: ScenarioValue; }>; export type ScenarioAst = Readonly<{ readonly version: 1; readonly name: string; readonly seed?: number; readonly steps: readonly ScenarioStep[]; readonly barriers: readonly ScenarioBarrier[]; readonly faults: readonly ScenarioFault[]; readonly extensions: readonly ScenarioExtension[]; }>; export const freezeScenario = (value: T): Readonly => { for (const child of Object.values(value)) if (child && typeof child === "object" && !Object.isFrozen(child)) freezeScenario(child as object); return Object.freeze(value); };