@patterkit/runtime 0.4.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engine.ts","../src/tags.ts","../src/describe.ts","../src/gamedata.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// @patterkit/runtime - the reference runtime.\n//\n// An `Engine` is the world + flow manager: it owns the compiled Bundle, the\n// shared state (shared `@patter` globals + shared `@scene` props + host foreign\n// scopes), and a set of named **flows**. All *play* happens on a `Flow` handle\n// (`engine.openFlow(id, ...)`): a flow has its own execution cursor, its own PRNG,\n// and its own copy of the NOT-shared state (per-flow `@patter` globals + per-flow\n// `@scene` props). Multiple flows run concurrently and independently - a flow is\n// addressed explicitly (`alice.advance()`), so there is no ambient \"current flow\".\n//\n// Scopes are just two tokens - `@patter` (global; bare `@name`) and `@scene`\n// (scene-local) - with an orthogonal per-property `shared` flag (default: shared\n// for `@patter`, per-flow for `@scene`). So each token spans two storage areas:\n// the shared half lives on the engine, the per-flow half on the flow; a read/write\n// routes by the property's `shared` flag. (Mirrors Storylet Studio's @world/@site.)\n//\n// A flow plays the select-one-child model (spec §4): a block branches one\n// eligible child; a group runs its selector (branch / sequence / choice -\n// `sequence` covers order x exhaust); a `choice` group stops for the host; a snippet runs onEnter, delivers\n// beats, runs onExit, follows its jump. Falling off the end ends the flow.\n// Cross-flow jumps are not a thing - the host switches flows.\n//\n// `engine.saveGame()` / `loadGame()` snapshot + restore the WHOLE game: `@patter`\n// plus every live flow's scopes + PRNG + cursor.\n// ---------------------------------------------------------------------------\n\nimport { evaluate, deserialiseAst } from \"@wildwinter/expr\";\nimport type { ScalarValue, EvalContext, ExprNode } from \"@wildwinter/expr\";\nimport { matchedSpecificity as scoreSpecificity, type EvalTruthy } from \"@wildwinter/expr-specificity\";\nimport { ScopeRegistry } from \"@wildwinter/scoperegistry\";\nimport type { ScopeDeclaration, ScopeResolver } from \"@wildwinter/scoperegistry\";\nimport { patterDialect, interpolate, splitRef, stripCaptions } from \"@patterkit/dialect\";\nimport { walkNodes, effectiveGameId, castStringKey, DEFAULT_CAPTION_DELIMITERS, DEFAULT_CAPTION_CHARACTER } from \"@patterkit/model\";\nimport { buildTagIndex } from \"./tags.js\";\nimport type {\n Bundle, CompiledScene, CompiledBlock, CompiledGroup, CompiledSnippet,\n CompiledEffect, Beat, LineBeat, TextBeat, GameData, Expression, PropertyDecl, PropertyType, Jump, HostScopeDecl,\n} from \"@patterkit/model\";\n\ntype SelectableNode = CompiledGroup | CompiledSnippet;\n\n// Compiled expressions are immutable, so each one's AST is deserialised once -\n// per evaluation was the engine's hottest path (every condition / effect / slot).\nconst astCache = new WeakMap<Expression, ExprNode>();\n\n/** A property-state snapshot: owned scope -> property name -> value. */\nexport type EngineSave = Record<string, Record<string, ScalarValue>>;\n\n/** Serialised `sequence` selector visit state for one group (spec §4 / §7). */\nexport interface SelectorSnapshot {\n seq?: number; // sequential cursor (visits taken)\n bag?: string[]; // shuffle: child ids still undrawn this pass\n last?: string; // last child id picked (no-immediate-repeat)\n}\n\n/** One entry on a flow's continuation stack: a position within a container's children. */\nexport interface StackFrame {\n sceneId: string;\n /** A block id or a run-group id (both are sequential containers). */\n containerId: string;\n index: number;\n /** SNAPSHOT-ONLY (never set on a live frame): the id of the child at `index` when the save was\n * taken. On restore the child is re-found by this id, so a save survives siblings being inserted,\n * removed, or reordered before the cursor (live bundle refresh / patched-game saves). Absent (an\n * older save, or a frame saved at its container's end) falls back to the raw `index`. */\n nextId?: string;\n}\n\n/** The serialised cursor + scopes + PRNG of a single flow. */\nexport interface FlowSnapshot {\n /** This flow's owned-scope values = the NOT-shared `@patter` globals (under token \"patter\"). */\n scopes: EngineSave;\n /** Per-scene NOT-shared `@scene` bags (scene id -> name -> value); persist across re-entries (spec §7). */\n sceneBags: Record<string, Record<string, ScalarValue>>;\n /** This flow's built-in PRNG position (mulberry32 state). */\n rngState: number;\n /** This flow's per-node entry counts (node id -> times entered by this flow). */\n visits: Record<string, number>;\n cursor: {\n flowEnded: boolean;\n currentSceneId: string | null;\n /** The continuation stack (call frames + the active block run). */\n stack: StackFrame[];\n activeSnippetId: string | null;\n beatIndex: number;\n /** The pending choice's exact option set, REPLAYED on load (schema 9.3). */\n pendingChoice: SavedChoice | null;\n /** The chosen option owning a prompt still to be replayed (save taken between choose + advance).\n * Optional / absent in older saves -> no pending prompt. */\n pendingPromptOwnerId?: string | null;\n /** This flow's `sequence` selector cursors. */\n selectors: Record<string, SelectorSnapshot>;\n };\n}\n\n/**\n * A pending choice as saved: the option set the player was shown, restored\n * verbatim - re-deriving on load would re-evaluate conditions (consuming PRNG\n * draws a second time) and could mutate the choice under the player.\n */\nexport interface SavedChoice {\n groupId: string;\n options: ChoiceOption[];\n}\n\n/** A full resumable save-game: shared `@patter` state + every live flow. */\nexport interface SaveGame {\n version: number;\n /** Shared `@patter` globals (owned scope \"patter\"). */\n shared: EngineSave;\n /** World-wide per-node entry counts (node id -> times entered by any flow). */\n sharedVisits: Record<string, number>;\n /** Shared selector cursors (node id -> snapshot) for `shared` memoried selectors. */\n sharedSelectors: Record<string, SelectorSnapshot>;\n /** Shared, scene-namespaced `@scene` bags (scene id -> name -> value) - the shared scene props. */\n stageBags: Record<string, Record<string, ScalarValue>>;\n /** Each live flow's snapshot, keyed by flow id. */\n flows: Record<string, FlowSnapshot>;\n}\n\n/** What `Flow.advance()` surfaces to the host at each stop. */\nexport type StepResult =\n | { type: \"line\"; id: string; text: string; character?: string; characterName?: string; direction?: string; gameData?: GameData; tags?: string[] }\n | { type: \"text\"; id: string; text: string; gameData?: GameData; tags?: string[] }\n | { type: \"gameEvent\"; id: string; gameData?: GameData; tags?: string[] }\n | { type: \"choice\"; groupId: string; options: ChoiceOption[] }\n | { type: \"end\" };\n\n// --- Static structure introspection (editor / dev tooling) -------------------\n// A read-only view of the AUTHORED tree (scenes -> blocks -> groups/snippets -> beats), for dev\n// tools that build against the writer's structure (e.g. an Unreal Sequencer of subsequences per\n// beat). Static: no flow, no play state. Per-beat data mirrors what a StepResult would carry\n// (source text, author gameData, accumulated tags), read at the default locale.\n\n/** One beat's static data - the same shape a delivered step carries, resolved at the source locale. */\nexport interface BeatInfo {\n id: string;\n kind: \"line\" | \"text\" | \"gameEvent\";\n /** Speaker token (line only). */\n character?: string;\n /** Resolved display name for `character` (source locale), if the cast declares one. */\n characterName?: string;\n /** Performance direction (line only). */\n direction?: string;\n /** Source text, un-interpolated (line / text). Omitted for gameEvent and IDs-only bundles. */\n text?: string;\n /** Author gameData overrides on this beat (raw, as the step carries them). Omitted when empty. */\n gameData?: GameData;\n /** Accumulated author tags (scene -> block -> group(s) -> snippet -> beat). Omitted when empty. */\n tags?: string[];\n}\n\n/** A node in the outline tree: a group (with its selector + children) or a snippet (with its beats). */\nexport interface OutlineNode {\n type: \"group\" | \"snippet\";\n id: string;\n tags?: string[];\n // group only\n selector?: string;\n /** A choice/option group's prompt beat, if any. */\n prompt?: BeatInfo;\n children?: OutlineNode[];\n // snippet only\n beats?: BeatInfo[];\n jumpTo?: string;\n jumpMode?: \"jump\" | \"call\";\n}\n\n/** A block in the outline tree. */\nexport interface OutlineBlock {\n id: string;\n gameId?: string;\n name: string;\n tags?: string[];\n children: OutlineNode[];\n}\n\n/** A scene in the outline tree. */\nexport interface OutlineScene {\n id: string;\n gameId?: string;\n name: string;\n tags?: string[];\n blocks: OutlineBlock[];\n}\n\n/** One beat in document order, with the scene/block/snippet it lives in (the flat view). */\nexport interface FlatBeat {\n sceneId: string;\n blockId: string;\n snippetId: string;\n beat: BeatInfo;\n}\n\n/** What {@link Flow.advanceToStop} returns: the beats walked, and the choice / end that stopped it. */\nexport interface AdvanceToStopResult {\n /** The line / text / game-event beats played on the way to the stop (never a choice / end). */\n played: Array<Extract<StepResult, { type: \"line\" | \"text\" | \"gameEvent\" }>>;\n stop: Extract<StepResult, { type: \"choice\" | \"end\" }>;\n}\n\n/** The choice text of an option (spec §5): its `prompt` beat, resolved + interpolated. */\nexport interface ChoicePrompt {\n kind: \"line\" | \"text\";\n /** Display text (interpolated; may be empty - the host can render from gameData / an icon). */\n text: string;\n /** Speaker / direction - present only for a `line` prompt (the PC's spoken choice). */\n character?: string;\n /** The speaker's resolved player-facing name (locale-aware; absent when the character has none). */\n characterName?: string;\n direction?: string;\n}\n\n/** A single option of a pending `choice` group. */\nexport interface ChoiceOption {\n /** The option's id (an Option group, or a degenerate option snippet) - pass to `choose()`. */\n id: string;\n /**\n * The option's `prompt` (spec §5) - the choice text as a structured line/text beat. For the\n * degenerate bare-snippet tolerance, derived from the snippet's first content line. Undefined\n * only when even that is absent; internal ids are never leaked as display text.\n */\n prompt?: ChoicePrompt;\n /** False when the option's condition fails; still returned (greyed) unless hidden. */\n eligible: boolean;\n gameData?: GameData;\n}\n\n/**\n * The host's **World Properties** resolver: a `{ get, set? }` the game provides so the story can read\n * (and, if you allow it, write) its `@world.*` values at runtime. Property metadata (types, read-only)\n * comes from the compiled bundle's declared world properties; the values themselves live in the host and\n * are never stored or saved by this engine.\n */\nexport type WorldResolver = ScopeResolver;\n\nexport interface EngineOptions {\n /**\n * Custom float-in-[0,1) source for `random()` / shuffle, shared by all flows.\n * Overrides the built-in seeded PRNG - but its position is NOT captured by\n * `saveGame()`. For resumable runs, use the built-in per-flow seed instead.\n */\n rng?: () => number;\n /** Default seed for each flow's built-in (serialisable) PRNG; override per flow in `openFlow`. */\n seed?: number;\n /** Active locale for string lookups (embedded localisation). Defaults to the bundle's default locale.\n * Ignored by an \"ids\" bundle, which emits beat IDs for the game to localise itself. */\n locale?: string;\n /** The host's resolver for **World Properties** (`@world.*`): the values the game owns and the story\n * reads. Omit it and the runtime self-backs `@world` from the declared defaults. Shared by all flows. */\n world?: WorldResolver;\n /**\n * Replay a chosen option's `prompt` as its first played beat (spec §5). Default `false`:\n * the prompt is a label only and `choose()` plays just the option's content. `true`: the\n * prompt beat is delivered first (the choice \"spoken back\"). A host decision, not authored.\n */\n replayPromptOnChoose?: boolean;\n /** Closed captions (#214): show non-spoken caption cues inside dialogue lines (the `[sigh]` in\n * `Oh dear. [sigh] What now?`). Default `true` (full text). `false` strips every cue + its delimiters\n * and collapses the whitespace - for a player who hears the audio and doesn't want the captions.\n * Toggle live with `engine.setClosedCaptions(...)`. */\n closedCaptions?: boolean;\n /** Diagnostics hook (opt-in, dev tooling only): fired with the choice's group id whenever a choice runs\n * DRY - no takeable option and no eligible fallback - so it falls through silently. The behaviour is\n * unchanged; this only makes the fall-through observable. The coverage harness uses it to flag choices\n * that ran dry. Leave it unset in shipped games (zero cost). */\n onDryChoice?: (groupId: string) => void;\n}\n\n/** One shared `@patter` property, for a live state inspector: its ref, declared type, current value,\n * declared default (for reset), and enum options. Mirrors the Unity / Godot ports' ListProperties. */\nexport interface PropertyRow {\n ref: string;\n type: PropertyType;\n value: ScalarValue | undefined;\n default: ScalarValue;\n values?: string[];\n}\n\n/** Options for opening a flow. */\nexport interface OpenFlowOptions {\n /** Scene to start at - its host-facing gameId (address) OR its internal id; defaults to the\n * bundle's first scene. */\n scene?: string;\n /** Block within the scene to start at - its gameId (scene-scoped address) OR its internal id. */\n block?: string;\n /** Seed for this flow's PRNG (defaults to the engine's `seed`). */\n seed?: number;\n}\n\ninterface ChoiceState {\n /** The choice group's id (saved alongside the verbatim option set - SavedChoice). */\n groupId: string;\n options: ChoiceOption[];\n byId: Map<string, SelectableNode>;\n}\n\ninterface SelectorState {\n seq?: number; // sequential cursor (visits taken)\n bag?: string[]; // shuffle: child ids still undrawn this pass (undefined = not started)\n last?: string; // last child id picked (no-immediate-repeat across reshuffles)\n}\n\n/** Shared, read-mostly context the engine hands to every flow it owns. */\ninterface FlowHost {\n bundle: Bundle;\n /** IDs-only build (`localisation.mode === \"ids\"`, no source-debug): the engine emits each beat's ID as\n * its text and omits character display names, leaving localisation to the game (use `flow.interpolate`\n * to apply `{@ref}` property replacement to a string the game looked up itself). */\n emitIds: boolean;\n strings: Record<string, string>;\n /** The DEFAULT locale's string table - fallback for a key the active locale is missing (notably the\n * cast display-name keys, seeded there from `displayName`). */\n defaultStrings: Record<string, string>;\n /** Cast canonical name -> authoring `displayName` (the unlocalised fallback when no loc string exists). */\n castDisplay: Map<string, string>;\n nodeIndex: Map<string, SelectableNode>;\n blockIndex: Map<string, { sceneId: string }>;\n blockById: Map<string, CompiledBlock>;\n /** Host-facing addresses (spec §6), shared with the engine: scene gameId -> internal id (project-wide),\n * and per-scene block gameId -> internal id. A flow needs them to resolve `goto` by address. */\n sceneGameIdToId: Map<string, string>;\n blockGameIdToId: Map<string, Map<string, string>>;\n /** Author tags (#215): node id -> accumulated tags (own + every ancestor's, deduped). Built once. */\n tagIndex: Map<string, string[]>;\n /** The SHARED `@patter` globals (owned scope \"patter\") + world properties (`@world`). */\n shared: ScopeRegistry;\n /** Decls for the shared `@patter` globals - (re)seed on `engine.reset()`. */\n patterSharedDecls: ScopeDeclaration[];\n /** Decls for the per-flow `@patter` globals - seed each flow's local registry. */\n patterLocalDecls: ScopeDeclaration[];\n /** Lowercase names of the SHARED globals (route a `@patter` ref to engine vs flow). */\n patterSharedNames: Set<string>;\n /** Per-scene set of SHARED `@scene` prop names (route a `@scene` ref to stage vs flow). */\n sceneSharedNames: Map<string, Set<string>>;\n /** World-wide per-node entry counts (node id -> times entered by any flow). */\n sharedVisits: Map<string, number>;\n /** Shared selector cursors (node id -> SelectorState) for `shared` memoried selectors. */\n sharedSelectors: Map<string, SelectorState>;\n /** Shared, scene-namespaced `@scene` bags (scene id -> name -> value) for shared scene props. */\n stageBags: Map<string, Record<string, ScalarValue>>;\n customRng?: () => number;\n /** Play a chosen option's prompt as its first beat (spec §5); default false. */\n replayPromptOnChoose?: boolean;\n /** Closed captions (#214). `captionsOn`: show caption cues in dialogue lines (default true); when\n * false the engine strips `captionOpen`…`captionClose` spans from line text. Mutable via\n * `setClosedCaptions` (one toggle, all flows - like setLocale). */\n captionsOn: boolean;\n captionOpen: string;\n captionClose: string;\n /** A cast member whose lines are pure captions: when captions are off ALL of its dialogue + speaker is\n * omitted (a silent line), delimiters or not. Default `SFX`. Empty = no caption character. */\n captionCharacter: string;\n /** Diagnostics hook (opt-in, dev only): fired when a choice runs DRY - nothing takeable and no eligible\n * fallback - so it falls through and the flow continues past it. Zero cost when unset; the coverage\n * harness passes it to surface silent fall-throughs. Not a gameplay signal (the behaviour is unchanged). */\n onDryChoice?: (groupId: string) => void;\n /** Memoised `splitRef` results (ref string -> {scope,name}). The split depends only on `shared`'s scope\n * set, which is fixed for the engine's life, so every effect target / `{@ref}` slot parses once. */\n refSplitCache: Map<string, { scope: string; name: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// Engine - the world + flow manager\n// ---------------------------------------------------------------------------\n\nexport class Engine {\n private readonly host: FlowHost;\n private readonly defaultSeed: number;\n private readonly flowsById = new Map<string, Flow>();\n /** Every locale's string table (the inline `bundle.strings`), kept so the active locale can be swapped\n * live (setLocale) without rebuilding the engine. Reassigned wholesale by `replaceStrings`\n * (live bundle refresh, tier 1), hence not readonly. */\n private allStrings: Record<string, Record<string, string>>;\n /** The currently active locale (string lookups + character names resolve in it). */\n private currentLocale: string;\n /** True for a source-only DEBUG build (`localisation: { mode: \"ids\", sourceDebug: true }`) - the strings\n * are the source language, embedded only so the build can be played; not a shippable localised build. */\n private readonly sourceDebug: boolean;\n /** Host-facing addresses (spec §6): scene gameId -> internal id (project-wide), and per-scene\n * block gameId -> internal id. The effective gameId falls back to the name slug when unpinned. */\n private readonly sceneGameIdToId = new Map<string, string>();\n private readonly blockGameIdToId = new Map<string, Map<string, string>>();\n\n /** The options this engine was built with - reused verbatim by `hotSwap` so the replacement\n * engine keeps the same world resolver, custom RNG, and diagnostic hooks. */\n private readonly creationOptions: EngineOptions;\n\n constructor(bundle: Bundle, options: EngineOptions = {}) {\n this.creationOptions = options;\n const locale = options.locale ?? bundle.locales.default;\n const allStrings = bundle.strings;\n this.allStrings = allStrings;\n this.currentLocale = locale;\n const strings = allStrings[locale] ?? {};\n const defaultStrings = allStrings[bundle.locales.default] ?? {};\n // Localisation mode (spec §11). \"ids\" + no source-debug = the engine emits beat IDs (the game localises\n // itself). A source-debug build still resolves its embedded source strings, but is flagged for a warning.\n const loc = bundle.localisation;\n const emitIds = loc?.mode === \"ids\" && !loc.sourceDebug;\n this.sourceDebug = loc?.mode === \"ids\" && !!loc.sourceDebug;\n if (this.sourceDebug && typeof console !== \"undefined\") {\n console.warn(\"[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.\");\n }\n // Cast name -> displayName: the unlocalised fallback for a character's shown name when neither the\n // active nor the default locale carries a `cast:<name>` string.\n const castDisplay = new Map<string, string>();\n for (const c of bundle.cast ?? []) if (c.displayName) castDisplay.set(c.name, c.displayName);\n this.defaultSeed = (options.seed ?? 0x9e3779b9) >>> 0;\n\n const nodeIndex = new Map<string, SelectableNode>();\n const blockIndex = new Map<string, { sceneId: string }>();\n const blockById = new Map<string, CompiledBlock>();\n for (const [sceneId, scene] of Object.entries(bundle.scenes)) {\n this.sceneGameIdToId.set(effectiveGameId(scene), sceneId);\n const blockAddrs = new Map<string, string>();\n for (const block of scene.blocks) {\n blockIndex.set(block.id, { sceneId });\n blockById.set(block.id, block);\n blockAddrs.set(effectiveGameId(block), block.id);\n walkNodes<SelectableNode>(block.children, (n) => nodeIndex.set(n.id, n));\n }\n this.blockGameIdToId.set(sceneId, blockAddrs);\n }\n\n // Globals (`@patter`) split by the `shared` flag (default shared): shared ones\n // live in the engine's owned scope, per-flow ones seed each flow's registry.\n const props = bundle.properties ?? [];\n const patterSharedDecls = props.filter((p) => p.shared ?? true).map(toDecl);\n const patterLocalDecls = props.filter((p) => !(p.shared ?? true)).map(toDecl);\n const patterSharedNames = new Set(patterSharedDecls.map((d) => d.name.toLowerCase()));\n\n const shared = new ScopeRegistry().defineOwned(\"patter\", patterSharedDecls);\n const hostBound = new Set<string>();\n // The host's World Properties resolver binds `@world`; its declarations (types, read-only) come from\n // the compiled bundle's declared world properties. An explicit binding always wins over the self-backed\n // fallback below.\n if (options.world) {\n const worldSpec = bundle.scopeRegistry?.scopes.find((s) => s.token === \"world\");\n const decls = (worldSpec?.declarations ?? []).map(toForeignDecl);\n shared.defineForeign(\"world\", options.world, decls, worldSpec?.writable ?? true);\n hostBound.add(\"world\");\n }\n // A project that DECLARES `@world` but whose embedder binds no resolver (the standalone case) gets a\n // self-backed one: a live in-memory bag seeded from the declarations' defaults. The story reads/writes\n // it like any scope; it stays *foreign* (not in Patter's save: the host owns it conceptually).\n for (const spec of bundle.scopeRegistry?.scopes ?? []) {\n if (hostBound.has(spec.token)) continue;\n const decls = (spec.declarations ?? []).map(toForeignDecl);\n shared.defineForeign(spec.token, selfBackedResolver(spec.declarations ?? []), decls, spec.writable ?? true);\n }\n\n // Scene props (`@scene`) split by `shared` (default per-flow): record, per\n // scene, which names are shared so a `@scene` ref routes to stage vs flow.\n const sceneSharedNames = new Map<string, Set<string>>();\n for (const [sceneId, scene] of Object.entries(bundle.scenes)) {\n const names = new Set((scene.sceneProps ?? []).filter((p) => p.shared ?? false).map((p) => p.name.toLowerCase()));\n sceneSharedNames.set(sceneId, names);\n }\n\n this.host = {\n bundle, emitIds, strings, defaultStrings, castDisplay, nodeIndex, blockIndex, blockById,\n sceneGameIdToId: this.sceneGameIdToId, blockGameIdToId: this.blockGameIdToId, // same instances the engine resolves with\n tagIndex: buildTagIndex(bundle), shared,\n patterSharedDecls, patterLocalDecls, patterSharedNames, sceneSharedNames,\n sharedVisits: new Map(),\n sharedSelectors: new Map(),\n stageBags: new Map(),\n customRng: options.rng,\n onDryChoice: options.onDryChoice,\n replayPromptOnChoose: options.replayPromptOnChoose ?? false,\n captionsOn: options.closedCaptions ?? true, // captions shown by default (full text)\n captionOpen: (bundle.closedCaptions ?? DEFAULT_CAPTION_DELIMITERS).open,\n captionClose: (bundle.closedCaptions ?? DEFAULT_CAPTION_DELIMITERS).close,\n captionCharacter: bundle.closedCaptions?.character || DEFAULT_CAPTION_CHARACTER, // absent/empty -> SFX\n refSplitCache: new Map(),\n };\n }\n\n /** The active locale (string + character-name lookups resolve in it). */\n get locale(): string { return this.currentLocale; }\n\n /** True for a source-only DEBUG build: the embedded strings are the source language (for debugging),\n * not a shippable localised build. An IDs-only ship build is `false`. */\n get isSourceDebug(): boolean { return this.sourceDebug; }\n\n /**\n * Switch the active locale LIVE - a real game's \"language\" setting can change mid-session. Subsequent\n * string lookups (new beats, re-resolved character names, `{@ref}` interpolation) render in the new\n * locale; everything else - flow position, `@patter`/`@scene` state, visit counts, the PRNG - is\n * untouched (already-emitted text isn't retro-translated; that's the host's call). A locale with no\n * table resolves every string via the `<Untranslated: {id}>` source fallback. All open flows share the\n * engine's string table, so the swap reaches every flow at once.\n */\n setLocale(locale: string): void {\n this.currentLocale = locale;\n this.host.strings = this.allStrings[locale] ?? {};\n }\n\n /**\n * Live bundle refresh, tier 1 (strings only): swap every locale's string table in place from a\n * freshly compiled bundle whose STRUCTURE is unchanged (same `content.structureHash`). Like\n * setLocale, nothing restarts and no flow is touched: the next delivered beat reads the new text,\n * `{@ref}` slots re-interpolate, and beats the host already received keep the words it saw. The\n * swap reaches every open flow at once and is not part of save state. Structural edits need the\n * full save/load hot swap instead (a structure change here simply won't show).\n */\n replaceStrings(bundle: Bundle): void {\n this.allStrings = bundle.strings;\n this.host.strings = this.allStrings[this.currentLocale] ?? {};\n this.host.defaultStrings = this.allStrings[this.host.bundle.locales.default] ?? {};\n }\n\n /**\n * Live bundle refresh, tier 2 (full swap): rebuild on an edited bundle with the whole run carried\n * over. Snapshot (`saveGame`), construct a fresh engine on `bundle` with THIS engine's original\n * options (same world resolver, RNG, hooks), restore (`loadGame`), and carry over the presentation\n * state that deliberately isn't save state (active locale, closed-captions toggle). The\n * content-drift policy (§9.8) resolves edits under the cursor: stack frames re-find their next\n * child by id, drifted options drop, a vanished snippet is skipped.\n *\n * Returns the REPLACEMENT engine; this one is left untouched and should be discarded. Hosts\n * re-bind their flow handles via `next.getFlow(id)`. If the restore throws (defensive - §9.8\n * makes this unreachable for ordinary edits), the swap falls back to a cold engine with each\n * saved flow restarted from the top of the scene it was in.\n */\n hotSwap(bundle: Bundle): Engine {\n const snapshot = this.saveGame();\n const carryOver = (next: Engine): Engine => {\n next.setLocale(this.currentLocale);\n next.setClosedCaptions(this.host.captionsOn);\n return next;\n };\n const next = new Engine(bundle, this.creationOptions);\n try {\n next.loadGame(snapshot);\n return carryOver(next);\n } catch {\n // A partial load may have mutated `next`: fall back on a THIRD, cold engine and restart each\n // flow at the top of the scene it was in (dropped when that scene is gone too).\n const fresh = new Engine(bundle, this.creationOptions);\n for (const [id, f] of Object.entries(snapshot.flows)) {\n const sceneId = f.cursor.currentSceneId;\n try { fresh.openFlow(id, sceneId !== null ? { scene: sceneId } : {}); } catch { /* scene deleted: drop the flow */ }\n }\n return carryOver(fresh);\n }\n }\n\n /** Whether closed captions are currently shown (full dialogue text). */\n get closedCaptions(): boolean { return this.host.captionsOn; }\n\n /**\n * Turn closed captions on/off LIVE (#214). When OFF, subsequent dialogue lines have their caption\n * cues (`[sigh]` etc., between the project's delimiters) and the surrounding whitespace stripped;\n * narration, choice prompts, and everything else are untouched. Like setLocale this is a presentation\n * toggle - it reaches every open flow at once and isn't part of save state; already-emitted text is\n * not retro-edited. An IDs-only game applies the same rule itself via `flow.stripCaptions`.\n */\n setClosedCaptions(on: boolean): void {\n this.host.captionsOn = on;\n }\n\n /**\n * Open (and start) a named flow. Each flow has its own cursor, PRNG, and per-flow\n * half of the scopes (not-shared `@patter`/`@scene`); all flows share the shared\n * half.\n *\n * Re-opening an existing id REPLACES it with a fresh flow, and CLOSES the old one\n * ({@link Flow.close}) so a host still holding it cannot keep driving the shared world. Replacing is\n * therefore a reset: that name's cursor, visit counts, selector cursors (so any shuffle / once-each\n * position) and per-flow properties all start over.\n *\n * Contrast {@link runFlow}, which REUSES a flow of the same name instead of replacing it - that is the\n * call to reach for when you want a speaker's variation state to carry on.\n */\n openFlow(id: string, opts: OpenFlowOptions = {}): Flow {\n const sceneId = this.resolveSceneRef(opts.scene);\n const blockId = this.resolveBlockRef(sceneId, opts.block);\n this.flowsById.get(id)?.close(); // finish the flow this name used to mean\n const flow = new Flow(id, this.host, opts.seed ?? this.defaultSeed);\n this.flowsById.set(id, flow);\n flow.start(sceneId, blockId);\n return flow;\n }\n\n /**\n * \"Play this address and give me everything it produced\" - the one-call form of the bark / one-shot\n * pattern. The NAMED flow is reused if it already exists (moved with {@link Flow.goto}) and opened at\n * the address if not, then run to its next stop, returning every beat it played.\n *\n * Calling it again with the SAME NAME does NOT replace the flow - it reuses it, and that is the whole\n * point. A flow owns its selector cursors, visit counts and per-flow properties, so reusing one lets a\n * **shuffle keep its bag** and an **\"once each\" list keep its place**: successive calls give the next\n * variation instead of replaying the first forever. (A fresh flow each time would reset all of it,\n * unless every such group happened to be authored `shared`.) Use one name per independent speaker;\n * different names never share per-flow state.\n *\n * This is exactly where it differs from {@link openFlow}, which REPLACES a flow of the same name and\n * so resets that variation state. Never mix the two on one name unless you mean to start over.\n *\n * Returns the played beats in order - `[]` means the address had nothing left to give (an exhausted\n * variation list, say), which is the signal to fall back to other content. It THROWS on an address\n * that does not resolve: unlike `goto` (a navigation primitive, where probing is legitimate), naming a\n * location here asserts it exists, and keeping `[]` unambiguous is worth more than a soft failure.\n *\n * A run that stops at a CHOICE returns the beats up to it and leaves the choice pending on the flow -\n * fetch it with `engine.getFlow(name)?.getChoices()`.\n */\n runFlow(flow: string, scene: string, block?: string): AdvanceToStopResult[\"played\"] {\n const existing = this.flowsById.get(flow);\n if (!existing) return this.openFlow(flow, { scene, block }).advanceToStop().played; // start() reports a bad address\n if (!existing.goto(scene, block)) {\n throw new Error(`runFlow: address not found: ${scene}${block === undefined ? \"\" : ` / ${block}`}`);\n }\n return existing.advanceToStop().played;\n }\n\n /** Resolve a scene reference (a gameId address OR an internal id) to its internal id. */\n private resolveSceneRef(ref?: string): string | undefined {\n if (ref == null) return undefined;\n if (this.host.bundle.scenes[ref]) return ref; // already an internal id\n return this.sceneGameIdToId.get(ref) ?? ref; // a gameId, else pass through (start reports)\n }\n\n /** Resolve a block reference (a scene-scoped gameId OR an internal id) to its internal id. */\n private resolveBlockRef(sceneId: string | undefined, ref?: string): string | undefined {\n if (ref == null) return undefined;\n if (this.host.blockById.has(ref)) return ref; // already an internal id\n if (sceneId != null) { const id = this.blockGameIdToId.get(sceneId)?.get(ref); if (id) return id; }\n return ref; // pass through (start reports an unknown block)\n }\n\n /** The host-facing address (gameId) of a scene / block by internal id, or undefined if unknown.\n * The inverse of the resolve helpers - for a host that wants to display / log the address. */\n sceneAddress(sceneId: string): string | undefined {\n const scene = this.host.bundle.scenes[sceneId];\n return scene ? effectiveGameId(scene) : undefined;\n }\n blockAddress(blockId: string): string | undefined {\n const block = this.host.blockById.get(blockId);\n return block ? effectiveGameId(block) : undefined;\n }\n\n /**\n * Author tags (#215) accumulated for a beat by id: its own tags unioned with every ancestor's\n * (scene → block → group(s) → snippet → beat), deduped, outermost-first. The same value the beat's\n * delivered step carries. Empty array for an unknown id or a beat with no tags anywhere up the chain.\n */\n tagsForBeat(beatId: string): string[] {\n return this.host.tagIndex.get(beatId) ?? [];\n }\n /** A scene's own tags (by internal id or gameId address). Empty when none / unknown. */\n tagsForScene(sceneRef: string): string[] {\n const id = this.resolveSceneRef(sceneRef);\n return (id != null ? this.host.tagIndex.get(id) : undefined) ?? [];\n }\n /** A block's accumulated tags (scene + block), by scene + block ref (id or gameId). Empty when none / unknown. */\n tagsForBlock(sceneRef: string, blockRef: string): string[] {\n const sceneId = this.resolveSceneRef(sceneRef);\n const id = this.resolveBlockRef(sceneId, blockRef);\n return (id != null ? this.host.tagIndex.get(id) : undefined) ?? [];\n }\n\n /**\n * The authored structure as a nested tree: scenes -> blocks -> children (groups + snippets, groups\n * preserved) -> a snippet's beats. Static (no flow / play state); per-beat data is read at the source\n * locale. For dev tooling that builds against the writer's structure (see also {@link getBeatSequence}).\n */\n getOutline(): OutlineScene[] {\n return Object.values(this.host.bundle.scenes).map((scene) => ({\n id: scene.id,\n ...(effectiveGameId(scene) ? { gameId: effectiveGameId(scene) } : {}),\n name: scene.name,\n ...this.tagsField(scene.id),\n blocks: scene.blocks.map((block) => ({\n id: block.id,\n ...(effectiveGameId(block) ? { gameId: effectiveGameId(block) } : {}),\n name: block.name,\n ...this.tagsField(block.id),\n children: block.children.map((n) => this.outlineNode(n)),\n })),\n }));\n }\n\n /**\n * Every beat in document order, flattened (through groups), each with the scene / block / snippet it\n * belongs to and its static data. The linear view of {@link getOutline} - hand it to a tool that lays\n * one item per beat (e.g. an Unreal Sequencer of subsequences).\n */\n getBeatSequence(): FlatBeat[] {\n const out: FlatBeat[] = [];\n for (const scene of Object.values(this.host.bundle.scenes)) {\n for (const block of scene.blocks) {\n walkNodes<SelectableNode>(block.children, (n) => {\n if (n.type !== \"snippet\") return;\n for (const beat of n.beats ?? []) {\n out.push({ sceneId: scene.id, blockId: block.id, snippetId: n.id, beat: this.beatInfo(beat) });\n }\n });\n }\n }\n return out;\n }\n\n /** A node's outline entry: a group (selector + prompt + children) or a snippet (beats + jump). */\n private outlineNode(n: SelectableNode): OutlineNode {\n if (n.type === \"group\") {\n return {\n type: \"group\",\n id: n.id,\n ...this.tagsField(n.id),\n ...(n.selector ? { selector: n.selector } : {}),\n ...(n.prompt ? { prompt: this.beatInfo(n.prompt) } : {}),\n children: n.children.map((c) => this.outlineNode(c)),\n };\n }\n return {\n type: \"snippet\",\n id: n.id,\n ...this.tagsField(n.id),\n beats: (n.beats ?? []).map((b) => this.beatInfo(b)),\n ...(n.jump ? { jumpTo: n.jump.to, ...(n.jump.mode ? { jumpMode: n.jump.mode } : {}) } : {}),\n };\n }\n\n /** One beat's static data (source locale), the same shape a delivered step carries. */\n private beatInfo(beat: Beat): BeatInfo {\n const tags = this.host.tagIndex.get(beat.id);\n const info: BeatInfo = { id: beat.id, kind: beat.kind };\n if (beat.kind === \"line\") {\n if (beat.character !== undefined) {\n info.character = beat.character;\n const name = this.host.defaultStrings[castStringKey(beat.character)] ?? this.host.castDisplay.get(beat.character);\n if (name !== undefined) info.characterName = name;\n }\n if (beat.direction !== undefined) info.direction = beat.direction;\n }\n if (beat.kind === \"line\" || beat.kind === \"text\") {\n const source = this.host.defaultStrings[beat.id]; // source-locale text, un-interpolated\n if (source !== undefined) info.text = source;\n }\n if (beat.gameData && Object.keys(beat.gameData).length) info.gameData = beat.gameData;\n if (tags && tags.length) info.tags = tags;\n return info;\n }\n\n /** A `{ tags }` fragment for an id, present only when the id has accumulated tags (keeps output tidy). */\n private tagsField(id: string): { tags?: string[] } {\n const tags = this.host.tagIndex.get(id);\n return tags && tags.length ? { tags } : {};\n }\n\n /** Retrieve an open flow by id (undefined if none / closed). */\n getFlow(id: string): Flow | undefined {\n return this.flowsById.get(id);\n }\n\n /** All currently-open flows. */\n flows(): Flow[] {\n return [...this.flowsById.values()];\n }\n\n /** Close (remove) a flow. The flow object is FINISHED, not merely unregistered, so a host still\n * holding it cannot keep advancing it into the shared world (see {@link Flow.close}). */\n closeFlow(id: string): void {\n this.flowsById.get(id)?.close();\n this.flowsById.delete(id);\n }\n\n /**\n * Reset the whole game to its initial state: drop every flow, re-seed the shared\n * `@patter` globals to their declared defaults, and clear all shared state (shared\n * `@scene` bags, world visit counts). World properties are host-owned and untouched.\n * After reset, open fresh flows with `openFlow`.\n */\n reset(): void {\n for (const flow of this.flowsById.values()) flow.close(); // finish them, don't just forget them\n this.flowsById.clear();\n this.host.shared.reseedOwned(\"patter\", this.host.patterSharedDecls);\n this.host.sharedVisits.clear();\n this.host.sharedSelectors.clear();\n this.host.stageBags.clear();\n }\n\n /** Read a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level). */\n getProperty(ref: string): ScalarValue | undefined {\n const { scope, name } = this.splitShared(ref);\n return this.host.shared.get(scope, name);\n }\n\n /** Write a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level). */\n setProperty(ref: string, value: ScalarValue): void {\n const { scope, name } = this.splitShared(ref);\n this.host.shared.set(scope, name, value);\n }\n\n /** The shared `@patter` properties, for a live state inspector: each with its ref, type, current\n * value, declared default (for reset), and enum options. Mirrors the Unity / Godot ports. */\n listProperties(): PropertyRow[] {\n return this.host.patterSharedDecls.map((d) => ({\n ref: `@${d.name}`,\n type: d.type as PropertyType,\n values: d.values,\n value: this.getProperty(`@${d.name}`),\n default: declDefault(d),\n }));\n }\n\n // @scene is scene-namespaced and needs a flow's current scene - silently\n // routing it into the shared bag (as a junk \"scene.x\" key) was a trap.\n private splitShared(ref: string): { scope: string; name: string } {\n let split = this.host.refSplitCache.get(ref);\n if (!split) { split = splitRef(ref, (t) => t === \"scene\" || this.host.shared.has(t)); this.host.refSplitCache.set(ref, split); }\n if (split.scope === \"scene\") {\n throw new Error(`'${ref}': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);\n }\n return split;\n }\n\n /** Snapshot shared `@patter` state only (for a unified cross-engine save blob, Phase D). */\n save(): EngineSave {\n return this.host.shared.save();\n }\n\n /** Restore shared `@patter` values (world properties untouched). */\n load(blob: EngineSave): void {\n this.host.shared.load(blob);\n }\n\n /** Snapshot the whole game: shared `@patter` + visit counts + every live flow. */\n saveGame(): SaveGame {\n const flows: Record<string, FlowSnapshot> = {};\n for (const [id, flow] of this.flowsById) flows[id] = flow.snapshot();\n return {\n version: 2,\n shared: this.host.shared.save(),\n sharedVisits: Object.fromEntries(this.host.sharedVisits),\n sharedSelectors: serialiseSelectors(this.host.sharedSelectors),\n stageBags: Object.fromEntries([...this.host.stageBags].map(([s, bag]) => [s, { ...bag }])),\n flows,\n };\n }\n\n /** Restore a `saveGame()`: shared globals + visit counts + shared scene bags + reconstruct every flow. */\n loadGame(save: SaveGame): void {\n if (save.version !== 2) throw new Error(`unsupported save version: ${save.version}`);\n this.host.shared.load(save.shared);\n this.host.sharedVisits.clear();\n for (const [id, n] of Object.entries(save.sharedVisits ?? {})) this.host.sharedVisits.set(id, n);\n this.host.sharedSelectors.clear();\n for (const [id, st] of deserialiseSelectors(save.sharedSelectors)) this.host.sharedSelectors.set(id, st);\n this.host.stageBags.clear();\n for (const [s, bag] of Object.entries(save.stageBags ?? {})) this.host.stageBags.set(s, { ...bag });\n this.flowsById.clear();\n for (const [id, snap] of Object.entries(save.flows)) {\n const flow = new Flow(id, this.host, this.defaultSeed);\n flow.restore(snap);\n this.flowsById.set(id, flow);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Flow - one playable flow (cursor + the per-flow half of @patter/@scene + PRNG)\n// ---------------------------------------------------------------------------\n\nexport class Flow {\n readonly id: string;\n private readonly host: FlowHost;\n private local: ScopeRegistry; // owns \"patter\" = the NOT-shared globals (this flow's copy)\n private rngState: number;\n\n // Execution cursor. The `stack` is the continuation stack: each frame is a\n // position within a block's children (the top frame is the active block run;\n // lower frames are pending call-returns). A snippet's beats deliver from\n // `activeSnippet`/`beatIndex`.\n private started = false;\n private flowEnded = false;\n /** Closed by the engine (see `close()`). Terminal, and distinct from `flowEnded`: an ENDED flow is\n * merely out of content and `goto` revives it; a CLOSED one is finished for good. */\n private closed = false;\n private currentSceneId: string | null = null;\n private stack: StackFrame[] = [];\n private activeSnippet: CompiledSnippet | null = null;\n private beatIndex = 0;\n private pendingChoice: ChoiceState | null = null;\n /** When `replayPromptOnChoose`, the chosen option's prompt beat to deliver before its content. */\n private pendingPromptBeat: LineBeat | TextBeat | null = null;\n /** The chosen option that owns `pendingPromptBeat`, so a save taken between choose() and the next\n * advance() can re-derive the prompt on load (the beat isn't otherwise reachable by id). */\n private pendingPromptOwnerId: string | null = null;\n private selectors = new Map<string, SelectorState>();\n /** Per-node entry counts for this flow (node id -> times entered). */\n private visitCounts = new Map<string, number>();\n\n // Per-flow halves of the two scopes. The NOT-shared `@patter` globals live in\n // `local` (owned scope \"patter\"); the NOT-shared `@scene` props live in\n // `sceneBags` (namespaced per scene; they PERSIST across re-entries, spec §7).\n // The SHARED halves live on the host (`host.shared` / `host.stageBags`). Each\n // resolver presents one merged scope, routing each property to its half by the\n // declared `shared` flag.\n private sceneBags = new Map<string, Record<string, ScalarValue>>();\n\n private readonly patterResolver: ScopeResolver = {\n get: (n) => (this.host.patterSharedNames.has(n) ? this.host.shared.get(\"patter\", n) : this.local.get(\"patter\", n)),\n set: (n, v) => {\n if (this.host.patterSharedNames.has(n)) this.host.shared.set(\"patter\", n, v);\n else this.local.set(\"patter\", n, v);\n },\n };\n\n private readonly sceneResolver: ScopeResolver = {\n get: (n) => {\n const s = this.currentSceneId;\n if (s === null) return undefined;\n const bag = this.host.sceneSharedNames.get(s)?.has(n) ? this.host.stageBags.get(s) : this.sceneBags.get(s);\n return bag?.[n];\n },\n set: (n, v) => {\n const s = this.currentSceneId;\n if (s === null) return;\n const bag = this.host.sceneSharedNames.get(s)?.has(n) ? this.host.stageBags.get(s) : this.sceneBags.get(s);\n if (bag) bag[n] = v;\n },\n };\n\n // The eval context is built ONCE: every constituent resolves live state at\n // call time (shared bags mutate in place per scoperegistry's contract;\n // patter/scene route through this flow's resolvers, which read the current\n // `local`/`sceneBags`/`currentSceneId`; the host callbacks read current flow\n // fields). Rebuilding it per evaluation was the engine's hottest allocation.\n private readonly evalCtx: EvalContext;\n\n constructor(id: string, host: FlowHost, seed: number) {\n this.id = id;\n this.host = host;\n this.rngState = seed >>> 0;\n this.local = this.freshLocal();\n\n const scopes = { ...host.shared.toEvalContext().scopes }; // shared @patter bag + foreign resolvers\n scopes[\"patter\"] = this.patterResolver; // override with the merged shared+per-flow view\n scopes[\"scene\"] = this.sceneResolver;\n this.evalCtx = {\n scopes,\n host: {\n nextRandom: this.rng,\n visits: (id: string) => this.visitCounts.get(id) ?? 0,\n patterVisits: (id: string) => this.host.sharedVisits.get(id) ?? 0,\n },\n };\n }\n\n // -- Host API -------------------------------------------------------------\n\n /** Begin this flow at a scene (and optionally a specific block within it). */\n start(sceneId?: string, blockId?: string): void {\n this.sceneBags.clear();\n this.local = this.freshLocal();\n this.selectors.clear();\n this.visitCounts.clear();\n this.stack = [];\n this.currentSceneId = null;\n this.flowEnded = false;\n this.activeSnippet = null;\n this.beatIndex = 0;\n this.pendingChoice = null;\n this.started = true;\n\n if (blockId) {\n const loc = this.host.blockIndex.get(blockId);\n if (!loc) throw new Error(`unknown block: ${blockId}`);\n this.enterSceneSetup(loc.sceneId);\n this.stack = [{ sceneId: loc.sceneId, containerId: blockId, index: 0 }];\n this.enter(blockId);\n } else {\n const id = sceneId ?? Object.keys(this.host.bundle.scenes)[0];\n const scene = id ? this.host.bundle.scenes[id] : undefined;\n if (!scene) throw new Error(id ? `unknown scene: ${id}` : \"no scenes in bundle\");\n this.enterSceneSetup(id!);\n const first = scene.blocks[0];\n if (first) { this.stack = [{ sceneId: id!, containerId: first.id, index: 0 }]; this.enter(first.id); }\n }\n this.settle();\n }\n\n /**\n * Forget everything in this flow and begin again - its per-flow state (not-shared\n * `@patter` globals + `@scene` props), cursor, callstack, selector cursors, and\n * visit counts. Shared state (shared `@patter` / `@scene`, world visit counts) is\n * untouched. A clearer-named alias of `start()`.\n */\n reset(sceneId?: string, blockId?: string): void {\n this.start(sceneId, blockId);\n }\n\n /**\n * Send this flow's cursor to an ADDRESS, exactly as an authored `go` jump would: the target scene's\n * `onEntry` effects run, entering counts as a visit, and the callstack is REPLACED - any pending\n * `call` returns are discarded, just as a goto inside a call does.\n *\n * `scene` and `block` are host-facing gameIds (spec §6) or internal ids; `block` is scene-scoped, so\n * it is looked up within `scene`. `\"END\"` ends the flow. To move within the current scene, pass the\n * current scene's address again (`flow.currentScene` -> `engine.sceneAddress`).\n *\n * This is HOST navigation, not authoring, and it takes effect IMMEDIATELY: any beats left in the\n * snippet being delivered are abandoned, and a pending choice is dropped. The format stops an AUTHOR\n * writing a divert into the middle of a snippet; a host teleport is out-of-band, like `reset()` or\n * `loadGame()`. A flow that never started starts here; one that already ended resumes here.\n *\n * Returns false - leaving the cursor exactly where it was - if the address does not resolve. Per-flow\n * state (properties, visit counts, selector cursors) is untouched either way: this MOVES, never resets.\n */\n goto(scene: string, block?: string): boolean {\n if (this.closed) return false; // closed is terminal: unlike \"ended\", a goto cannot revive it\n if (scene === \"END\") {\n this.started = true; this.pendingChoice = null; this.pendingPromptBeat = null; this.pendingPromptOwnerId = null;\n this.activeSnippet = null; this.beatIndex = 0;\n this.flowEnded = true; this.stack = [];\n return true;\n }\n // Resolve BOTH addresses before touching any state, so a bad one is a no-op rather than a half-move.\n const sceneId = this.host.sceneGameIdToId.get(scene) ?? (this.host.bundle.scenes[scene] ? scene : undefined);\n if (sceneId === undefined) return false;\n let blockId: string | undefined;\n if (block !== undefined) {\n blockId = this.host.blockGameIdToId.get(sceneId)?.get(block)\n ?? (this.host.blockIndex.get(block)?.sceneId === sceneId ? block : undefined);\n if (blockId === undefined) return false; // a block address is scene-scoped: unknown HERE is unknown\n }\n // Never started: start() does the same landing plus the one-time per-flow setup.\n if (!this.started) { this.start(sceneId, blockId); return true; }\n\n this.pendingChoice = null; this.pendingPromptBeat = null; this.pendingPromptOwnerId = null;\n this.activeSnippet = null; this.beatIndex = 0; // abandon the rest of the snippet being delivered\n this.flowEnded = false; // an ended flow resumes at the target\n this.enterTarget(blockId ?? sceneId, \"jump\"); // \"jump\" = replace the stack, exactly like an authored goto\n this.settle();\n return true;\n }\n\n /**\n * Finish this flow for good. Engine-managed: `engine.closeFlow(id)`, `engine.reset()`, and re-opening\n * a name with `engine.openFlow` all call it on the flow being dropped.\n *\n * A dropped flow used to stay fully live: unregistered and invisible to `engine.flows()`, but a host\n * still holding the object could keep advancing it, and every scene `onEntry`, shared property, world\n * visit count and shared selector cursor it touched still landed on the engine. Closing makes that\n * stale reference inert - `advance()` reports the end and `goto()` refuses - so a forgotten reference\n * cannot quietly mutate the world. Terminal: unlike ending, a close is never revived.\n */\n close(): void {\n this.closed = true;\n this.flowEnded = true;\n this.stack = [];\n this.activeSnippet = null;\n this.beatIndex = 0;\n this.pendingChoice = null;\n this.pendingPromptBeat = null;\n this.pendingPromptOwnerId = null;\n }\n\n /** True once the engine has closed this flow (closed, dropped by `reset()`, or replaced by name). */\n get isClosed(): boolean { return this.closed; }\n\n /** The scene the cursor is currently in - set on entry and whenever a jump crosses scenes. Read\n * right after `advance()` to know which scene the just-played beat lives in (tooling that mirrors\n * the playhead, e.g. an editor following a cross-scene jump). `null` before the flow has started. */\n get currentScene(): string | null { return this.currentSceneId; }\n\n /** Run until the next line, game event, choice, or the end of the flow. */\n advance(): StepResult {\n if (this.closed) return { type: \"end\" }; // a stale reference to a closed flow drives nothing\n if (!this.started) throw new Error(\"flow has not been started\");\n // A replayed prompt (replayPromptOnChoose) is delivered first, before the option's content.\n if (this.pendingPromptBeat) { const b = this.pendingPromptBeat; this.pendingPromptBeat = null; this.pendingPromptOwnerId = null; return this.beatResult(b); }\n this.settle();\n if (this.flowEnded) return { type: \"end\" };\n if (this.pendingChoice) return { type: \"choice\", groupId: this.pendingChoice.groupId, options: this.pendingChoice.options };\n if (!this.activeSnippet) { this.flowEnded = true; return { type: \"end\" }; }\n return this.beatResult(this.activeSnippet.beats![this.beatIndex++]!);\n }\n\n /**\n * Advance repeatedly, collecting every played beat, until a choice or the end - the \"play to the\n * next stop\" a host's play UI / tooling wants. The terminal `choice` / `end` is returned as `stop`;\n * `played` holds the line / text / game-event results walked on the way to it. Termination is guaranteed\n * (each `advance()` makes progress or `settle()` throws on a contentless jump cycle).\n */\n advanceToStop(): AdvanceToStopResult {\n const played: AdvanceToStopResult[\"played\"] = [];\n for (;;) {\n const r = this.advance();\n if (r.type === \"choice\" || r.type === \"end\") return { played, stop: r };\n played.push(r); // narrowed to line / text / game-event by the guard above\n }\n }\n\n /**\n * Drive the cursor to the next *deliverable* stop: a beat ready on the active\n * snippet, a pending choice, or the end. Runs onExit/jump seams and walks the\n * block run (sequentially, skipping ineligible children); a finished block pops\n * to its caller (call-return) or ends the flow.\n */\n private settle(): void {\n let transitions = 0;\n for (;;) {\n // Static validation cannot rule out jump cycles (conditions gate them),\n // so a content bug like two pure jumps jumping at each other must be an\n // error, not a hang.\n if (++transitions > 10_000) {\n throw new Error(\"flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content\");\n }\n if (this.flowEnded || this.pendingChoice) return;\n\n if (this.activeSnippet) {\n if (this.beatIndex < (this.activeSnippet.beats?.length ?? 0)) return; // a beat is ready\n this.runEffects(this.activeSnippet.onExit);\n const jump = this.activeSnippet.jump;\n this.activeSnippet = null;\n this.beatIndex = 0;\n this.resolveJump(jump);\n continue;\n }\n\n const frame = this.stack[this.stack.length - 1];\n if (!frame) { this.flowEnded = true; return; }\n if (frame.sceneId !== this.currentSceneId) this.currentSceneId = frame.sceneId; // resumed scene (no reseed)\n const children = this.childrenOf(frame.containerId);\n if (!children) { this.stack.pop(); continue; } // drifted container -> skip the frame\n while (frame.index < children.length && !this.eligible(children[frame.index]!)) frame.index++;\n if (frame.index >= children.length) { this.stack.pop(); continue; } // run exhausted -> resume caller\n this.enterChild(children[frame.index++]!); // advance past it: that's the gather/return point\n }\n }\n\n /** The options of a pending choice (empty when not at a choice point). */\n getChoices(): ChoiceOption[] {\n return this.pendingChoice?.options ?? [];\n }\n\n /** Pick an eligible option by id; the next `advance()` runs it. */\n choose(id: string): void {\n const choice = this.pendingChoice;\n if (!choice) throw new Error(\"no choice is pending\");\n const option = choice.options.find((o) => o.id === id);\n if (!option) throw new Error(`unknown choice option: ${id}`);\n if (!option.eligible) throw new Error(`choice option is not eligible: ${id}`);\n const node = choice.byId.get(id)!;\n this.pendingChoice = null;\n // Optionally speak the chosen option's prompt back as its first beat (spec §5).\n this.pendingPromptBeat = this.host.replayPromptOnChoose ? this.promptBeatOf(node) ?? null : null;\n this.pendingPromptOwnerId = this.pendingPromptBeat ? node.id : null;\n // The block frame is already advanced past the choice group (the gather point),\n // so when the chosen option finishes without a jump, the flow continues there.\n this.enterChild(node);\n }\n\n isEnded(): boolean {\n return this.flowEnded;\n }\n\n /** Read a property by ref - `@patter` / `@scene` (each routed by its `shared` flag) or foreign. */\n getProperty(ref: string): ScalarValue | undefined {\n const { scope, name } = this.splitRef(ref);\n if (scope === \"patter\") return this.patterResolver.get(name);\n if (scope === \"scene\") return this.sceneResolver.get(name);\n return this.host.shared.get(scope, name); // foreign\n }\n\n /** Write a property by ref (routed by scope, then by the property's `shared` flag). */\n setProperty(ref: string, value: ScalarValue): void {\n const { scope, name } = this.splitRef(ref);\n if (scope === \"patter\") {\n this.patterResolver.set!(name, value);\n } else if (scope === \"scene\") {\n // The resolver stays graceful for expression evaluation, but a host write\n // with nowhere to land must error, not silently vanish.\n if (this.currentSceneId === null) throw new Error(`'${ref}': the flow has not entered a scene yet`);\n this.sceneResolver.set!(name, value);\n } else {\n this.host.shared.set(scope, name, value); // foreign\n }\n }\n\n // -- Save / restore (engine-driven) --------------------------------------\n\n /** @internal Snapshot this flow's cursor + per-flow scopes (not-shared `@patter`/`@scene`) + PRNG. */\n snapshot(): FlowSnapshot {\n return {\n scopes: this.local.save(), // owned scope \"patter\" = the NOT-shared globals (@scene saved separately)\n sceneBags: Object.fromEntries([...this.sceneBags].map(([s, bag]) => [s, { ...bag }])),\n rngState: this.rngState,\n visits: Object.fromEntries(this.visitCounts),\n cursor: {\n flowEnded: this.flowEnded,\n currentSceneId: this.currentSceneId,\n // Stamp each frame with the id of the child it would run next (nextId), so a restore against\n // an EDITED bundle re-finds the position by id instead of trusting the raw index (§9.8 /\n // live bundle refresh). A frame saved at its container's end has no next child - no stamp.\n stack: this.stack.map((f) => {\n const next = this.childrenOf(f.containerId)?.[f.index];\n return next ? { ...f, nextId: next.id } : { ...f };\n }),\n activeSnippetId: this.activeSnippet?.id ?? null,\n beatIndex: this.beatIndex,\n pendingChoice: this.pendingChoice\n ? { groupId: this.pendingChoice.groupId, options: this.pendingChoice.options.map((o) => ({ ...o })) }\n : null,\n pendingPromptOwnerId: this.pendingPromptOwnerId,\n selectors: serialiseSelectors(this.selectors),\n },\n };\n }\n\n /** @internal Restore this flow from a snapshot. */\n restore(snap: FlowSnapshot): void {\n this.rngState = snap.rngState >>> 0;\n this.visitCounts = new Map(Object.entries(snap.visits ?? {}));\n const c = snap.cursor;\n this.started = true;\n this.flowEnded = c.flowEnded;\n this.beatIndex = c.beatIndex;\n this.currentSceneId = c.currentSceneId;\n // Re-bind each frame to the CURRENT bundle: prefer the saved next-child id (survives siblings\n // inserted / removed / reordered before the cursor); fall back to the raw index when the id is\n // absent (an older save) or its node drifted out of the bundle (§9.8 best-effort).\n this.stack = c.stack.map((f) => {\n const { nextId, ...frame } = f;\n if (nextId !== undefined) {\n const at = this.childrenOf(frame.containerId)?.findIndex((ch) => ch.id === nextId) ?? -1;\n if (at >= 0) return { ...frame, index: at };\n }\n return { ...frame };\n });\n\n // Restore the per-flow @scene bags, then the per-flow @patter globals. @scene\n // resolves through `sceneResolver` over these bags, so nothing else to reseed.\n this.sceneBags = new Map(Object.entries(snap.sceneBags ?? {}).map(([s, bag]) => [s, { ...bag }]));\n this.local = this.freshLocal();\n this.local.load(snap.scopes); // loads the owned not-shared globals; shared halves live on the host\n\n // Content-drift policy (§9.8): if a saved position points at content deleted\n // since the save, resume best-effort rather than throwing - the missing\n // snippet / choice is dropped and play continues from the surviving stack.\n this.activeSnippet = null;\n if (c.activeSnippetId !== null) {\n const node = this.host.nodeIndex.get(c.activeSnippetId);\n if (node && node.type === \"snippet\") this.activeSnippet = node;\n }\n\n this.selectors = deserialiseSelectors(c.selectors); // this flow's (non-shared) selector cursors\n\n // Replay the saved option set VERBATIM (schema 9.3) - re-deriving would\n // re-evaluate conditions (double-consuming PRNG draws) and could change the\n // choice under the player. Options whose nodes drifted out of the bundle\n // are dropped; a choice with no surviving options dissolves (9.8).\n this.pendingChoice = null;\n if (c.pendingChoice !== null) {\n const byId = new Map<string, SelectableNode>();\n const options: ChoiceOption[] = [];\n for (const o of c.pendingChoice.options) {\n const node = this.host.nodeIndex.get(o.id);\n if (!node) continue;\n byId.set(o.id, node);\n options.push({ ...o });\n }\n if (options.length > 0) this.pendingChoice = { groupId: c.pendingChoice.groupId, options, byId };\n }\n\n // A save taken between choose() and the next advance() left a prompt still to be replayed\n // (replayPromptOnChoose). Re-derive it from the chosen option - dropped if that option drifted out\n // of the bundle (§9.8), exactly as the live choose() would have produced nothing.\n this.pendingPromptBeat = null;\n this.pendingPromptOwnerId = c.pendingPromptOwnerId ?? null;\n if (this.pendingPromptOwnerId) {\n const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);\n this.pendingPromptBeat = owner ? this.promptBeatOf(owner) ?? null : null;\n if (!this.pendingPromptBeat) this.pendingPromptOwnerId = null;\n }\n }\n\n // -- Scene / block / node entry ------------------------------------------\n\n /** Set the current scene, reset its scene-local props, run onEntry. */\n private enterSceneSetup(sceneId: string): void {\n const scene = this.host.bundle.scenes[sceneId];\n if (!scene) throw new Error(`unknown scene: ${sceneId}`);\n this.currentSceneId = sceneId;\n this.enter(sceneId);\n this.seedScene(scene); // seeds @scene defaults (per-flow on first entry; shared once globally)\n this.runEffects(scene.onEntry); // on-entry effects still fire every entry (spec §4)\n }\n\n /**\n * Play one child of the active run. A snippet begins delivering. A group is\n * walked by its selector: the default `run` pushes a nested run (its children\n * play in order, gathering back); `choice` stops for the host; a select-one\n * selector (branch, or a `sequence` in any order x exhaust mode) picks ONE child (recursing\n * to a leaf) - selecting nothing contributes no content and the run continues.\n */\n private enterChild(node: SelectableNode): void {\n this.enter(node.id);\n if (node.type === \"snippet\") { this.beginSnippet(node); return; }\n const selector = node.selector ?? \"run\";\n if (selector === \"run\") {\n this.stack.push({ sceneId: this.currentSceneId!, containerId: node.id, index: 0 });\n return;\n }\n if (selector === \"choice\") { this.setupChoice(node); return; }\n const pick = this.selectChild(node);\n if (pick) this.enterChild(pick);\n }\n\n /** A container's children, whether it's a block or a run-group; undefined if the id is gone. */\n private childrenOf(containerId: string): SelectableNode[] | undefined {\n const block = this.host.blockById.get(containerId);\n if (block) return block.children;\n const node = this.host.nodeIndex.get(containerId);\n if (node && node.type === \"group\") return node.children;\n return undefined; // content drift: the container was deleted since the save\n }\n\n private beginSnippet(snippet: CompiledSnippet): void {\n this.runEffects(snippet.onEnter);\n this.activeSnippet = snippet;\n this.beatIndex = 0;\n }\n\n private setupChoice(group: CompiledGroup): void {\n const options: ChoiceOption[] = [];\n const byId = new Map<string, SelectableNode>();\n const fallbacks: SelectableNode[] = [];\n for (const child of group.children) {\n // An option is an Option group (its content runs + gathers back) or - the\n // degenerate shape - a single snippet. prompt / sticky / fallback / secretUntilEligible\n // live on whichever (spec §5).\n if (child.fallback === true) { fallbacks.push(child); continue; } // never a normal option; auto-followed when last\n // Once-only (default): once the player has followed it, it is GONE from the choice entirely -\n // not delivered, not flagged unavailable, simply absent. A `sticky` option is never consumed,\n // so it stays available as long as its condition passes. Consumption is the existing per-flow\n // visit count, so it persists through save/restore for free.\n if (child.sticky !== true && (this.visitCounts.get(child.id) ?? 0) >= 1) continue;\n const eligible = this.eligible(child);\n const hidden = child.secretUntilEligible === true;\n if (!eligible && hidden) continue; // secret while ineligible; otherwise an ineligible option shows greyed\n options.push({ id: child.id, prompt: this.promptFor(child), eligible, gameData: child.gameData });\n byId.set(child.id, child);\n }\n if (options.length > 0) { this.pendingChoice = { groupId: group.id, options, byId }; return; }\n // No normal option survives. Auto-follow the fallback if it is eligible (its own condition still\n // applies); otherwise the choice GATHERS - it contributes nothing and the run continues past it\n // (a dry choice falls through rather than deadlocking; the validator warns about choices that can\n // run dry with no fallback).\n const fallback = fallbacks.find((f) => this.eligible(f));\n if (fallback) { this.enterChild(fallback); return; }\n // Nothing takeable and no eligible fallback: the choice runs dry and the flow walks past it. The\n // behaviour is unchanged; the opt-in diagnostics hook makes this silent fall-through observable.\n this.host.onDryChoice?.(group.id);\n }\n\n // -- Jumps (jump / call-return) ----------------------------------------\n\n private resolveJump(jump: Jump | undefined): void {\n // No jump: gather - the snippet falls through and the block run continues\n // (settle's frame walk picks the next child, or pops to a caller).\n if (!jump) return;\n this.enterTarget(jump.to, jump.mode === \"call\" ? \"call\" : \"jump\");\n }\n\n /**\n * Route to a target (scene / block / `END`). `call` PUSHES a return frame (the\n * caller's block run, already advanced to its next child, stays below); `jump`\n * is absolute - it REPLACES the whole stack, discarding pending returns. `END`\n * hard-ends the flow regardless of the callstack.\n */\n private enterTarget(to: string, mode: \"call\" | \"jump\"): void {\n if (to === \"END\") { this.flowEnded = true; this.stack = []; return; }\n\n let sceneId: string;\n let containerId: string;\n const scene = this.host.bundle.scenes[to];\n if (scene) {\n this.enterSceneSetup(to);\n const first = scene.blocks[0];\n if (!first) { if (mode === \"jump\") this.stack = []; return; } // empty scene\n sceneId = to; containerId = first.id;\n } else {\n const loc = this.host.blockIndex.get(to);\n if (!loc) throw new Error(`jump target not found: ${to}`);\n if (loc.sceneId !== this.currentSceneId) this.enterSceneSetup(loc.sceneId);\n sceneId = loc.sceneId; containerId = to;\n }\n\n this.enter(containerId); // count the entered block\n const frame: StackFrame = { sceneId, containerId, index: 0 };\n if (mode === \"call\") this.stack.push(frame);\n else this.stack = [frame];\n }\n\n // -- Selectors ------------------------------------------------------------\n\n private selectChild(group: CompiledGroup): SelectableNode | null {\n const eligible = group.children.filter((c) => this.eligible(c));\n if (eligible.length === 0) return null;\n const st = this.selectorState(group);\n\n switch (group.selector) {\n case \"branch\":\n return eligible[0]!;\n\n case \"sequence\": {\n const order = group.options?.order ?? \"sequential\";\n const exhaust = group.options?.exhaust ?? \"once\";\n return order === \"shuffle\" ? this.pickShuffle(eligible, exhaust, st)\n : order === \"specificity\" ? this.pickSpecificity(eligible, exhaust, st)\n : this.pickSequential(eligible, exhaust, st);\n }\n\n case \"run\":\n case \"choice\":\n default:\n return null; // run / choice / default are handled in enterChild, not here\n }\n }\n\n /** `sequence` with `order: \"sequential\"` - walk children in authored order. */\n private pickSequential(eligible: SelectableNode[], exhaust: string, st: SelectorState): SelectableNode | null {\n const len = eligible.length;\n const n = st.seq ?? 0;\n st.seq = n + 1;\n if (exhaust === \"repeat\") return eligible[n % len]!; // cycle\n if (n < len) return eligible[n]!; // still in the first pass\n if (exhaust === \"stick\") return eligible[len - 1]!; // hold the last forever (stopping)\n return null; // once: nothing after the pass\n }\n\n /**\n * `sequence` with `order: \"shuffle\"` - draw WITHOUT replacement (a bag), never\n * repeating the immediately-previous pick across a reshuffle (no line twice in a\n * row when >=2 are eligible). `stick` holds out the last authored child as the\n * permanent terminal; `once` stops after one pass; `repeat` reshuffles.\n */\n private pickShuffle(eligible: SelectableNode[], exhaust: string, st: SelectorState): SelectableNode | null {\n const len = eligible.length;\n const stick = exhaust === \"stick\";\n const fill = (): string[] => (stick ? eligible.slice(0, len - 1) : eligible).map((c) => c.id);\n\n if (st.bag === undefined) st.bag = fill();\n if (st.bag.length === 0) { // a full pass just completed\n if (exhaust === \"once\") return null;\n if (stick) { const last = eligible[len - 1]!; st.last = last.id; return last; }\n st.bag = fill(); // repeat: reshuffle\n }\n\n // Draw without replacement, never repeating the immediately-previous pick. Done allocation-free:\n // rather than materialise a filtered pool, find last's slot `p` and draw into the reduced span,\n // skipping that slot - identical distribution to filtering it out, then erase the pick in place.\n const pool = st.bag;\n const p = st.last !== undefined && pool.length > 1 ? pool.indexOf(st.last) : -1;\n let i = Math.floor(this.rng() * (p >= 0 ? pool.length - 1 : pool.length));\n if (p >= 0 && i >= p) i++;\n const id = pool[i]!;\n pool.splice(i, 1);\n st.last = id;\n return eligible.find((c) => c.id === id)!;\n }\n\n /**\n * `sequence` with `order: \"specificity\"` - **Best match**: score every eligible child by how\n * specifically its condition fits the CURRENT state (`matchedSpec`), keep the top-scoring tier,\n * and break ties with the seeded shuffle (no immediate repeat). A child with no condition scores\n * 0, so it is the filler that wins only when nothing more specific is eligible.\n *\n * `exhaust` composes as it does for the other orders: `repeat` re-scores the full eligible set\n * every draw (re-pickable - the character keeps preferring the on-topic line); `once` uses each\n * pick up (a bag of remaining ids), so as specific lines are consumed the group slides down to\n * less-specific ones and finally the filler, then yields null; `stick` degrades like `once` but\n * holds the final pick forever instead of drying up.\n */\n private pickSpecificity(eligible: SelectableNode[], exhaust: string, st: SelectorState): SelectableNode | null {\n let pool = eligible;\n if (exhaust !== \"repeat\") { // once / stick: draw without replacement\n if (st.bag === undefined) st.bag = eligible.map((c) => c.id);\n const remaining = new Set(st.bag);\n pool = eligible.filter((c) => remaining.has(c.id));\n if (pool.length === 0) { // every child used up\n return exhaust === \"stick\" && st.last !== undefined\n ? eligible.find((c) => c.id === st.last) ?? null // hold the last pick if still eligible\n : null;\n }\n }\n\n // Top specificity tier among the drawable pool.\n let best = -1;\n const scored = pool.map((c) => { const s = this.specScore(c); if (s > best) best = s; return { c, s }; });\n const tier = scored.filter((x) => x.s === best).map((x) => x.c);\n\n // Tie-break by the seeded PRNG, never repeating the immediately-previous pick (matches shuffle).\n // A lone top-tier child is returned WITHOUT drawing, so a clear winner consumes no randomness.\n let pick: SelectableNode;\n if (tier.length === 1) {\n pick = tier[0]!;\n } else {\n const p = st.last !== undefined ? tier.findIndex((c) => c.id === st.last) : -1;\n let i = Math.floor(this.rng() * (p >= 0 ? tier.length - 1 : tier.length));\n if (p >= 0 && i >= p) i++;\n pick = tier[i]!;\n }\n\n if (exhaust !== \"repeat\") st.bag = st.bag!.filter((id) => id !== pick.id);\n st.last = pick.id;\n return pick;\n }\n\n /** A child's Best-match score against the current state: 0 when it has no condition (the filler\n * tier), else the specificity of its (already-passing) condition. */\n private specScore(node: SelectableNode): number {\n return node.condition ? this.matchedSpec(this.conditionAst(node.condition), true) : 0;\n }\n\n /**\n * The **matched-specificity** metric (parity contract): how many atomic constraints are actively\n * holding this condition TRUE against the live state. Evaluation-aware, not a static clause count -\n * it walks the tree with a De-Morgan polarity flag so `or` and `not` score the branch that is\n * actually carrying the truth. `want` = \"does this subtree need to be true for the whole condition\n * to hold?\" (true at the root). Only `and`/`or`/`not`/`check_flags` are structural; every other\n * node (comparisons, scoped vars, literals, other calls) is an atom, evaluated whole.\n */\n private matchedSpec(node: ExprNode, want: boolean): number {\n // Delegates to the shared @wildwinter/expr-specificity scorer (same walk,\n // shared with Storylet Studio). We supply Patter's truthiness rule and keep\n // check_flags counting via the package's default counting call.\n const evalTruthy: EvalTruthy = (n) => truthy(evaluate(n, this.evalCtx, patterDialect));\n return scoreSpecificity(node, evalTruthy, { want });\n }\n\n /** A selector's cursor state - shared across flows (`group.shared`) or this flow's own. */\n private selectorState(group: CompiledGroup): SelectorState {\n const map = group.shared ? this.host.sharedSelectors : this.selectors;\n let st = map.get(group.id);\n if (!st) { st = {}; map.set(group.id, st); }\n return st;\n }\n\n // -- Effects + expressions ------------------------------------------------\n\n private runEffects(effects: CompiledEffect[] | undefined): void {\n // SET-ONLY (spec §15): an effect mutates a property. Host events ride on gameData, not effects.\n for (const e of effects ?? []) {\n this.setProperty(e.target, this.evalExpr(e.value));\n }\n }\n\n private eligible(node: SelectableNode): boolean {\n if (!node.condition) return true;\n return truthy(this.evalExpr(node.condition));\n }\n\n private evalExpr(expr: Expression): ScalarValue {\n return evaluate(this.conditionAst(expr), this.evalCtx, patterDialect);\n }\n\n /** The deserialised (in-memory) AST for an expression, cached per Expression. Shared by the\n * evaluator and the Best-match specificity walker so both work off one parse. */\n private conditionAst(expr: Expression): ExprNode {\n let ast = astCache.get(expr);\n if (!ast) { ast = deserialiseAst(expr.ast); astCache.set(expr, ast); }\n return ast;\n }\n\n /** Record an entry of a node (entered-only; spec §7): bumps the flow + world counts. */\n private enter(id: string): void {\n this.visitCounts.set(id, (this.visitCounts.get(id) ?? 0) + 1);\n this.host.sharedVisits.set(id, (this.host.sharedVisits.get(id) ?? 0) + 1);\n }\n\n /** Next float in [0, 1): the shared custom PRNG, or this flow's serialisable mulberry32. */\n private readonly rng = (): number => {\n if (this.host.customRng) return this.host.customRng();\n const a = (this.rngState + 0x6d2b79f5) | 0;\n this.rngState = a;\n let t = Math.imul(a ^ (a >>> 15), 1 | a);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n\n // -- Strings / beats ------------------------------------------------------\n\n private beatResult(beat: Beat): StepResult {\n // Accumulated author tags (#215): the beat's own tags unioned with every\n // ancestor's. Omitted from the step when empty (parity with `gameData`).\n const tags = this.host.tagIndex.get(beat.id);\n const withTags = tags && tags.length ? { tags } : {};\n // Inline `{@ref}` interpolation (spec §16): text beats always interpolate;\n // line beats interpolate only in a non-voiced project (voiced lines are\n // static). Game-event beats carry no localised content.\n switch (beat.kind) {\n case \"gameEvent\":\n return { type: \"gameEvent\", id: beat.id, gameData: beat.gameData, ...withTags };\n case \"text\":\n return { type: \"text\", id: beat.id, text: this.interpolate(this.resolveString(beat.id)), gameData: beat.gameData, ...withTags };\n case \"line\": {\n const raw = this.resolveString(beat.id);\n // Closed captions (#214) apply to DIALOGUE lines only: strip cues when captions are off. Two ways a\n // line goes SILENT (off only): the caption CHARACTER speaks it (whole line is a caption - omit all\n // dialogue, delimiters or not), or stripping cues leaves it empty. A silent line still FIRES (audio\n // plays + visits count) but carries no text + no speaker, so no caption shows.\n const off = !this.host.captionsOn;\n const captionChar = off && beat.character === this.host.captionCharacter; // captionCharacter is always set (defaults SFX)\n const text = captionChar ? \"\" : this.captionLine(this.host.bundle.voiced ? raw : this.interpolate(raw));\n const silent = off && text.length === 0;\n return {\n type: \"line\",\n id: beat.id,\n text,\n character: silent ? undefined : beat.character,\n characterName: silent ? undefined : this.resolveCharacterName(beat.character),\n direction: silent ? undefined : beat.direction,\n gameData: beat.gameData,\n ...withTags,\n };\n }\n }\n }\n\n /**\n * Expand inline `{@ref}` slots (spec §16) against this flow's CURRENT property state. Public so an\n * IDs-only game can apply the same property replacement to a string it looked up in its own loc system:\n * the engine handed it the beat ID, the game fetched its translation, then calls `flow.interpolate(...)`.\n */\n interpolate(raw: string): string {\n return interpolate(raw, (ref) => this.getProperty(ref));\n }\n\n /**\n * Apply the project's caption rule to a string UNCONDITIONALLY (#214): remove every cue span between\n * the project's delimiters and collapse the whitespace. Public so an IDs-only game - which looks up\n * its own strings - can match the embedded runtime: `flow.stripCaptions(flow.interpolate(text))` when\n * its own captions setting is off. (Embedded play does this automatically for dialogue lines.)\n */\n stripCaptions(raw: string): string {\n return stripCaptions(raw, this.host.captionOpen, this.host.captionClose);\n }\n\n /** Caption-strip a dialogue line ONLY when captions are off; otherwise pass the text through. The\n * internal gate the engine applies to every `line` beat / line-kind prompt. */\n private captionLine(text: string): string {\n return this.host.captionsOn ? text : this.stripCaptions(text);\n }\n\n /**\n * An option's prompt (spec §5): the Option group's `prompt` beat, resolved + interpolated\n * (choice labels are on-screen text, so they interpolate, spec §16). For the degenerate\n * bare-snippet tolerance - or an Option group authored without a prompt - it falls back to the\n * option's first content line. NO look-ahead. Undefined only when even that is absent.\n */\n private promptFor(node: SelectableNode): ChoicePrompt | undefined {\n const beat = this.promptBeatOf(node);\n if (!beat) return undefined;\n const text = this.interpolate(this.resolveString(beat.id));\n // A line-kind prompt is dialogue, so captions apply to it; a text-kind prompt is left as-is.\n return beat.kind === \"line\"\n ? { kind: \"line\", text: this.captionLine(text), character: beat.character, characterName: this.resolveCharacterName(beat.character), direction: beat.direction }\n : { kind: \"text\", text };\n }\n\n /** The prompt BEAT of an option: the Option group's `prompt`, else (tolerance) its first content line. */\n private promptBeatOf(node: SelectableNode): LineBeat | TextBeat | undefined {\n if (node.type === \"group\" && node.prompt) return node.prompt;\n const snippet = node.type === \"snippet\" ? node : this.firstTextSnippetIn(node.children);\n return (snippet?.beats ?? []).find((b): b is LineBeat | TextBeat => b.kind === \"line\" || b.kind === \"text\");\n }\n\n /** The first snippet with a line/text beat within a child list, depth-first in authored order. */\n private firstTextSnippetIn(children: SelectableNode[]): CompiledSnippet | undefined {\n let found: CompiledSnippet | undefined;\n walkNodes<SelectableNode>(children, (n) => {\n if (!found && n.type === \"snippet\" && (n.beats ?? []).some((b) => b.kind === \"line\" || b.kind === \"text\")) {\n found = n;\n }\n });\n return found;\n }\n\n private resolveString(id: string): string {\n if (this.host.emitIds) return id; // IDs-only build: the game resolves text from this id itself\n const active = this.host.strings[id];\n if (active !== undefined) return active;\n // A key the active locale is missing falls back to the default-locale (source) text, but is flagged\n // LOUDLY: an untranslated string is a hard fail authors must notice, not silently paper over. Only a\n // key absent from the default locale too (never extracted) degrades to its bare id.\n const source = this.host.defaultStrings[id];\n return source !== undefined ? `<Untranslated: ${id}> ${source}` : id;\n }\n\n /** A character's player-facing name: the `cast:<name>` string in the active locale, else the default\n * locale, else the authoring `displayName`. Undefined when the character has no display name at all\n * (the host falls back to the `character` token itself). */\n private resolveCharacterName(character: string | undefined): string | undefined {\n if (character === undefined) return undefined;\n if (this.host.emitIds) return undefined; // IDs-only: omit the display name; the game maps the `character` token\n const key = castStringKey(character);\n return this.host.strings[key] ?? this.host.defaultStrings[key] ?? this.host.castDisplay.get(character);\n }\n\n /** Split a ref into scope + name. Tokens: `@scene`, foreign tokens, else `@patter` (incl. bare `@name`). */\n private splitRef(ref: string): { scope: string; name: string } {\n // host.shared.has(\"patter\") is true, so it covers @patter + every foreign token; @scene is explicit.\n let hit = this.host.refSplitCache.get(ref);\n if (!hit) { hit = splitRef(ref, (t) => t === \"scene\" || this.host.shared.has(t)); this.host.refSplitCache.set(ref, hit); }\n return hit;\n }\n\n /** The per-flow registry: the NOT-shared `@patter` globals (the shared ones live on the host). */\n private freshLocal(): ScopeRegistry {\n return new ScopeRegistry().defineOwned(\"patter\", this.host.patterLocalDecls);\n }\n\n /**\n * Seed a scene's `@scene` props (spec §7). The not-shared props seed THIS flow's\n * bag the first time it enters (persist across re-entries thereafter); the shared\n * props seed the host's stage bag the first time ANY flow enters the scene (shared\n * and persistent thereafter - a later flow finds it present and leaves it).\n * `temporary` props are the exception: reseeded to their default on every entry.\n */\n private seedScene(scene: CompiledScene): void {\n const shared = this.host.sceneSharedNames.get(scene.id) ?? new Set<string>();\n if (!this.sceneBags.has(scene.id)) {\n const bag: Record<string, ScalarValue> = {};\n for (const decl of scene.sceneProps ?? []) {\n const name = decl.name.toLowerCase();\n if (!shared.has(name)) bag[name] = sceneDefault(decl);\n }\n this.sceneBags.set(scene.id, bag);\n }\n if (!this.host.stageBags.has(scene.id)) {\n const bag: Record<string, ScalarValue> = {};\n for (const decl of scene.sceneProps ?? []) {\n const name = decl.name.toLowerCase();\n if (shared.has(name)) bag[name] = sceneDefault(decl);\n }\n this.host.stageBags.set(scene.id, bag);\n }\n\n // `temporary` props are reseeded to their default on EVERY entry (\"fresh each\n // playthrough\"), rather than persisting across re-entries like the rest.\n for (const decl of scene.sceneProps ?? []) {\n if (!decl.temporary) continue;\n const name = decl.name.toLowerCase();\n const bag = shared.has(name) ? this.host.stageBags.get(scene.id) : this.sceneBags.get(scene.id);\n if (bag) bag[name] = sceneDefault(decl);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers.\n// ---------------------------------------------------------------------------\n\n/** Serialise a `sequence` selector-cursor map to plain snapshots. */\nfunction serialiseSelectors(map: Map<string, SelectorState>): Record<string, SelectorSnapshot> {\n const out: Record<string, SelectorSnapshot> = {};\n for (const [id, st] of map) {\n const v: SelectorSnapshot = {};\n if (st.seq !== undefined) v.seq = st.seq;\n if (st.bag) v.bag = [...st.bag];\n if (st.last !== undefined) v.last = st.last;\n out[id] = v;\n }\n return out;\n}\n\n/** Rebuild a `sequence` selector-cursor map from snapshots. */\nfunction deserialiseSelectors(rec: Record<string, SelectorSnapshot> | undefined): Map<string, SelectorState> {\n const map = new Map<string, SelectorState>();\n for (const [id, v] of Object.entries(rec ?? {})) {\n const st: SelectorState = {};\n if (v.seq !== undefined) st.seq = v.seq;\n if (v.bag) st.bag = [...v.bag];\n if (v.last !== undefined) st.last = v.last;\n map.set(id, st);\n }\n return map;\n}\n\n/** Adapt a Patter `PropertyDecl` to a registry `ScopeDeclaration` (same type vocabulary). */\nfunction toDecl(decl: PropertyDecl): ScopeDeclaration {\n return { name: decl.name, type: decl.type, values: decl.values, default: decl.default };\n}\n\n/** A shared-decl's value for reset-to-default: its declared default, else the type default. */\nfunction declDefault(d: ScopeDeclaration): ScalarValue {\n if (d.default !== undefined) return d.default;\n switch (d.type) {\n case \"number\": return 0;\n case \"string\": return \"\";\n case \"flags\": return [];\n case \"enum\": return d.values?.[0] ?? \"\";\n default: return false; // boolean (and any unknown) → false\n }\n}\n\n/** A host-scope declaration (`@world.x`) → registry declaration. */\nfunction toForeignDecl(decl: HostScopeDecl): ScopeDeclaration {\n return { name: decl.name, type: decl.type, values: decl.values, default: decl.default, writable: decl.writable };\n}\n\n/** The seed value for a host-scope property: its declared default, else the type default. */\nfunction hostScopeDefault(decl: HostScopeDecl): ScalarValue {\n if (decl.default !== undefined) return decl.default;\n switch (decl.type) {\n case \"boolean\": return false;\n case \"number\": return 0;\n case \"string\": return \"\";\n case \"flags\": return [];\n case \"enum\": return decl.values?.[0] ?? \"\";\n }\n}\n\n/** Build a live in-memory `{ get, set }` resolver for a self-backed host scope (the standalone `@world`):\n * a plain bag seeded from declaration defaults. Declared-but-unseeded names still read `undefined`; an\n * opaque scope (no declarations) starts empty and accepts any name. Per-property read-only is enforced at\n * validation, not here (the registry is per-scope), so `set` accepts any name. */\nfunction selfBackedResolver(decls: HostScopeDecl[]): ScopeResolver {\n // Keyed LOWERCASE. The compiler lowercases every property reference, so an AST reads `isnight` where\n // the declaration says `isNight`; seeding the bag verbatim meant any declared name carrying a capital\n // was never found, read as undefined, and silently took the falsy branch. `@patter` and `@scene`\n // already normalise (patterSharedNames / sceneSharedNames); this resolver was the one that did not.\n const key = (name: string): string => name.toLowerCase();\n const bag = new Map<string, ScalarValue>();\n for (const d of decls) bag.set(key(d.name), hostScopeDefault(d));\n return {\n get: (name) => bag.get(key(name)),\n set: (name, value) => { bag.set(key(name), value); },\n };\n}\n\n/** The seed value for a scene-local property (its `default`, else the type default). */\nfunction sceneDefault(decl: PropertyDecl): ScalarValue {\n if (decl.default !== undefined) return decl.default;\n switch (decl.type) {\n case \"boolean\": return false;\n case \"number\": return 0;\n case \"string\": return \"\";\n case \"flags\": return [];\n case \"enum\": return decl.values?.[0] ?? \"\";\n }\n}\n\nfunction truthy(v: ScalarValue): boolean {\n if (typeof v === \"boolean\") return v;\n if (typeof v === \"number\") return v !== 0;\n if (typeof v === \"string\") return v !== \"\";\n return v.length > 0; // string[]\n}\n","// ---------------------------------------------------------------------------\n// Author tags (#215): a cross-cutting label layer baked into the bundle.\n//\n// A node's *accumulated* tags are the union of its own and every ancestor's,\n// ordered outermost-first (scene → block → group(s) → snippet → beat) and\n// deduped. That accumulation is purely structural, it depends only on where a\n// node sits in the tree, not on play state, so it's precomputed ONCE at engine\n// load into a flat `id -> string[]` index and read back as O(1) lookups for both\n// the delivered step `tags` and the `tagsFor*` accessors.\n// ---------------------------------------------------------------------------\n\nimport type { Bundle, CompiledGroup, CompiledSnippet } from \"@patterkit/model\";\n\nfunction dedupe(tags: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const t of tags) if (!seen.has(t)) { seen.add(t); out.push(t); }\n return out;\n}\n\n/**\n * Map every node id (scene / block / group / snippet / beat) to its accumulated\n * tags. Node ids are globally unique within a project (the validator enforces\n * it), so one flat map suffices. Nodes with no tags anywhere up the chain map to\n * an empty array.\n */\nexport function buildTagIndex(bundle: Bundle): Map<string, string[]> {\n const index = new Map<string, string[]>();\n\n const visit = (node: CompiledGroup | CompiledSnippet, inherited: string[]): void => {\n const acc = dedupe([...inherited, ...(node.tags ?? [])]);\n index.set(node.id, acc);\n if (node.type === \"group\") {\n for (const child of node.children) visit(child, acc);\n } else {\n for (const beat of node.beats ?? []) index.set(beat.id, dedupe([...acc, ...(beat.tags ?? [])]));\n }\n };\n\n for (const scene of Object.values(bundle.scenes)) {\n const sceneAcc = dedupe(scene.tags ?? []);\n index.set(scene.id, sceneAcc);\n for (const block of scene.blocks) {\n const blockAcc = dedupe([...sceneAcc, ...(block.tags ?? [])]);\n index.set(block.id, blockAcc);\n for (const child of block.children) visit(child, blockAcc);\n }\n }\n\n return index;\n}\n","// ---------------------------------------------------------------------------\n// describeBundle - the bundle inspector's runtime half.\n//\n// A BUNDLE-level function, deliberately NOT an Engine method. It answers the\n// integrator's question from the imported asset alone, with no engine, no state\n// and nothing running:\n//\n// I dropped a .patterc into my project. What may my game code call, and is\n// this the bundle I think it is?\n//\n// That is a different question from the one the property examiner answers. The\n// examiner (Engine.listProperties + the per-engine state panels) watches and\n// edits a LIVE game. This is static: it is the API boundary made visible, read\n// off the asset in an editor inspector before anything runs.\n//\n// Three readers. The integrator reads the addresses and the host scopes: those\n// are what game code may call and what it must supply. The writer reads them\n// when nothing happens on `runFlow(\"x\", \"some_scene\")` - the address they typed\n// is not in the list. The designer reads the identity and counts to confirm\n// what actually shipped.\n//\n// Everything is in BUNDLE ORDER, never sorted: two runtimes must render the\n// same rows in the same sequence, and bundle order is the only order all four\n// ports can agree on without importing a collation rule.\n//\n// Cheap by construction. Scenes, blocks and declarations are walked once;\n// nothing here parses an expression, resolves a string table, or touches the\n// per-locale text. `beats` is the one count that requires descending to the\n// leaves, and it is a running total taken during the same single walk.\n// ---------------------------------------------------------------------------\n\nimport { effectiveGameId } from \"@patterkit/model\";\nimport type {\n Bundle, CompiledBlock, CompiledGroup, CompiledSnippet, GameDataField,\n GameDataNodeKind, PropertyDecl, PropertyType, ScalarValue,\n} from \"@patterkit/model\";\n\n/** Which bundle this is: identity, staleness fingerprints, and how it ships. */\nexport interface BundleIdentity {\n /** The bundle schema tag (\"patter/bundle@0\"). */\n schema: string;\n /** The project name. A save must agree with this. */\n project: string;\n /** The authored bundle version, if the project stamps one. */\n version?: string;\n /** Fingerprint over the WHOLE bundle: what binds saves and gates staleness. */\n hash?: string;\n /** The same fingerprint with the string tables left out. Equal structureHash\n * plus a different hash means a TEXT-ONLY edit, which is what makes a live\n * hot-swap safe. Showing both lets an integrator tell those apart at sight. */\n structureHash?: string;\n /** Project-wide VO mode. */\n voiced: boolean;\n defaultLocale: string;\n locales: string[];\n /** How strings ship: \"embedded\" (the runtime resolves text) or \"ids\" (the\n * runtime emits beat IDs and the game localises them itself). */\n localisation: \"embedded\" | \"ids\";\n /** True when the source locale was embedded purely for debug playback. Such a\n * build is NOT shippable, which is worth saying loudly in an inspector. */\n sourceDebug: boolean;\n}\n\n/** One scene, and the addresses game code may aim at inside it. */\nexport interface AddressSummary {\n /** The host-facing scene address: what `runFlow` / `goto` take. Derived from\n * the name when the author set no explicit gameId, exactly as the runtime\n * resolves it, so this list is the truth rather than an approximation. */\n gameId: string;\n /** The authored scene name, for recognising the row. */\n name: string;\n /** Block addresses within this scene. A block address is SCENE-SCOPED: the\n * pair is the address, which is why these are nested rather than flattened. */\n blocks: { gameId: string; name: string }[];\n}\n\n/** One author-defined gameData field: part of the host-facing data surface. */\nexport interface GameDataFieldSummary {\n name: string;\n type: string;\n /** Whether the schema carries a fallback. Sparse storage means a node that\n * sets nothing reads this, so a field with no default can arrive absent. */\n hasDefault: boolean;\n /** Allowed values for an enum field: the set host code switches on. */\n values?: string[];\n purpose?: string;\n}\n\n/** The gameData fields declared for one kind of node. */\nexport interface GameDataSummary {\n kind: GameDataNodeKind;\n fields: GameDataFieldSummary[];\n}\n\n/** One declared property. `hasDefault` rather than the value itself: an\n * inspector wants to know whether the host MUST supply something. */\nexport interface PropertySummary {\n name: string;\n type: PropertyType;\n hasDefault: boolean;\n default?: ScalarValue;\n /** Shared across all flows, or kept per-flow. Defaults differ by scope\n * (`@patter` shared, `@scene` per-flow), so it is resolved here. */\n shared: boolean;\n}\n\n/** A host scope (`@world` and friends): what the GAME must supply.\n *\n * The highest-value section of the whole description. Today an integrator\n * discovers a missing world property when a condition silently reads a\n * self-backed default and a branch never fires. */\nexport interface HostScopeSummary {\n /** The token after `@`, e.g. \"world\". */\n token: string;\n /** Scope-level read/write default for its declarations. */\n writable: boolean;\n /** An OPAQUE scope declares no names: any name is accepted, unchecked. The\n * host contract is then \"anything\", which is worth showing as such rather\n * than as an empty property list. */\n opaque: boolean;\n properties: PropertySummary[];\n}\n\n/** Story-owned declarations, for orientation rather than for calling. */\nexport interface OwnedProperties {\n /** Project-level (`@patter`). */\n patter: PropertySummary[];\n /** Per scene (`@scene`), keyed by the scene's host address. */\n scene: { gameId: string; properties: PropertySummary[] }[];\n}\n\n/** \"Is this the right build?\" at a glance. */\nexport interface BundleCounts {\n scenes: number;\n blocks: number;\n groups: number;\n snippets: number;\n /** Snippet beats. This is the SAME population `Engine.getBeatSequence` walks, deliberately, so a\n * tool that lists beats and an inspector that counts them never disagree. Choice prompts are not\n * in it - see `prompts`. */\n beats: number;\n /** Choice-option prompts: beats that live on a group rather than in a snippet.\n *\n * Counted separately rather than folded into `beats` because folding them in would make this\n * number disagree with `getBeatSequence`, and leaving them out entirely would understate a\n * choice-heavy story - a branching script could report a handful of beats and look like the wrong\n * build. Neither silence nor a redefinition; a second row. */\n prompts: number;\n /** Beats that fire a game event rather than producing player-facing words. */\n gameEvents: number;\n /** Cast members the bundle carries (player-facing only; the compiler strips\n * the authoring fields). */\n cast: number;\n}\n\nexport interface BundleDescription {\n identity: BundleIdentity;\n /** Everything game code may aim at, in bundle order. */\n addresses: AddressSummary[];\n /** What the host must supply. */\n hostScopes: HostScopeSummary[];\n /** What the story owns. */\n properties: OwnedProperties;\n /** The author-defined data surface, grouped by node kind. */\n gameData: GameDataSummary[];\n counts: BundleCounts;\n}\n\n/** Resolve a declaration's sharing default, which differs by the scope it sits\n * in: a project-level property is shared, a scene-local one is per-flow. */\nconst isShared = (d: PropertyDecl, scopeDefault: boolean): boolean =>\n d.shared ?? scopeDefault;\n\nfunction summariseProperty(d: PropertyDecl, scopeDefault: boolean): PropertySummary {\n return {\n name: d.name,\n type: d.type,\n hasDefault: d.default !== undefined,\n ...(d.default !== undefined ? { default: d.default } : {}),\n shared: isShared(d, scopeDefault),\n };\n}\n\nfunction summariseField(f: GameDataField): GameDataFieldSummary {\n return {\n name: f.name,\n type: f.type,\n hasDefault: f.default !== undefined,\n ...(f.values ? { values: [...f.values] } : {}),\n ...(f.purpose ? { purpose: f.purpose } : {}),\n };\n}\n\n/** One pass over a block's tree, accumulating counts. Iterative rather than\n * recursive: a deeply nested choice tree should not put an inspector's stack\n * at risk, and the traversal order does not matter for a count. */\nfunction countBlock(block: CompiledBlock, counts: BundleCounts): void {\n counts.blocks++;\n const stack: Array<CompiledGroup | CompiledSnippet> = [...block.children];\n while (stack.length) {\n const node = stack.pop()!;\n if (node.type === \"group\") {\n counts.groups++;\n if (node.prompt) counts.prompts++;\n stack.push(...node.children);\n continue;\n }\n counts.snippets++;\n for (const beat of node.beats ?? []) {\n counts.beats++;\n if (beat.kind === \"gameEvent\") counts.gameEvents++;\n }\n }\n}\n\n/**\n * Describe a compiled bundle: what it is, and what a game may call on it.\n *\n * Pure and allocation-light. Safe to call from an editor inspector on every\n * selection, though a details panel should still build its rows once rather\n * than per repaint.\n */\nexport function describeBundle(bundle: Bundle): BundleDescription {\n const counts: BundleCounts = {\n scenes: 0, blocks: 0, groups: 0, snippets: 0, beats: 0, prompts: 0, gameEvents: 0,\n cast: bundle.cast?.length ?? 0,\n };\n\n const addresses: AddressSummary[] = [];\n const sceneProps: OwnedProperties[\"scene\"] = [];\n for (const scene of Object.values(bundle.scenes)) {\n counts.scenes++;\n const gameId = effectiveGameId(scene);\n addresses.push({\n gameId,\n name: scene.name,\n blocks: scene.blocks.map((b) => ({ gameId: effectiveGameId(b), name: b.name })),\n });\n for (const block of scene.blocks) countBlock(block, counts);\n // Scene-local declarations default to PER-FLOW, unlike project-level ones.\n if (scene.sceneProps?.length) {\n sceneProps.push({ gameId, properties: scene.sceneProps.map((d) => summariseProperty(d, false)) });\n }\n }\n\n const hostScopes: HostScopeSummary[] = (bundle.scopeRegistry?.scopes ?? []).map((s) => ({\n token: s.token,\n writable: s.writable ?? true,\n opaque: s.declarations === undefined,\n // A host scope's values live outside the story, so \"shared\" is not a choice\n // its declarations make; they are world-wide by nature.\n properties: (s.declarations ?? []).map((d) => summariseProperty(d as PropertyDecl, true)),\n }));\n\n const gameData: GameDataSummary[] = Object.entries(bundle.gameDataFields ?? {})\n .filter(([, fields]) => (fields?.length ?? 0) > 0)\n .map(([kind, fields]) => ({\n kind: kind as GameDataNodeKind,\n fields: (fields ?? []).map(summariseField),\n }));\n\n return {\n identity: {\n schema: bundle.schema,\n project: bundle.content.project,\n ...(bundle.content.version !== undefined ? { version: bundle.content.version } : {}),\n ...(bundle.content.hash !== undefined ? { hash: bundle.content.hash } : {}),\n ...(bundle.content.structureHash !== undefined ? { structureHash: bundle.content.structureHash } : {}),\n voiced: bundle.voiced,\n defaultLocale: bundle.locales.default,\n locales: [...bundle.locales.included],\n // Absent means \"embedded\": the back-compat default a bundle written before\n // the field existed relies on.\n localisation: bundle.localisation?.mode ?? \"embedded\",\n sourceDebug: bundle.localisation?.sourceDebug ?? false,\n },\n addresses,\n hostScopes,\n properties: {\n patter: (bundle.properties ?? []).map((d) => summariseProperty(d, true)),\n scene: sceneProps,\n },\n gameData,\n counts,\n };\n}\n","// gameData read helpers (spec: author-defined custom fields per node type). The published bundle\n// carries the field SCHEMA per node type (`bundle.gameDataFields`, each field with its default) plus\n// each node's SPARSE overrides (`node.gameData`). Storage is sparse + merge-at-read: a node holds only\n// the values it overrides, and a reader falls back to the field's default. These pure helpers do that\n// resolution so a host doesn't re-implement it.\n\nimport type { Bundle, GameData, GameDataField, GameDataNodeKind } from \"@patterkit/model\";\n\n/** The author-defined gameData fields declared for a node TYPE in a bundle (empty when none). */\nexport function gameDataFields(bundle: Bundle, kind: GameDataNodeKind): GameDataField[] {\n return bundle.gameDataFields?.[kind] ?? [];\n}\n\n/** One node's effective value for a field: its sparse OVERRIDE if present, else the field's declared\n * default (undefined if neither is set). `fields` is the schema for the node's type. */\nexport function gameDataValue(fields: GameDataField[], node: GameData | undefined, name: string): unknown {\n if (node && Object.prototype.hasOwnProperty.call(node, name)) return node[name];\n return fields.find((f) => f.name === name)?.default;\n}\n\n/** A node's FULL effective gameData: every declared field resolved (override or default), plus any\n * override keys with no matching field (orphans, kept verbatim). Fields left with no value are omitted. */\nexport function effectiveGameData(fields: GameDataField[], node: GameData | undefined): GameData {\n const out: GameData = {};\n for (const f of fields) {\n const v = gameDataValue(fields, node, f.name);\n if (v !== undefined) out[f.name] = v;\n }\n for (const [k, v] of Object.entries(node ?? {})) if (!(k in out)) out[k] = v;\n return out;\n}\n"],"mappings":"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","names":["next","id","effectiveGameId"]}
1
+ {"version":3,"sources":["../src/engine.ts","../src/tags.ts","../src/describe.ts","../src/gamedata.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// @patterkit/runtime - the reference runtime.\n//\n// An `Engine` is the world + flow manager: it owns the compiled Bundle, the\n// shared state (shared `@patter` globals + shared `@scene` props + host foreign\n// scopes), and a set of named **flows**. All *play* happens on a `Flow` handle\n// (`engine.openFlow(id, ...)`): a flow has its own execution cursor, its own PRNG,\n// and its own copy of the NOT-shared state (per-flow `@patter` globals + per-flow\n// `@scene` props). Multiple flows run concurrently and independently - a flow is\n// addressed explicitly (`alice.advance()`), so there is no ambient \"current flow\".\n//\n// Scopes are just two tokens - `@patter` (global; bare `@name`) and `@scene`\n// (scene-local) - with an orthogonal per-property `shared` flag (default: shared\n// for `@patter`, per-flow for `@scene`). So each token spans two storage areas:\n// the shared half lives on the engine, the per-flow half on the flow; a read/write\n// routes by the property's `shared` flag. (Mirrors Storylet Studio's @world/@site.)\n//\n// A flow plays the select-one-child model (spec §4): a block branches one\n// eligible child; a group runs its selector (branch / sequence / choice -\n// `sequence` covers order x exhaust); a `choice` group stops for the host; a snippet runs onEnter, delivers\n// beats, runs onExit, follows its jump. Falling off the end ends the flow.\n// Cross-flow jumps are not a thing - the host switches flows.\n//\n// `engine.saveGame()` / `loadGame()` snapshot + restore the WHOLE game: `@patter`\n// plus every live flow's scopes + PRNG + cursor.\n// ---------------------------------------------------------------------------\n\nimport { evaluate, deserialiseAst } from \"@wildwinter/expr\";\nimport type { ScalarValue, EvalContext, ExprNode } from \"@wildwinter/expr\";\nimport { matchedSpecificity as scoreSpecificity, type EvalTruthy } from \"@wildwinter/expr-specificity\";\nimport { ScopeRegistry } from \"@wildwinter/scoperegistry\";\nimport type { ScopeDeclaration, ScopeResolver } from \"@wildwinter/scoperegistry\";\nimport { patterDialect, interpolate, splitRef, stripCaptions } from \"@patterkit/dialect\";\nimport { walkNodes, effectiveGameId, castStringKey, DEFAULT_CAPTION_DELIMITERS, DEFAULT_CAPTION_CHARACTER } from \"@patterkit/model\";\nimport { buildTagIndex } from \"./tags.js\";\nimport type {\n Bundle, CompiledScene, CompiledBlock, CompiledGroup, CompiledSnippet,\n CompiledEffect, Beat, LineBeat, TextBeat, GameData, Expression, PropertyDecl, PropertyType, Jump, HostScopeDecl,\n} from \"@patterkit/model\";\n\ntype SelectableNode = CompiledGroup | CompiledSnippet;\n\n// Compiled expressions are immutable, so each one's AST is deserialised once -\n// per evaluation was the engine's hottest path (every condition / effect / slot).\nconst astCache = new WeakMap<Expression, ExprNode>();\n\n/** A property-state snapshot: owned scope -> property name -> value. */\nexport type EngineSave = Record<string, Record<string, ScalarValue>>;\n\n/** Serialised `sequence` selector visit state for one group (spec §4 / §7). */\nexport interface SelectorSnapshot {\n seq?: number; // sequential cursor (visits taken)\n bag?: string[]; // shuffle: child ids still undrawn this pass\n last?: string; // last child id picked (no-immediate-repeat)\n}\n\n/** One entry on a flow's continuation stack: a position within a container's children. */\nexport interface StackFrame {\n sceneId: string;\n /** A block id or a run-group id (both are sequential containers). */\n containerId: string;\n index: number;\n /** SNAPSHOT-ONLY (never set on a live frame): the id of the child at `index` when the save was\n * taken. On restore the child is re-found by this id, so a save survives siblings being inserted,\n * removed, or reordered before the cursor (live bundle refresh / patched-game saves). Absent (an\n * older save, or a frame saved at its container's end) falls back to the raw `index`. */\n nextId?: string;\n}\n\n/** The serialised cursor + scopes + PRNG of a single flow. */\nexport interface FlowSnapshot {\n /** This flow's owned-scope values = the NOT-shared `@patter` globals (under token \"patter\"). */\n scopes: EngineSave;\n /** Per-scene NOT-shared `@scene` bags (scene id -> name -> value); persist across re-entries (spec §7). */\n sceneBags: Record<string, Record<string, ScalarValue>>;\n /** This flow's built-in PRNG position (mulberry32 state). */\n rngState: number;\n /** This flow's per-node entry counts (node id -> times entered by this flow). */\n visits: Record<string, number>;\n cursor: {\n flowEnded: boolean;\n currentSceneId: string | null;\n /** The continuation stack (call frames + the active block run). */\n stack: StackFrame[];\n activeSnippetId: string | null;\n beatIndex: number;\n /** The pending choice's exact option set, REPLAYED on load (schema 9.3). */\n pendingChoice: SavedChoice | null;\n /** The chosen option owning a prompt still to be replayed (save taken between choose + advance).\n * Optional / absent in older saves -> no pending prompt. */\n pendingPromptOwnerId?: string | null;\n /** This flow's `sequence` selector cursors. */\n selectors: Record<string, SelectorSnapshot>;\n };\n}\n\n/**\n * A pending choice as saved: the option set the player was shown, restored\n * verbatim - re-deriving on load would re-evaluate conditions (consuming PRNG\n * draws a second time) and could mutate the choice under the player.\n */\nexport interface SavedChoice {\n groupId: string;\n options: ChoiceOption[];\n}\n\n/** A full resumable save-game: shared `@patter` state + every live flow. */\nexport interface SaveGame {\n version: number;\n /** Shared `@patter` globals (owned scope \"patter\"). */\n shared: EngineSave;\n /** World-wide per-node entry counts (node id -> times entered by any flow). */\n sharedVisits: Record<string, number>;\n /** Shared selector cursors (node id -> snapshot) for `shared` memoried selectors. */\n sharedSelectors: Record<string, SelectorSnapshot>;\n /** Shared, scene-namespaced `@scene` bags (scene id -> name -> value) - the shared scene props. */\n stageBags: Record<string, Record<string, ScalarValue>>;\n /** Each live flow's snapshot, keyed by flow id. */\n flows: Record<string, FlowSnapshot>;\n}\n\n/** What `Flow.advance()` surfaces to the host at each stop. */\nexport type StepResult =\n | { type: \"line\"; id: string; text: string; character?: string; characterName?: string; direction?: string; gameData?: GameData; tags?: string[] }\n | { type: \"text\"; id: string; text: string; gameData?: GameData; tags?: string[] }\n | { type: \"gameEvent\"; id: string; gameData?: GameData; tags?: string[] }\n | { type: \"choice\"; groupId: string; options: ChoiceOption[] }\n | { type: \"end\" };\n\n// --- Static structure introspection (editor / dev tooling) -------------------\n// A read-only view of the AUTHORED tree (scenes -> blocks -> groups/snippets -> beats), for dev\n// tools that build against the writer's structure (e.g. an Unreal Sequencer of subsequences per\n// beat). Static: no flow, no play state. Per-beat data mirrors what a StepResult would carry\n// (source text, author gameData, accumulated tags), read at the default locale.\n\n/** One beat's static data - the same shape a delivered step carries, resolved at the source locale. */\nexport interface BeatInfo {\n id: string;\n kind: \"line\" | \"text\" | \"gameEvent\";\n /** Speaker token (line only). */\n character?: string;\n /** Resolved display name for `character` (source locale), if the cast declares one. */\n characterName?: string;\n /** Performance direction (line only). */\n direction?: string;\n /** Source text, un-interpolated (line / text). Omitted for gameEvent and IDs-only bundles. */\n text?: string;\n /** Author gameData overrides on this beat (raw, as the step carries them). Omitted when empty. */\n gameData?: GameData;\n /** Accumulated author tags (scene -> block -> group(s) -> snippet -> beat). Omitted when empty. */\n tags?: string[];\n}\n\n/** A node in the outline tree: a group (with its selector + children) or a snippet (with its beats). */\nexport interface OutlineNode {\n type: \"group\" | \"snippet\";\n id: string;\n tags?: string[];\n // group only\n selector?: string;\n /** A choice/option group's prompt beat, if any. */\n prompt?: BeatInfo;\n children?: OutlineNode[];\n // snippet only\n beats?: BeatInfo[];\n jumpTo?: string;\n jumpMode?: \"jump\" | \"call\";\n}\n\n/** A block in the outline tree. */\nexport interface OutlineBlock {\n id: string;\n gameId?: string;\n name: string;\n tags?: string[];\n children: OutlineNode[];\n}\n\n/** A scene in the outline tree. */\nexport interface OutlineScene {\n id: string;\n gameId?: string;\n name: string;\n tags?: string[];\n blocks: OutlineBlock[];\n}\n\n/** One beat in document order, with the scene/block/snippet it lives in (the flat view). */\nexport interface FlatBeat {\n sceneId: string;\n blockId: string;\n snippetId: string;\n beat: BeatInfo;\n}\n\n/** What {@link Flow.advanceToStop} returns: the beats walked, and the choice / end that stopped it. */\nexport interface AdvanceToStopResult {\n /** The line / text / game-event beats played on the way to the stop (never a choice / end). */\n played: Array<Extract<StepResult, { type: \"line\" | \"text\" | \"gameEvent\" }>>;\n stop: Extract<StepResult, { type: \"choice\" | \"end\" }>;\n}\n\n/** The choice text of an option (spec §5): its `prompt` beat, resolved + interpolated. */\nexport interface ChoicePrompt {\n kind: \"line\" | \"text\";\n /** Display text (interpolated; may be empty - the host can render from gameData / an icon). */\n text: string;\n /** Speaker / direction - present only for a `line` prompt (the PC's spoken choice). */\n character?: string;\n /** The speaker's resolved player-facing name (locale-aware; absent when the character has none). */\n characterName?: string;\n direction?: string;\n}\n\n/** A single option of a pending `choice` group. */\nexport interface ChoiceOption {\n /** The option's id (an Option group, or a degenerate option snippet) - pass to `choose()`. */\n id: string;\n /**\n * The option's `prompt` (spec §5) - the choice text as a structured line/text beat. For the\n * degenerate bare-snippet tolerance, derived from the snippet's first content line. Undefined\n * only when even that is absent; internal ids are never leaked as display text.\n */\n prompt?: ChoicePrompt;\n /** False when the option's condition fails; still returned (greyed) unless hidden. */\n eligible: boolean;\n gameData?: GameData;\n}\n\n/**\n * The host's **World Properties** resolver: a `{ get, set? }` the game provides so the story can read\n * (and, if you allow it, write) its `@world.*` values at runtime. Property metadata (types, read-only)\n * comes from the compiled bundle's declared world properties; the values themselves live in the host and\n * are never stored or saved by this engine.\n */\nexport type WorldResolver = ScopeResolver;\n\nexport interface EngineOptions {\n /**\n * Custom float-in-[0,1) source for `random()` / shuffle, shared by all flows.\n * Overrides the built-in seeded PRNG - but its position is NOT captured by\n * `saveGame()`. For resumable runs, use the built-in per-flow seed instead.\n */\n rng?: () => number;\n /** Default seed for each flow's built-in (serialisable) PRNG; override per flow in `openFlow`. */\n seed?: number;\n /** Active locale for string lookups (embedded localisation). Defaults to the bundle's default locale.\n * Ignored by an \"ids\" bundle, which emits beat IDs for the game to localise itself. */\n locale?: string;\n /** The host's resolver for **World Properties** (`@world.*`): the values the game owns and the story\n * reads. Omit it and the runtime self-backs `@world` from the declared defaults. Shared by all flows. */\n world?: WorldResolver;\n /**\n * Replay a chosen option's `prompt` as its first played beat (spec §5). Default `false`:\n * the prompt is a label only and `choose()` plays just the option's content. `true`: the\n * prompt beat is delivered first (the choice \"spoken back\"). A host decision, not authored.\n */\n replayPromptOnChoose?: boolean;\n /** Closed captions (#214): show non-spoken caption cues inside dialogue lines (the `[sigh]` in\n * `Oh dear. [sigh] What now?`). Default `true` (full text). `false` strips every cue + its delimiters\n * and collapses the whitespace - for a player who hears the audio and doesn't want the captions.\n * Toggle live with `engine.setClosedCaptions(...)`. */\n closedCaptions?: boolean;\n /** Diagnostics hook (opt-in, dev tooling only): fired with the choice's group id whenever a choice runs\n * DRY - no takeable option and no eligible fallback - so it falls through silently. The behaviour is\n * unchanged; this only makes the fall-through observable. The coverage harness uses it to flag choices\n * that ran dry. Leave it unset in shipped games (zero cost). */\n onDryChoice?: (groupId: string) => void;\n}\n\n/** One shared `@patter` property, for a live state inspector: its ref, declared type, current value,\n * declared default (for reset), and enum options. Mirrors the Unity / Godot ports' ListProperties. */\nexport interface PropertyRow {\n ref: string;\n type: PropertyType;\n value: ScalarValue | undefined;\n default: ScalarValue;\n values?: string[];\n}\n\n/** Options for opening a flow. */\nexport interface OpenFlowOptions {\n /** Scene to start at - its host-facing gameId (address) OR its internal id; defaults to the\n * bundle's first scene. */\n scene?: string;\n /** Block within the scene to start at - its gameId (scene-scoped address) OR its internal id. */\n block?: string;\n /** Seed for this flow's PRNG (defaults to the engine's `seed`). */\n seed?: number;\n}\n\ninterface ChoiceState {\n /** The choice group's id (saved alongside the verbatim option set - SavedChoice). */\n groupId: string;\n options: ChoiceOption[];\n byId: Map<string, SelectableNode>;\n}\n\ninterface SelectorState {\n seq?: number; // sequential cursor (visits taken)\n bag?: string[]; // shuffle: child ids still undrawn this pass (undefined = not started)\n last?: string; // last child id picked (no-immediate-repeat across reshuffles)\n}\n\n/** Shared, read-mostly context the engine hands to every flow it owns. */\ninterface FlowHost {\n bundle: Bundle;\n /** IDs-only build (`localisation.mode === \"ids\"`, no source-debug): the engine emits each beat's ID as\n * its text and omits character display names, leaving localisation to the game (use `flow.interpolate`\n * to apply `{@ref}` property replacement to a string the game looked up itself). */\n emitIds: boolean;\n strings: Record<string, string>;\n /** The DEFAULT locale's string table - fallback for a key the active locale is missing (notably the\n * cast display-name keys, seeded there from `displayName`). */\n defaultStrings: Record<string, string>;\n /** Cast canonical name -> authoring `displayName` (the unlocalised fallback when no loc string exists). */\n castDisplay: Map<string, string>;\n nodeIndex: Map<string, SelectableNode>;\n blockIndex: Map<string, { sceneId: string }>;\n blockById: Map<string, CompiledBlock>;\n /** Host-facing addresses (spec §6), shared with the engine: scene gameId -> internal id (project-wide),\n * and per-scene block gameId -> internal id. A flow needs them to resolve `goto` by address. */\n sceneGameIdToId: Map<string, string>;\n blockGameIdToId: Map<string, Map<string, string>>;\n /** Author tags (#215): node id -> accumulated tags (own + every ancestor's, deduped). Built once. */\n tagIndex: Map<string, string[]>;\n /** The SHARED `@patter` globals (owned scope \"patter\") + world properties (`@world`). */\n shared: ScopeRegistry;\n /** Decls for the shared `@patter` globals - (re)seed on `engine.reset()`. */\n patterSharedDecls: ScopeDeclaration[];\n /** Decls for the per-flow `@patter` globals - seed each flow's local registry. */\n patterLocalDecls: ScopeDeclaration[];\n /** Lowercase names of the SHARED globals (route a `@patter` ref to engine vs flow). */\n patterSharedNames: Set<string>;\n /** Per-scene set of SHARED `@scene` prop names (route a `@scene` ref to stage vs flow). */\n sceneSharedNames: Map<string, Set<string>>;\n /** World-wide per-node entry counts (node id -> times entered by any flow). */\n sharedVisits: Map<string, number>;\n /** Shared selector cursors (node id -> SelectorState) for `shared` memoried selectors. */\n sharedSelectors: Map<string, SelectorState>;\n /** Shared, scene-namespaced `@scene` bags (scene id -> name -> value) for shared scene props. */\n stageBags: Map<string, Record<string, ScalarValue>>;\n customRng?: () => number;\n /** Play a chosen option's prompt as its first beat (spec §5); default false. */\n replayPromptOnChoose?: boolean;\n /** Closed captions (#214). `captionsOn`: show caption cues in dialogue lines (default true); when\n * false the engine strips `captionOpen`…`captionClose` spans from line text. Mutable via\n * `setClosedCaptions` (one toggle, all flows - like setLocale). */\n captionsOn: boolean;\n captionOpen: string;\n captionClose: string;\n /** A cast member whose lines are pure captions: when captions are off ALL of its dialogue + speaker is\n * omitted (a silent line), delimiters or not. Default `SFX`. Empty = no caption character. */\n captionCharacter: string;\n /** Diagnostics hook (opt-in, dev only): fired when a choice runs DRY - nothing takeable and no eligible\n * fallback - so it falls through and the flow continues past it. Zero cost when unset; the coverage\n * harness passes it to surface silent fall-throughs. Not a gameplay signal (the behaviour is unchanged). */\n onDryChoice?: (groupId: string) => void;\n /** Memoised `splitRef` results (ref string -> {scope,name}). The split depends only on `shared`'s scope\n * set, which is fixed for the engine's life, so every effect target / `{@ref}` slot parses once. */\n refSplitCache: Map<string, { scope: string; name: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// Engine - the world + flow manager\n// ---------------------------------------------------------------------------\n\nexport class Engine {\n private readonly host: FlowHost;\n private readonly defaultSeed: number;\n private readonly flowsById = new Map<string, Flow>();\n /** Every locale's string table (the inline `bundle.strings`), kept so the active locale can be swapped\n * live (setLocale) without rebuilding the engine. Reassigned wholesale by `replaceStrings`\n * (live bundle refresh, tier 1), hence not readonly. */\n private allStrings: Record<string, Record<string, string>>;\n /** The currently active locale (string lookups + character names resolve in it). */\n private currentLocale: string;\n /** True for a source-only DEBUG build (`localisation: { mode: \"ids\", sourceDebug: true }`) - the strings\n * are the source language, embedded only so the build can be played; not a shippable localised build. */\n private readonly sourceDebug: boolean;\n /** Host-facing addresses (spec §6): scene gameId -> internal id (project-wide), and per-scene\n * block gameId -> internal id. The effective gameId falls back to the name slug when unpinned. */\n private readonly sceneGameIdToId = new Map<string, string>();\n private readonly blockGameIdToId = new Map<string, Map<string, string>>();\n\n /** The options this engine was built with - reused verbatim by `hotSwap` so the replacement\n * engine keeps the same world resolver, custom RNG, and diagnostic hooks. */\n private readonly creationOptions: EngineOptions;\n\n constructor(bundle: Bundle, options: EngineOptions = {}) {\n this.creationOptions = options;\n const locale = options.locale ?? bundle.locales.default;\n const allStrings = bundle.strings;\n this.allStrings = allStrings;\n this.currentLocale = locale;\n const strings = allStrings[locale] ?? {};\n const defaultStrings = allStrings[bundle.locales.default] ?? {};\n // Localisation mode (spec §11). \"ids\" + no source-debug = the engine emits beat IDs (the game localises\n // itself). A source-debug build still resolves its embedded source strings, but is flagged for a warning.\n const loc = bundle.localisation;\n const emitIds = loc?.mode === \"ids\" && !loc.sourceDebug;\n this.sourceDebug = loc?.mode === \"ids\" && !!loc.sourceDebug;\n if (this.sourceDebug && typeof console !== \"undefined\") {\n console.warn(\"[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.\");\n }\n // Cast name -> displayName: the unlocalised fallback for a character's shown name when neither the\n // active nor the default locale carries a `cast:<name>` string.\n const castDisplay = new Map<string, string>();\n for (const c of bundle.cast ?? []) if (c.displayName) castDisplay.set(c.name, c.displayName);\n this.defaultSeed = (options.seed ?? 0x9e3779b9) >>> 0;\n\n const nodeIndex = new Map<string, SelectableNode>();\n const blockIndex = new Map<string, { sceneId: string }>();\n const blockById = new Map<string, CompiledBlock>();\n for (const [sceneId, scene] of Object.entries(bundle.scenes)) {\n this.sceneGameIdToId.set(effectiveGameId(scene), sceneId);\n const blockAddrs = new Map<string, string>();\n for (const block of scene.blocks) {\n blockIndex.set(block.id, { sceneId });\n blockById.set(block.id, block);\n blockAddrs.set(effectiveGameId(block), block.id);\n walkNodes<SelectableNode>(block.children, (n) => nodeIndex.set(n.id, n));\n }\n this.blockGameIdToId.set(sceneId, blockAddrs);\n }\n\n // Globals (`@patter`) split by the `shared` flag (default shared): shared ones\n // live in the engine's owned scope, per-flow ones seed each flow's registry.\n const props = bundle.properties ?? [];\n const patterSharedDecls = props.filter((p) => p.shared ?? true).map(toDecl);\n const patterLocalDecls = props.filter((p) => !(p.shared ?? true)).map(toDecl);\n const patterSharedNames = new Set(patterSharedDecls.map((d) => d.name.toLowerCase()));\n\n const shared = new ScopeRegistry().defineOwned(\"patter\", patterSharedDecls);\n const hostBound = new Set<string>();\n // The host's World Properties resolver binds `@world`; its declarations (types, read-only) come from\n // the compiled bundle's declared world properties. An explicit binding always wins over the self-backed\n // fallback below.\n if (options.world) {\n const worldSpec = bundle.scopeRegistry?.scopes.find((s) => s.token === \"world\");\n const decls = (worldSpec?.declarations ?? []).map(toForeignDecl);\n shared.defineForeign(\"world\", options.world, decls, worldSpec?.writable ?? true);\n hostBound.add(\"world\");\n }\n // A project that DECLARES `@world` but whose embedder binds no resolver (the standalone case) gets a\n // self-backed one: a live in-memory bag seeded from the declarations' defaults. The story reads/writes\n // it like any scope; it stays *foreign* (not in Patter's save: the host owns it conceptually).\n for (const spec of bundle.scopeRegistry?.scopes ?? []) {\n if (hostBound.has(spec.token)) continue;\n const decls = (spec.declarations ?? []).map(toForeignDecl);\n shared.defineForeign(spec.token, selfBackedResolver(spec.declarations ?? []), decls, spec.writable ?? true);\n }\n\n // Scene props (`@scene`) split by `shared` (default per-flow): record, per\n // scene, which names are shared so a `@scene` ref routes to stage vs flow.\n const sceneSharedNames = new Map<string, Set<string>>();\n for (const [sceneId, scene] of Object.entries(bundle.scenes)) {\n const names = new Set((scene.sceneProps ?? []).filter((p) => p.shared ?? false).map((p) => p.name.toLowerCase()));\n sceneSharedNames.set(sceneId, names);\n }\n\n this.host = {\n bundle, emitIds, strings, defaultStrings, castDisplay, nodeIndex, blockIndex, blockById,\n sceneGameIdToId: this.sceneGameIdToId, blockGameIdToId: this.blockGameIdToId, // same instances the engine resolves with\n tagIndex: buildTagIndex(bundle), shared,\n patterSharedDecls, patterLocalDecls, patterSharedNames, sceneSharedNames,\n sharedVisits: new Map(),\n sharedSelectors: new Map(),\n stageBags: new Map(),\n customRng: options.rng,\n onDryChoice: options.onDryChoice,\n replayPromptOnChoose: options.replayPromptOnChoose ?? false,\n captionsOn: options.closedCaptions ?? true, // captions shown by default (full text)\n captionOpen: (bundle.closedCaptions ?? DEFAULT_CAPTION_DELIMITERS).open,\n captionClose: (bundle.closedCaptions ?? DEFAULT_CAPTION_DELIMITERS).close,\n captionCharacter: bundle.closedCaptions?.character || DEFAULT_CAPTION_CHARACTER, // absent/empty -> SFX\n refSplitCache: new Map(),\n };\n }\n\n /** The active locale (string + character-name lookups resolve in it). */\n get locale(): string { return this.currentLocale; }\n\n /** True for a source-only DEBUG build: the embedded strings are the source language (for debugging),\n * not a shippable localised build. An IDs-only ship build is `false`. */\n get isSourceDebug(): boolean { return this.sourceDebug; }\n\n /**\n * Switch the active locale LIVE - a real game's \"language\" setting can change mid-session. Subsequent\n * string lookups (new beats, re-resolved character names, `{@ref}` interpolation) render in the new\n * locale; everything else - flow position, `@patter`/`@scene` state, visit counts, the PRNG - is\n * untouched (already-emitted text isn't retro-translated; that's the host's call). A locale with no\n * table resolves every string via the `<Untranslated: {id}>` source fallback. All open flows share the\n * engine's string table, so the swap reaches every flow at once.\n */\n setLocale(locale: string): void {\n this.currentLocale = locale;\n this.host.strings = this.allStrings[locale] ?? {};\n }\n\n /**\n * Live bundle refresh, tier 1 (strings only): swap every locale's string table in place from a\n * freshly compiled bundle whose STRUCTURE is unchanged (same `content.structureHash`). Like\n * setLocale, nothing restarts and no flow is touched: the next delivered beat reads the new text,\n * `{@ref}` slots re-interpolate, and beats the host already received keep the words it saw. The\n * swap reaches every open flow at once and is not part of save state. Structural edits need the\n * full save/load hot swap instead (a structure change here simply won't show).\n */\n replaceStrings(bundle: Bundle): void {\n this.allStrings = bundle.strings;\n this.host.strings = this.allStrings[this.currentLocale] ?? {};\n this.host.defaultStrings = this.allStrings[this.host.bundle.locales.default] ?? {};\n }\n\n /**\n * Live bundle refresh, tier 2 (full swap): rebuild on an edited bundle with the whole run carried\n * over. Snapshot (`saveGame`), construct a fresh engine on `bundle` with THIS engine's original\n * options (same world resolver, RNG, hooks), restore (`loadGame`), and carry over the presentation\n * state that deliberately isn't save state (active locale, closed-captions toggle). The\n * content-drift policy (§9.8) resolves edits under the cursor: stack frames re-find their next\n * child by id, drifted options drop, a vanished snippet is skipped.\n *\n * Returns the REPLACEMENT engine; this one is left untouched and should be discarded. Hosts\n * re-bind their flow handles via `next.getFlow(id)`. If the restore throws (defensive - §9.8\n * makes this unreachable for ordinary edits), the swap falls back to a cold engine with each\n * saved flow restarted from the top of the scene it was in.\n */\n hotSwap(bundle: Bundle): Engine {\n const snapshot = this.saveGame();\n const carryOver = (next: Engine): Engine => {\n next.setLocale(this.currentLocale);\n next.setClosedCaptions(this.host.captionsOn);\n return next;\n };\n const next = new Engine(bundle, this.creationOptions);\n try {\n next.loadGame(snapshot);\n return carryOver(next);\n } catch {\n // A partial load may have mutated `next`: fall back on a THIRD, cold engine and restart each\n // flow at the top of the scene it was in (dropped when that scene is gone too).\n const fresh = new Engine(bundle, this.creationOptions);\n for (const [id, f] of Object.entries(snapshot.flows)) {\n const sceneId = f.cursor.currentSceneId;\n try { fresh.openFlow(id, sceneId !== null ? { scene: sceneId } : {}); } catch { /* scene deleted: drop the flow */ }\n }\n return carryOver(fresh);\n }\n }\n\n /** Whether closed captions are currently shown (full dialogue text). */\n get closedCaptions(): boolean { return this.host.captionsOn; }\n\n /**\n * Turn closed captions on/off LIVE (#214). When OFF, subsequent dialogue lines have their caption\n * cues (`[sigh]` etc., between the project's delimiters) and the surrounding whitespace stripped;\n * narration, choice prompts, and everything else are untouched. Like setLocale this is a presentation\n * toggle - it reaches every open flow at once and isn't part of save state; already-emitted text is\n * not retro-edited. An IDs-only game applies the same rule itself via `flow.stripCaptions`.\n */\n setClosedCaptions(on: boolean): void {\n this.host.captionsOn = on;\n }\n\n /**\n * Open (and start) a named flow. Each flow has its own cursor, PRNG, and per-flow\n * half of the scopes (not-shared `@patter`/`@scene`); all flows share the shared\n * half.\n *\n * Re-opening an existing id REPLACES it with a fresh flow, and CLOSES the old one\n * ({@link Flow.close}) so a host still holding it cannot keep driving the shared world. Replacing is\n * therefore a reset: that name's cursor, visit counts, selector cursors (so any shuffle / once-each\n * position) and per-flow properties all start over.\n *\n * Contrast {@link runFlow}, which REUSES a flow of the same name instead of replacing it - that is the\n * call to reach for when you want a speaker's variation state to carry on.\n */\n openFlow(id: string, opts: OpenFlowOptions = {}): Flow {\n const sceneId = this.resolveSceneRef(opts.scene);\n const blockId = this.resolveBlockRef(sceneId, opts.block);\n this.flowsById.get(id)?.close(); // finish the flow this name used to mean\n const flow = new Flow(id, this.host, opts.seed ?? this.defaultSeed);\n this.flowsById.set(id, flow);\n flow.start(sceneId, blockId);\n return flow;\n }\n\n /**\n * \"Play this address and give me everything it produced\" - the one-call form of the bark / one-shot\n * pattern. The NAMED flow is reused if it already exists (moved with {@link Flow.goto}) and opened at\n * the address if not, then run to its next stop, returning every beat it played.\n *\n * Calling it again with the SAME NAME does NOT replace the flow - it reuses it, and that is the whole\n * point. A flow owns its selector cursors, visit counts and per-flow properties, so reusing one lets a\n * **shuffle keep its bag** and an **\"once each\" list keep its place**: successive calls give the next\n * variation instead of replaying the first forever. (A fresh flow each time would reset all of it,\n * unless every such group happened to be authored `shared`.) Use one name per independent speaker;\n * different names never share per-flow state.\n *\n * This is exactly where it differs from {@link openFlow}, which REPLACES a flow of the same name and\n * so resets that variation state. Never mix the two on one name unless you mean to start over.\n *\n * Returns the played beats in order - `[]` means the address had nothing left to give (an exhausted\n * variation list, say), which is the signal to fall back to other content. It THROWS on an address\n * that does not resolve: unlike `goto` (a navigation primitive, where probing is legitimate), naming a\n * location here asserts it exists, and keeping `[]` unambiguous is worth more than a soft failure.\n *\n * A run that stops at a CHOICE returns the beats up to it and leaves the choice pending on the flow -\n * fetch it with `engine.getFlow(name)?.getChoices()`.\n */\n runFlow(flow: string, scene: string, block?: string): AdvanceToStopResult[\"played\"] {\n const existing = this.flowsById.get(flow);\n if (!existing) return this.openFlow(flow, { scene, block }).advanceToStop().played; // start() reports a bad address\n if (!existing.goto(scene, block)) {\n throw new Error(`runFlow: address not found: ${scene}${block === undefined ? \"\" : ` / ${block}`}`);\n }\n return existing.advanceToStop().played;\n }\n\n /** Resolve a scene reference (a gameId address OR an internal id) to its internal id. */\n private resolveSceneRef(ref?: string): string | undefined {\n if (ref == null) return undefined;\n if (this.host.bundle.scenes[ref]) return ref; // already an internal id\n return this.sceneGameIdToId.get(ref) ?? ref; // a gameId, else pass through (start reports)\n }\n\n /** Resolve a block reference (a scene-scoped gameId OR an internal id) to its internal id. */\n private resolveBlockRef(sceneId: string | undefined, ref?: string): string | undefined {\n if (ref == null) return undefined;\n if (this.host.blockById.has(ref)) return ref; // already an internal id\n if (sceneId != null) { const id = this.blockGameIdToId.get(sceneId)?.get(ref); if (id) return id; }\n return ref; // pass through (start reports an unknown block)\n }\n\n /** The host-facing address (gameId) of a scene / block by internal id, or undefined if unknown.\n * The inverse of the resolve helpers - for a host that wants to display / log the address. */\n sceneAddress(sceneId: string): string | undefined {\n const scene = this.host.bundle.scenes[sceneId];\n return scene ? effectiveGameId(scene) : undefined;\n }\n blockAddress(blockId: string): string | undefined {\n const block = this.host.blockById.get(blockId);\n return block ? effectiveGameId(block) : undefined;\n }\n\n /**\n * Author tags (#215) accumulated for a beat by id: its own tags unioned with every ancestor's\n * (scene → block → group(s) → snippet → beat), deduped, outermost-first. The same value the beat's\n * delivered step carries. Empty array for an unknown id or a beat with no tags anywhere up the chain.\n */\n tagsForBeat(beatId: string): string[] {\n return this.host.tagIndex.get(beatId) ?? [];\n }\n /** A scene's own tags (by internal id or gameId address). Empty when none / unknown. */\n tagsForScene(sceneRef: string): string[] {\n const id = this.resolveSceneRef(sceneRef);\n return (id != null ? this.host.tagIndex.get(id) : undefined) ?? [];\n }\n /** A block's accumulated tags (scene + block), by scene + block ref (id or gameId). Empty when none / unknown. */\n tagsForBlock(sceneRef: string, blockRef: string): string[] {\n const sceneId = this.resolveSceneRef(sceneRef);\n const id = this.resolveBlockRef(sceneId, blockRef);\n return (id != null ? this.host.tagIndex.get(id) : undefined) ?? [];\n }\n\n /**\n * Every cast member the PROJECT declares, in authored order - the same list `describeBundle` counts.\n * A superset of any scene's cast: the validator holds a beat's `character` to a declared member, so\n * {@link castForScene} and {@link castForBlock} only ever return names that appear here.\n */\n getCast(): string[] {\n // `cast` is absent from a bundle whose project declares none (the compiler omits the key), and a\n // nameless member is junk from a hand-edited bundle: both give an empty answer, not a throw.\n const names: string[] = [];\n for (const c of this.host.bundle.cast ?? []) if (c?.name) names.push(c.name);\n return names;\n }\n\n /**\n * A scene's cast: the `character` token of every speaker with a line anywhere in it, deduped, in\n * first-appearance order. Static, like {@link getOutline}: it walks the authored structure, so a\n * speaker behind a condition, inside any group, or voicing a choice prompt counts - this is who CAN\n * speak in the scene, not who a given playthrough heard. Empty for an unknown ref, or a scene with no\n * dialogue. Tokens, not display names: resolve those through the delivered step (`characterName`),\n * which is what follows `setLocale`.\n */\n castForScene(sceneRef: string): string[] {\n const id = this.resolveSceneRef(sceneRef);\n const scene = id != null ? this.host.bundle.scenes[id] : undefined;\n if (!scene) return [];\n const out = new Set<string>();\n for (const block of scene.blocks) collectCast(block.children, out);\n return [...out];\n }\n\n /** One block's cast, by scene + block ref (id or gameId). {@link castForScene} scoped to a block. */\n castForBlock(sceneRef: string, blockRef: string): string[] {\n const sceneId = this.resolveSceneRef(sceneRef);\n const id = this.resolveBlockRef(sceneId, blockRef);\n const block = id != null ? this.host.blockById.get(id) : undefined;\n if (!block) return [];\n const out = new Set<string>();\n collectCast(block.children, out);\n return [...out];\n }\n\n /**\n * The authored structure as a nested tree: scenes -> blocks -> children (groups + snippets, groups\n * preserved) -> a snippet's beats. Static (no flow / play state); per-beat data is read at the source\n * locale. For dev tooling that builds against the writer's structure (see also {@link getBeatSequence}).\n */\n getOutline(): OutlineScene[] {\n return Object.values(this.host.bundle.scenes).map((scene) => ({\n id: scene.id,\n ...(effectiveGameId(scene) ? { gameId: effectiveGameId(scene) } : {}),\n name: scene.name,\n ...this.tagsField(scene.id),\n blocks: scene.blocks.map((block) => ({\n id: block.id,\n ...(effectiveGameId(block) ? { gameId: effectiveGameId(block) } : {}),\n name: block.name,\n ...this.tagsField(block.id),\n children: block.children.map((n) => this.outlineNode(n)),\n })),\n }));\n }\n\n /**\n * Every beat in document order, flattened (through groups), each with the scene / block / snippet it\n * belongs to and its static data. The linear view of {@link getOutline} - hand it to a tool that lays\n * one item per beat (e.g. an Unreal Sequencer of subsequences).\n */\n getBeatSequence(): FlatBeat[] {\n const out: FlatBeat[] = [];\n for (const scene of Object.values(this.host.bundle.scenes)) {\n for (const block of scene.blocks) {\n walkNodes<SelectableNode>(block.children, (n) => {\n if (n.type !== \"snippet\") return;\n for (const beat of n.beats ?? []) {\n out.push({ sceneId: scene.id, blockId: block.id, snippetId: n.id, beat: this.beatInfo(beat) });\n }\n });\n }\n }\n return out;\n }\n\n /** A node's outline entry: a group (selector + prompt + children) or a snippet (beats + jump). */\n private outlineNode(n: SelectableNode): OutlineNode {\n if (n.type === \"group\") {\n return {\n type: \"group\",\n id: n.id,\n ...this.tagsField(n.id),\n ...(n.selector ? { selector: n.selector } : {}),\n ...(n.prompt ? { prompt: this.beatInfo(n.prompt) } : {}),\n children: n.children.map((c) => this.outlineNode(c)),\n };\n }\n return {\n type: \"snippet\",\n id: n.id,\n ...this.tagsField(n.id),\n beats: (n.beats ?? []).map((b) => this.beatInfo(b)),\n ...(n.jump ? { jumpTo: n.jump.to, ...(n.jump.mode ? { jumpMode: n.jump.mode } : {}) } : {}),\n };\n }\n\n /** One beat's static data (source locale), the same shape a delivered step carries. */\n private beatInfo(beat: Beat): BeatInfo {\n const tags = this.host.tagIndex.get(beat.id);\n const info: BeatInfo = { id: beat.id, kind: beat.kind };\n if (beat.kind === \"line\") {\n if (beat.character !== undefined) {\n info.character = beat.character;\n const name = this.host.defaultStrings[castStringKey(beat.character)] ?? this.host.castDisplay.get(beat.character);\n if (name !== undefined) info.characterName = name;\n }\n if (beat.direction !== undefined) info.direction = beat.direction;\n }\n if (beat.kind === \"line\" || beat.kind === \"text\") {\n const source = this.host.defaultStrings[beat.id]; // source-locale text, un-interpolated\n if (source !== undefined) info.text = source;\n }\n if (beat.gameData && Object.keys(beat.gameData).length) info.gameData = beat.gameData;\n if (tags && tags.length) info.tags = tags;\n return info;\n }\n\n /** A `{ tags }` fragment for an id, present only when the id has accumulated tags (keeps output tidy). */\n private tagsField(id: string): { tags?: string[] } {\n const tags = this.host.tagIndex.get(id);\n return tags && tags.length ? { tags } : {};\n }\n\n /** Retrieve an open flow by id (undefined if none / closed). */\n getFlow(id: string): Flow | undefined {\n return this.flowsById.get(id);\n }\n\n /** All currently-open flows. */\n flows(): Flow[] {\n return [...this.flowsById.values()];\n }\n\n /** Close (remove) a flow. The flow object is FINISHED, not merely unregistered, so a host still\n * holding it cannot keep advancing it into the shared world (see {@link Flow.close}). */\n closeFlow(id: string): void {\n this.flowsById.get(id)?.close();\n this.flowsById.delete(id);\n }\n\n /**\n * Reset the whole game to its initial state: drop every flow, re-seed the shared\n * `@patter` globals to their declared defaults, and clear all shared state (shared\n * `@scene` bags, world visit counts). World properties are host-owned and untouched.\n * After reset, open fresh flows with `openFlow`.\n */\n reset(): void {\n for (const flow of this.flowsById.values()) flow.close(); // finish them, don't just forget them\n this.flowsById.clear();\n this.host.shared.reseedOwned(\"patter\", this.host.patterSharedDecls);\n this.host.sharedVisits.clear();\n this.host.sharedSelectors.clear();\n this.host.stageBags.clear();\n }\n\n /** Read a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level). */\n getProperty(ref: string): ScalarValue | undefined {\n const { scope, name } = this.splitShared(ref);\n return this.host.shared.get(scope, name);\n }\n\n /** Write a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level). */\n setProperty(ref: string, value: ScalarValue): void {\n const { scope, name } = this.splitShared(ref);\n this.host.shared.set(scope, name, value);\n }\n\n /** The shared `@patter` properties, for a live state inspector: each with its ref, type, current\n * value, declared default (for reset), and enum options. Mirrors the Unity / Godot ports. */\n listProperties(): PropertyRow[] {\n return this.host.patterSharedDecls.map((d) => ({\n ref: `@${d.name}`,\n type: d.type as PropertyType,\n values: d.values,\n value: this.getProperty(`@${d.name}`),\n default: declDefault(d),\n }));\n }\n\n // @scene is scene-namespaced and needs a flow's current scene - silently\n // routing it into the shared bag (as a junk \"scene.x\" key) was a trap.\n private splitShared(ref: string): { scope: string; name: string } {\n let split = this.host.refSplitCache.get(ref);\n if (!split) { split = splitRef(ref, (t) => t === \"scene\" || this.host.shared.has(t)); this.host.refSplitCache.set(ref, split); }\n if (split.scope === \"scene\") {\n throw new Error(`'${ref}': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);\n }\n return split;\n }\n\n /** Snapshot shared `@patter` state only (for a unified cross-engine save blob, Phase D). */\n save(): EngineSave {\n return this.host.shared.save();\n }\n\n /** Restore shared `@patter` values (world properties untouched). */\n load(blob: EngineSave): void {\n this.host.shared.load(blob);\n }\n\n /** Snapshot the whole game: shared `@patter` + visit counts + every live flow. */\n saveGame(): SaveGame {\n const flows: Record<string, FlowSnapshot> = {};\n for (const [id, flow] of this.flowsById) flows[id] = flow.snapshot();\n return {\n version: 2,\n shared: this.host.shared.save(),\n sharedVisits: Object.fromEntries(this.host.sharedVisits),\n sharedSelectors: serialiseSelectors(this.host.sharedSelectors),\n stageBags: Object.fromEntries([...this.host.stageBags].map(([s, bag]) => [s, { ...bag }])),\n flows,\n };\n }\n\n /** Restore a `saveGame()`: shared globals + visit counts + shared scene bags + reconstruct every flow. */\n loadGame(save: SaveGame): void {\n if (save.version !== 2) throw new Error(`unsupported save version: ${save.version}`);\n this.host.shared.load(save.shared);\n this.host.sharedVisits.clear();\n for (const [id, n] of Object.entries(save.sharedVisits ?? {})) this.host.sharedVisits.set(id, n);\n this.host.sharedSelectors.clear();\n for (const [id, st] of deserialiseSelectors(save.sharedSelectors)) this.host.sharedSelectors.set(id, st);\n this.host.stageBags.clear();\n for (const [s, bag] of Object.entries(save.stageBags ?? {})) this.host.stageBags.set(s, { ...bag });\n this.flowsById.clear();\n for (const [id, snap] of Object.entries(save.flows)) {\n const flow = new Flow(id, this.host, this.defaultSeed);\n flow.restore(snap);\n this.flowsById.set(id, flow);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Flow - one playable flow (cursor + the per-flow half of @patter/@scene + PRNG)\n// ---------------------------------------------------------------------------\n\nexport class Flow {\n readonly id: string;\n private readonly host: FlowHost;\n private local: ScopeRegistry; // owns \"patter\" = the NOT-shared globals (this flow's copy)\n private rngState: number;\n\n // Execution cursor. The `stack` is the continuation stack: each frame is a\n // position within a block's children (the top frame is the active block run;\n // lower frames are pending call-returns). A snippet's beats deliver from\n // `activeSnippet`/`beatIndex`.\n private started = false;\n private flowEnded = false;\n /** Closed by the engine (see `close()`). Terminal, and distinct from `flowEnded`: an ENDED flow is\n * merely out of content and `goto` revives it; a CLOSED one is finished for good. */\n private closed = false;\n private currentSceneId: string | null = null;\n private stack: StackFrame[] = [];\n private activeSnippet: CompiledSnippet | null = null;\n private beatIndex = 0;\n private pendingChoice: ChoiceState | null = null;\n /** When `replayPromptOnChoose`, the chosen option's prompt beat to deliver before its content. */\n private pendingPromptBeat: LineBeat | TextBeat | null = null;\n /** The chosen option that owns `pendingPromptBeat`, so a save taken between choose() and the next\n * advance() can re-derive the prompt on load (the beat isn't otherwise reachable by id). */\n private pendingPromptOwnerId: string | null = null;\n private selectors = new Map<string, SelectorState>();\n /** Per-node entry counts for this flow (node id -> times entered). */\n private visitCounts = new Map<string, number>();\n\n // Per-flow halves of the two scopes. The NOT-shared `@patter` globals live in\n // `local` (owned scope \"patter\"); the NOT-shared `@scene` props live in\n // `sceneBags` (namespaced per scene; they PERSIST across re-entries, spec §7).\n // The SHARED halves live on the host (`host.shared` / `host.stageBags`). Each\n // resolver presents one merged scope, routing each property to its half by the\n // declared `shared` flag.\n private sceneBags = new Map<string, Record<string, ScalarValue>>();\n\n private readonly patterResolver: ScopeResolver = {\n get: (n) => (this.host.patterSharedNames.has(n) ? this.host.shared.get(\"patter\", n) : this.local.get(\"patter\", n)),\n set: (n, v) => {\n if (this.host.patterSharedNames.has(n)) this.host.shared.set(\"patter\", n, v);\n else this.local.set(\"patter\", n, v);\n },\n };\n\n private readonly sceneResolver: ScopeResolver = {\n get: (n) => {\n const s = this.currentSceneId;\n if (s === null) return undefined;\n const bag = this.host.sceneSharedNames.get(s)?.has(n) ? this.host.stageBags.get(s) : this.sceneBags.get(s);\n return bag?.[n];\n },\n set: (n, v) => {\n const s = this.currentSceneId;\n if (s === null) return;\n const bag = this.host.sceneSharedNames.get(s)?.has(n) ? this.host.stageBags.get(s) : this.sceneBags.get(s);\n if (bag) bag[n] = v;\n },\n };\n\n // The eval context is built ONCE: every constituent resolves live state at\n // call time (shared bags mutate in place per scoperegistry's contract;\n // patter/scene route through this flow's resolvers, which read the current\n // `local`/`sceneBags`/`currentSceneId`; the host callbacks read current flow\n // fields). Rebuilding it per evaluation was the engine's hottest allocation.\n private readonly evalCtx: EvalContext;\n\n constructor(id: string, host: FlowHost, seed: number) {\n this.id = id;\n this.host = host;\n this.rngState = seed >>> 0;\n this.local = this.freshLocal();\n\n const scopes = { ...host.shared.toEvalContext().scopes }; // shared @patter bag + foreign resolvers\n scopes[\"patter\"] = this.patterResolver; // override with the merged shared+per-flow view\n scopes[\"scene\"] = this.sceneResolver;\n this.evalCtx = {\n scopes,\n host: {\n nextRandom: this.rng,\n visits: (id: string) => this.visitCounts.get(id) ?? 0,\n patterVisits: (id: string) => this.host.sharedVisits.get(id) ?? 0,\n },\n };\n }\n\n // -- Host API -------------------------------------------------------------\n\n /** Begin this flow at a scene (and optionally a specific block within it). */\n start(sceneId?: string, blockId?: string): void {\n this.sceneBags.clear();\n this.local = this.freshLocal();\n this.selectors.clear();\n this.visitCounts.clear();\n this.stack = [];\n this.currentSceneId = null;\n this.flowEnded = false;\n this.activeSnippet = null;\n this.beatIndex = 0;\n this.pendingChoice = null;\n this.started = true;\n\n if (blockId) {\n const loc = this.host.blockIndex.get(blockId);\n if (!loc) throw new Error(`unknown block: ${blockId}`);\n this.enterSceneSetup(loc.sceneId);\n this.stack = [{ sceneId: loc.sceneId, containerId: blockId, index: 0 }];\n this.enter(blockId);\n } else {\n const id = sceneId ?? Object.keys(this.host.bundle.scenes)[0];\n const scene = id ? this.host.bundle.scenes[id] : undefined;\n if (!scene) throw new Error(id ? `unknown scene: ${id}` : \"no scenes in bundle\");\n this.enterSceneSetup(id!);\n const first = scene.blocks[0];\n if (first) { this.stack = [{ sceneId: id!, containerId: first.id, index: 0 }]; this.enter(first.id); }\n }\n this.settle();\n }\n\n /**\n * Forget everything in this flow and begin again - its per-flow state (not-shared\n * `@patter` globals + `@scene` props), cursor, callstack, selector cursors, and\n * visit counts. Shared state (shared `@patter` / `@scene`, world visit counts) is\n * untouched. A clearer-named alias of `start()`.\n */\n reset(sceneId?: string, blockId?: string): void {\n this.start(sceneId, blockId);\n }\n\n /**\n * Send this flow's cursor to an ADDRESS, exactly as an authored `go` jump would: the target scene's\n * `onEntry` effects run, entering counts as a visit, and the callstack is REPLACED - any pending\n * `call` returns are discarded, just as a goto inside a call does.\n *\n * `scene` and `block` are host-facing gameIds (spec §6) or internal ids; `block` is scene-scoped, so\n * it is looked up within `scene`. `\"END\"` ends the flow. To move within the current scene, pass the\n * current scene's address again (`flow.currentScene` -> `engine.sceneAddress`).\n *\n * This is HOST navigation, not authoring, and it takes effect IMMEDIATELY: any beats left in the\n * snippet being delivered are abandoned, and a pending choice is dropped. The format stops an AUTHOR\n * writing a divert into the middle of a snippet; a host teleport is out-of-band, like `reset()` or\n * `loadGame()`. A flow that never started starts here; one that already ended resumes here.\n *\n * Returns false - leaving the cursor exactly where it was - if the address does not resolve. Per-flow\n * state (properties, visit counts, selector cursors) is untouched either way: this MOVES, never resets.\n */\n goto(scene: string, block?: string): boolean {\n if (this.closed) return false; // closed is terminal: unlike \"ended\", a goto cannot revive it\n if (scene === \"END\") {\n this.started = true; this.pendingChoice = null; this.pendingPromptBeat = null; this.pendingPromptOwnerId = null;\n this.activeSnippet = null; this.beatIndex = 0;\n this.flowEnded = true; this.stack = [];\n return true;\n }\n // Resolve BOTH addresses before touching any state, so a bad one is a no-op rather than a half-move.\n const sceneId = this.host.sceneGameIdToId.get(scene) ?? (this.host.bundle.scenes[scene] ? scene : undefined);\n if (sceneId === undefined) return false;\n let blockId: string | undefined;\n if (block !== undefined) {\n blockId = this.host.blockGameIdToId.get(sceneId)?.get(block)\n ?? (this.host.blockIndex.get(block)?.sceneId === sceneId ? block : undefined);\n if (blockId === undefined) return false; // a block address is scene-scoped: unknown HERE is unknown\n }\n // Never started: start() does the same landing plus the one-time per-flow setup.\n if (!this.started) { this.start(sceneId, blockId); return true; }\n\n this.pendingChoice = null; this.pendingPromptBeat = null; this.pendingPromptOwnerId = null;\n this.activeSnippet = null; this.beatIndex = 0; // abandon the rest of the snippet being delivered\n this.flowEnded = false; // an ended flow resumes at the target\n this.enterTarget(blockId ?? sceneId, \"jump\"); // \"jump\" = replace the stack, exactly like an authored goto\n this.settle();\n return true;\n }\n\n /**\n * Finish this flow for good. Engine-managed: `engine.closeFlow(id)`, `engine.reset()`, and re-opening\n * a name with `engine.openFlow` all call it on the flow being dropped.\n *\n * A dropped flow used to stay fully live: unregistered and invisible to `engine.flows()`, but a host\n * still holding the object could keep advancing it, and every scene `onEntry`, shared property, world\n * visit count and shared selector cursor it touched still landed on the engine. Closing makes that\n * stale reference inert - `advance()` reports the end and `goto()` refuses - so a forgotten reference\n * cannot quietly mutate the world. Terminal: unlike ending, a close is never revived.\n */\n close(): void {\n this.closed = true;\n this.flowEnded = true;\n this.stack = [];\n this.activeSnippet = null;\n this.beatIndex = 0;\n this.pendingChoice = null;\n this.pendingPromptBeat = null;\n this.pendingPromptOwnerId = null;\n }\n\n /** True once the engine has closed this flow (closed, dropped by `reset()`, or replaced by name). */\n get isClosed(): boolean { return this.closed; }\n\n /** The scene the cursor is currently in - set on entry and whenever a jump crosses scenes. Read\n * right after `advance()` to know which scene the just-played beat lives in (tooling that mirrors\n * the playhead, e.g. an editor following a cross-scene jump). `null` before the flow has started. */\n get currentScene(): string | null { return this.currentSceneId; }\n\n /** Run until the next line, game event, choice, or the end of the flow. */\n advance(): StepResult {\n if (this.closed) return { type: \"end\" }; // a stale reference to a closed flow drives nothing\n if (!this.started) throw new Error(\"flow has not been started\");\n // A replayed prompt (replayPromptOnChoose) is delivered first, before the option's content.\n if (this.pendingPromptBeat) { const b = this.pendingPromptBeat; this.pendingPromptBeat = null; this.pendingPromptOwnerId = null; return this.beatResult(b); }\n this.settle();\n if (this.flowEnded) return { type: \"end\" };\n if (this.pendingChoice) return { type: \"choice\", groupId: this.pendingChoice.groupId, options: this.pendingChoice.options };\n if (!this.activeSnippet) { this.flowEnded = true; return { type: \"end\" }; }\n return this.beatResult(this.activeSnippet.beats![this.beatIndex++]!);\n }\n\n /**\n * Advance repeatedly, collecting every played beat, until a choice or the end - the \"play to the\n * next stop\" a host's play UI / tooling wants. The terminal `choice` / `end` is returned as `stop`;\n * `played` holds the line / text / game-event results walked on the way to it. Termination is guaranteed\n * (each `advance()` makes progress or `settle()` throws on a contentless jump cycle).\n */\n advanceToStop(): AdvanceToStopResult {\n const played: AdvanceToStopResult[\"played\"] = [];\n for (;;) {\n const r = this.advance();\n if (r.type === \"choice\" || r.type === \"end\") return { played, stop: r };\n played.push(r); // narrowed to line / text / game-event by the guard above\n }\n }\n\n /**\n * Drive the cursor to the next *deliverable* stop: a beat ready on the active\n * snippet, a pending choice, or the end. Runs onExit/jump seams and walks the\n * block run (sequentially, skipping ineligible children); a finished block pops\n * to its caller (call-return) or ends the flow.\n */\n private settle(): void {\n let transitions = 0;\n for (;;) {\n // Static validation cannot rule out jump cycles (conditions gate them),\n // so a content bug like two pure jumps jumping at each other must be an\n // error, not a hang.\n if (++transitions > 10_000) {\n throw new Error(\"flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content\");\n }\n if (this.flowEnded || this.pendingChoice) return;\n\n if (this.activeSnippet) {\n if (this.beatIndex < (this.activeSnippet.beats?.length ?? 0)) return; // a beat is ready\n this.runEffects(this.activeSnippet.onExit);\n const jump = this.activeSnippet.jump;\n this.activeSnippet = null;\n this.beatIndex = 0;\n this.resolveJump(jump);\n continue;\n }\n\n const frame = this.stack[this.stack.length - 1];\n if (!frame) { this.flowEnded = true; return; }\n if (frame.sceneId !== this.currentSceneId) this.currentSceneId = frame.sceneId; // resumed scene (no reseed)\n const children = this.childrenOf(frame.containerId);\n if (!children) { this.stack.pop(); continue; } // drifted container -> skip the frame\n while (frame.index < children.length && !this.eligible(children[frame.index]!)) frame.index++;\n if (frame.index >= children.length) { this.stack.pop(); continue; } // run exhausted -> resume caller\n this.enterChild(children[frame.index++]!); // advance past it: that's the gather/return point\n }\n }\n\n /** The options of a pending choice (empty when not at a choice point). */\n getChoices(): ChoiceOption[] {\n return this.pendingChoice?.options ?? [];\n }\n\n /** Pick an eligible option by id; the next `advance()` runs it. */\n choose(id: string): void {\n const choice = this.pendingChoice;\n if (!choice) throw new Error(\"no choice is pending\");\n const option = choice.options.find((o) => o.id === id);\n if (!option) throw new Error(`unknown choice option: ${id}`);\n if (!option.eligible) throw new Error(`choice option is not eligible: ${id}`);\n const node = choice.byId.get(id)!;\n this.pendingChoice = null;\n // Optionally speak the chosen option's prompt back as its first beat (spec §5).\n this.pendingPromptBeat = this.host.replayPromptOnChoose ? this.promptBeatOf(node) ?? null : null;\n this.pendingPromptOwnerId = this.pendingPromptBeat ? node.id : null;\n // The block frame is already advanced past the choice group (the gather point),\n // so when the chosen option finishes without a jump, the flow continues there.\n this.enterChild(node);\n }\n\n isEnded(): boolean {\n return this.flowEnded;\n }\n\n /** Read a property by ref - `@patter` / `@scene` (each routed by its `shared` flag) or foreign. */\n getProperty(ref: string): ScalarValue | undefined {\n const { scope, name } = this.splitRef(ref);\n if (scope === \"patter\") return this.patterResolver.get(name);\n if (scope === \"scene\") return this.sceneResolver.get(name);\n return this.host.shared.get(scope, name); // foreign\n }\n\n /** Write a property by ref (routed by scope, then by the property's `shared` flag). */\n setProperty(ref: string, value: ScalarValue): void {\n const { scope, name } = this.splitRef(ref);\n if (scope === \"patter\") {\n this.patterResolver.set!(name, value);\n } else if (scope === \"scene\") {\n // The resolver stays graceful for expression evaluation, but a host write\n // with nowhere to land must error, not silently vanish.\n if (this.currentSceneId === null) throw new Error(`'${ref}': the flow has not entered a scene yet`);\n this.sceneResolver.set!(name, value);\n } else {\n this.host.shared.set(scope, name, value); // foreign\n }\n }\n\n // -- Save / restore (engine-driven) --------------------------------------\n\n /** @internal Snapshot this flow's cursor + per-flow scopes (not-shared `@patter`/`@scene`) + PRNG. */\n snapshot(): FlowSnapshot {\n return {\n scopes: this.local.save(), // owned scope \"patter\" = the NOT-shared globals (@scene saved separately)\n sceneBags: Object.fromEntries([...this.sceneBags].map(([s, bag]) => [s, { ...bag }])),\n rngState: this.rngState,\n visits: Object.fromEntries(this.visitCounts),\n cursor: {\n flowEnded: this.flowEnded,\n currentSceneId: this.currentSceneId,\n // Stamp each frame with the id of the child it would run next (nextId), so a restore against\n // an EDITED bundle re-finds the position by id instead of trusting the raw index (§9.8 /\n // live bundle refresh). A frame saved at its container's end has no next child - no stamp.\n stack: this.stack.map((f) => {\n const next = this.childrenOf(f.containerId)?.[f.index];\n return next ? { ...f, nextId: next.id } : { ...f };\n }),\n activeSnippetId: this.activeSnippet?.id ?? null,\n beatIndex: this.beatIndex,\n pendingChoice: this.pendingChoice\n ? { groupId: this.pendingChoice.groupId, options: this.pendingChoice.options.map((o) => ({ ...o })) }\n : null,\n pendingPromptOwnerId: this.pendingPromptOwnerId,\n selectors: serialiseSelectors(this.selectors),\n },\n };\n }\n\n /** @internal Restore this flow from a snapshot. */\n restore(snap: FlowSnapshot): void {\n this.rngState = snap.rngState >>> 0;\n this.visitCounts = new Map(Object.entries(snap.visits ?? {}));\n const c = snap.cursor;\n this.started = true;\n this.flowEnded = c.flowEnded;\n this.beatIndex = c.beatIndex;\n this.currentSceneId = c.currentSceneId;\n // Re-bind each frame to the CURRENT bundle: prefer the saved next-child id (survives siblings\n // inserted / removed / reordered before the cursor); fall back to the raw index when the id is\n // absent (an older save) or its node drifted out of the bundle (§9.8 best-effort).\n this.stack = c.stack.map((f) => {\n const { nextId, ...frame } = f;\n if (nextId !== undefined) {\n const at = this.childrenOf(frame.containerId)?.findIndex((ch) => ch.id === nextId) ?? -1;\n if (at >= 0) return { ...frame, index: at };\n }\n return { ...frame };\n });\n\n // Restore the per-flow @scene bags, then the per-flow @patter globals. @scene\n // resolves through `sceneResolver` over these bags, so nothing else to reseed.\n this.sceneBags = new Map(Object.entries(snap.sceneBags ?? {}).map(([s, bag]) => [s, { ...bag }]));\n this.local = this.freshLocal();\n this.local.load(snap.scopes); // loads the owned not-shared globals; shared halves live on the host\n\n // Content-drift policy (§9.8): if a saved position points at content deleted\n // since the save, resume best-effort rather than throwing - the missing\n // snippet / choice is dropped and play continues from the surviving stack.\n this.activeSnippet = null;\n if (c.activeSnippetId !== null) {\n const node = this.host.nodeIndex.get(c.activeSnippetId);\n if (node && node.type === \"snippet\") this.activeSnippet = node;\n }\n\n this.selectors = deserialiseSelectors(c.selectors); // this flow's (non-shared) selector cursors\n\n // Replay the saved option set VERBATIM (schema 9.3) - re-deriving would\n // re-evaluate conditions (double-consuming PRNG draws) and could change the\n // choice under the player. Options whose nodes drifted out of the bundle\n // are dropped; a choice with no surviving options dissolves (9.8).\n this.pendingChoice = null;\n if (c.pendingChoice !== null) {\n const byId = new Map<string, SelectableNode>();\n const options: ChoiceOption[] = [];\n for (const o of c.pendingChoice.options) {\n const node = this.host.nodeIndex.get(o.id);\n if (!node) continue;\n byId.set(o.id, node);\n options.push({ ...o });\n }\n if (options.length > 0) this.pendingChoice = { groupId: c.pendingChoice.groupId, options, byId };\n }\n\n // A save taken between choose() and the next advance() left a prompt still to be replayed\n // (replayPromptOnChoose). Re-derive it from the chosen option - dropped if that option drifted out\n // of the bundle (§9.8), exactly as the live choose() would have produced nothing.\n this.pendingPromptBeat = null;\n this.pendingPromptOwnerId = c.pendingPromptOwnerId ?? null;\n if (this.pendingPromptOwnerId) {\n const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);\n this.pendingPromptBeat = owner ? this.promptBeatOf(owner) ?? null : null;\n if (!this.pendingPromptBeat) this.pendingPromptOwnerId = null;\n }\n }\n\n // -- Scene / block / node entry ------------------------------------------\n\n /** Set the current scene, reset its scene-local props, run onEntry. */\n private enterSceneSetup(sceneId: string): void {\n const scene = this.host.bundle.scenes[sceneId];\n if (!scene) throw new Error(`unknown scene: ${sceneId}`);\n this.currentSceneId = sceneId;\n this.enter(sceneId);\n this.seedScene(scene); // seeds @scene defaults (per-flow on first entry; shared once globally)\n this.runEffects(scene.onEntry); // on-entry effects still fire every entry (spec §4)\n }\n\n /**\n * Play one child of the active run. A snippet begins delivering. A group is\n * walked by its selector: the default `run` pushes a nested run (its children\n * play in order, gathering back); `choice` stops for the host; a select-one\n * selector (branch, or a `sequence` in any order x exhaust mode) picks ONE child (recursing\n * to a leaf) - selecting nothing contributes no content and the run continues.\n */\n private enterChild(node: SelectableNode): void {\n this.enter(node.id);\n if (node.type === \"snippet\") { this.beginSnippet(node); return; }\n const selector = node.selector ?? \"run\";\n if (selector === \"run\") {\n this.stack.push({ sceneId: this.currentSceneId!, containerId: node.id, index: 0 });\n return;\n }\n if (selector === \"choice\") { this.setupChoice(node); return; }\n const pick = this.selectChild(node);\n if (pick) this.enterChild(pick);\n }\n\n /** A container's children, whether it's a block or a run-group; undefined if the id is gone. */\n private childrenOf(containerId: string): SelectableNode[] | undefined {\n const block = this.host.blockById.get(containerId);\n if (block) return block.children;\n const node = this.host.nodeIndex.get(containerId);\n if (node && node.type === \"group\") return node.children;\n return undefined; // content drift: the container was deleted since the save\n }\n\n private beginSnippet(snippet: CompiledSnippet): void {\n this.runEffects(snippet.onEnter);\n this.activeSnippet = snippet;\n this.beatIndex = 0;\n }\n\n private setupChoice(group: CompiledGroup): void {\n const options: ChoiceOption[] = [];\n const byId = new Map<string, SelectableNode>();\n const fallbacks: SelectableNode[] = [];\n for (const child of group.children) {\n // An option is an Option group (its content runs + gathers back) or - the\n // degenerate shape - a single snippet. prompt / sticky / fallback / secretUntilEligible\n // live on whichever (spec §5).\n if (child.fallback === true) { fallbacks.push(child); continue; } // never a normal option; auto-followed when last\n // Once-only (default): once the player has followed it, it is GONE from the choice entirely -\n // not delivered, not flagged unavailable, simply absent. A `sticky` option is never consumed,\n // so it stays available as long as its condition passes. Consumption is the existing per-flow\n // visit count, so it persists through save/restore for free.\n if (child.sticky !== true && (this.visitCounts.get(child.id) ?? 0) >= 1) continue;\n const eligible = this.eligible(child);\n const hidden = child.secretUntilEligible === true;\n if (!eligible && hidden) continue; // secret while ineligible; otherwise an ineligible option shows greyed\n options.push({ id: child.id, prompt: this.promptFor(child), eligible, gameData: child.gameData });\n byId.set(child.id, child);\n }\n if (options.length > 0) { this.pendingChoice = { groupId: group.id, options, byId }; return; }\n // No normal option survives. Auto-follow the fallback if it is eligible (its own condition still\n // applies); otherwise the choice GATHERS - it contributes nothing and the run continues past it\n // (a dry choice falls through rather than deadlocking; the validator warns about choices that can\n // run dry with no fallback).\n const fallback = fallbacks.find((f) => this.eligible(f));\n if (fallback) { this.enterChild(fallback); return; }\n // Nothing takeable and no eligible fallback: the choice runs dry and the flow walks past it. The\n // behaviour is unchanged; the opt-in diagnostics hook makes this silent fall-through observable.\n this.host.onDryChoice?.(group.id);\n }\n\n // -- Jumps (jump / call-return) ----------------------------------------\n\n private resolveJump(jump: Jump | undefined): void {\n // No jump: gather - the snippet falls through and the block run continues\n // (settle's frame walk picks the next child, or pops to a caller).\n if (!jump) return;\n this.enterTarget(jump.to, jump.mode === \"call\" ? \"call\" : \"jump\");\n }\n\n /**\n * Route to a target (scene / block / `END`). `call` PUSHES a return frame (the\n * caller's block run, already advanced to its next child, stays below); `jump`\n * is absolute - it REPLACES the whole stack, discarding pending returns. `END`\n * hard-ends the flow regardless of the callstack.\n */\n private enterTarget(to: string, mode: \"call\" | \"jump\"): void {\n if (to === \"END\") { this.flowEnded = true; this.stack = []; return; }\n\n let sceneId: string;\n let containerId: string;\n const scene = this.host.bundle.scenes[to];\n if (scene) {\n this.enterSceneSetup(to);\n const first = scene.blocks[0];\n if (!first) { if (mode === \"jump\") this.stack = []; return; } // empty scene\n sceneId = to; containerId = first.id;\n } else {\n const loc = this.host.blockIndex.get(to);\n if (!loc) throw new Error(`jump target not found: ${to}`);\n if (loc.sceneId !== this.currentSceneId) this.enterSceneSetup(loc.sceneId);\n sceneId = loc.sceneId; containerId = to;\n }\n\n this.enter(containerId); // count the entered block\n const frame: StackFrame = { sceneId, containerId, index: 0 };\n if (mode === \"call\") this.stack.push(frame);\n else this.stack = [frame];\n }\n\n // -- Selectors ------------------------------------------------------------\n\n private selectChild(group: CompiledGroup): SelectableNode | null {\n const eligible = group.children.filter((c) => this.eligible(c));\n if (eligible.length === 0) return null;\n const st = this.selectorState(group);\n\n switch (group.selector) {\n case \"branch\":\n return eligible[0]!;\n\n case \"sequence\": {\n const order = group.options?.order ?? \"sequential\";\n const exhaust = group.options?.exhaust ?? \"once\";\n return order === \"shuffle\" ? this.pickShuffle(eligible, exhaust, st)\n : order === \"specificity\" ? this.pickSpecificity(eligible, exhaust, st)\n : this.pickSequential(eligible, exhaust, st);\n }\n\n case \"run\":\n case \"choice\":\n default:\n return null; // run / choice / default are handled in enterChild, not here\n }\n }\n\n /** `sequence` with `order: \"sequential\"` - walk children in authored order. */\n private pickSequential(eligible: SelectableNode[], exhaust: string, st: SelectorState): SelectableNode | null {\n const len = eligible.length;\n const n = st.seq ?? 0;\n st.seq = n + 1;\n if (exhaust === \"repeat\") return eligible[n % len]!; // cycle\n if (n < len) return eligible[n]!; // still in the first pass\n if (exhaust === \"stick\") return eligible[len - 1]!; // hold the last forever (stopping)\n return null; // once: nothing after the pass\n }\n\n /**\n * `sequence` with `order: \"shuffle\"` - draw WITHOUT replacement (a bag), never\n * repeating the immediately-previous pick across a reshuffle (no line twice in a\n * row when >=2 are eligible). `stick` holds out the last authored child as the\n * permanent terminal; `once` stops after one pass; `repeat` reshuffles.\n */\n private pickShuffle(eligible: SelectableNode[], exhaust: string, st: SelectorState): SelectableNode | null {\n const len = eligible.length;\n const stick = exhaust === \"stick\";\n const fill = (): string[] => (stick ? eligible.slice(0, len - 1) : eligible).map((c) => c.id);\n\n if (st.bag === undefined) st.bag = fill();\n if (st.bag.length === 0) { // a full pass just completed\n if (exhaust === \"once\") return null;\n if (stick) { const last = eligible[len - 1]!; st.last = last.id; return last; }\n st.bag = fill(); // repeat: reshuffle\n }\n\n // Draw without replacement, never repeating the immediately-previous pick. Done allocation-free:\n // rather than materialise a filtered pool, find last's slot `p` and draw into the reduced span,\n // skipping that slot - identical distribution to filtering it out, then erase the pick in place.\n const pool = st.bag;\n const p = st.last !== undefined && pool.length > 1 ? pool.indexOf(st.last) : -1;\n let i = Math.floor(this.rng() * (p >= 0 ? pool.length - 1 : pool.length));\n if (p >= 0 && i >= p) i++;\n const id = pool[i]!;\n pool.splice(i, 1);\n st.last = id;\n return eligible.find((c) => c.id === id)!;\n }\n\n /**\n * `sequence` with `order: \"specificity\"` - **Best match**: score every eligible child by how\n * specifically its condition fits the CURRENT state (`matchedSpec`), keep the top-scoring tier,\n * and break ties with the seeded shuffle (no immediate repeat). A child with no condition scores\n * 0, so it is the filler that wins only when nothing more specific is eligible.\n *\n * `exhaust` composes as it does for the other orders: `repeat` re-scores the full eligible set\n * every draw (re-pickable - the character keeps preferring the on-topic line); `once` uses each\n * pick up (a bag of remaining ids), so as specific lines are consumed the group slides down to\n * less-specific ones and finally the filler, then yields null; `stick` degrades like `once` but\n * holds the final pick forever instead of drying up.\n */\n private pickSpecificity(eligible: SelectableNode[], exhaust: string, st: SelectorState): SelectableNode | null {\n let pool = eligible;\n if (exhaust !== \"repeat\") { // once / stick: draw without replacement\n if (st.bag === undefined) st.bag = eligible.map((c) => c.id);\n const remaining = new Set(st.bag);\n pool = eligible.filter((c) => remaining.has(c.id));\n if (pool.length === 0) { // every child used up\n return exhaust === \"stick\" && st.last !== undefined\n ? eligible.find((c) => c.id === st.last) ?? null // hold the last pick if still eligible\n : null;\n }\n }\n\n // Top specificity tier among the drawable pool.\n let best = -1;\n const scored = pool.map((c) => { const s = this.specScore(c); if (s > best) best = s; return { c, s }; });\n const tier = scored.filter((x) => x.s === best).map((x) => x.c);\n\n // Tie-break by the seeded PRNG, never repeating the immediately-previous pick (matches shuffle).\n // A lone top-tier child is returned WITHOUT drawing, so a clear winner consumes no randomness.\n let pick: SelectableNode;\n if (tier.length === 1) {\n pick = tier[0]!;\n } else {\n const p = st.last !== undefined ? tier.findIndex((c) => c.id === st.last) : -1;\n let i = Math.floor(this.rng() * (p >= 0 ? tier.length - 1 : tier.length));\n if (p >= 0 && i >= p) i++;\n pick = tier[i]!;\n }\n\n if (exhaust !== \"repeat\") st.bag = st.bag!.filter((id) => id !== pick.id);\n st.last = pick.id;\n return pick;\n }\n\n /** A child's Best-match score against the current state: 0 when it has no condition (the filler\n * tier), else the specificity of its (already-passing) condition. */\n private specScore(node: SelectableNode): number {\n return node.condition ? this.matchedSpec(this.conditionAst(node.condition), true) : 0;\n }\n\n /**\n * The **matched-specificity** metric (parity contract): how many atomic constraints are actively\n * holding this condition TRUE against the live state. Evaluation-aware, not a static clause count -\n * it walks the tree with a De-Morgan polarity flag so `or` and `not` score the branch that is\n * actually carrying the truth. `want` = \"does this subtree need to be true for the whole condition\n * to hold?\" (true at the root). Only `and`/`or`/`not`/`check_flags` are structural; every other\n * node (comparisons, scoped vars, literals, other calls) is an atom, evaluated whole.\n */\n private matchedSpec(node: ExprNode, want: boolean): number {\n // Delegates to the shared @wildwinter/expr-specificity scorer (same walk,\n // shared with Storylet Studio). We supply Patter's truthiness rule and keep\n // check_flags counting via the package's default counting call.\n const evalTruthy: EvalTruthy = (n) => truthy(evaluate(n, this.evalCtx, patterDialect));\n return scoreSpecificity(node, evalTruthy, { want });\n }\n\n /** A selector's cursor state - shared across flows (`group.shared`) or this flow's own. */\n private selectorState(group: CompiledGroup): SelectorState {\n const map = group.shared ? this.host.sharedSelectors : this.selectors;\n let st = map.get(group.id);\n if (!st) { st = {}; map.set(group.id, st); }\n return st;\n }\n\n // -- Effects + expressions ------------------------------------------------\n\n private runEffects(effects: CompiledEffect[] | undefined): void {\n // SET-ONLY (spec §15): an effect mutates a property. Host events ride on gameData, not effects.\n for (const e of effects ?? []) {\n this.setProperty(e.target, this.evalExpr(e.value));\n }\n }\n\n private eligible(node: SelectableNode): boolean {\n if (!node.condition) return true;\n return truthy(this.evalExpr(node.condition));\n }\n\n private evalExpr(expr: Expression): ScalarValue {\n return evaluate(this.conditionAst(expr), this.evalCtx, patterDialect);\n }\n\n /** The deserialised (in-memory) AST for an expression, cached per Expression. Shared by the\n * evaluator and the Best-match specificity walker so both work off one parse. */\n private conditionAst(expr: Expression): ExprNode {\n let ast = astCache.get(expr);\n if (!ast) { ast = deserialiseAst(expr.ast); astCache.set(expr, ast); }\n return ast;\n }\n\n /** Record an entry of a node (entered-only; spec §7): bumps the flow + world counts. */\n private enter(id: string): void {\n this.visitCounts.set(id, (this.visitCounts.get(id) ?? 0) + 1);\n this.host.sharedVisits.set(id, (this.host.sharedVisits.get(id) ?? 0) + 1);\n }\n\n /** Next float in [0, 1): the shared custom PRNG, or this flow's serialisable mulberry32. */\n private readonly rng = (): number => {\n if (this.host.customRng) return this.host.customRng();\n const a = (this.rngState + 0x6d2b79f5) | 0;\n this.rngState = a;\n let t = Math.imul(a ^ (a >>> 15), 1 | a);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n\n // -- Strings / beats ------------------------------------------------------\n\n private beatResult(beat: Beat): StepResult {\n // Accumulated author tags (#215): the beat's own tags unioned with every\n // ancestor's. Omitted from the step when empty (parity with `gameData`).\n const tags = this.host.tagIndex.get(beat.id);\n const withTags = tags && tags.length ? { tags } : {};\n // Inline `{@ref}` interpolation (spec §16): text beats always interpolate;\n // line beats interpolate only in a non-voiced project (voiced lines are\n // static). Game-event beats carry no localised content.\n switch (beat.kind) {\n case \"gameEvent\":\n return { type: \"gameEvent\", id: beat.id, gameData: beat.gameData, ...withTags };\n case \"text\":\n return { type: \"text\", id: beat.id, text: this.interpolate(this.resolveString(beat.id)), gameData: beat.gameData, ...withTags };\n case \"line\": {\n const raw = this.resolveString(beat.id);\n // Closed captions (#214) apply to DIALOGUE lines only: strip cues when captions are off. Two ways a\n // line goes SILENT (off only): the caption CHARACTER speaks it (whole line is a caption - omit all\n // dialogue, delimiters or not), or stripping cues leaves it empty. A silent line still FIRES (audio\n // plays + visits count) but carries no text + no speaker, so no caption shows.\n const off = !this.host.captionsOn;\n const captionChar = off && beat.character === this.host.captionCharacter; // captionCharacter is always set (defaults SFX)\n const text = captionChar ? \"\" : this.captionLine(this.host.bundle.voiced ? raw : this.interpolate(raw));\n const silent = off && text.length === 0;\n return {\n type: \"line\",\n id: beat.id,\n text,\n character: silent ? undefined : beat.character,\n characterName: silent ? undefined : this.resolveCharacterName(beat.character),\n direction: silent ? undefined : beat.direction,\n gameData: beat.gameData,\n ...withTags,\n };\n }\n }\n }\n\n /**\n * Expand inline `{@ref}` slots (spec §16) against this flow's CURRENT property state. Public so an\n * IDs-only game can apply the same property replacement to a string it looked up in its own loc system:\n * the engine handed it the beat ID, the game fetched its translation, then calls `flow.interpolate(...)`.\n */\n interpolate(raw: string): string {\n return interpolate(raw, (ref) => this.getProperty(ref));\n }\n\n /**\n * Apply the project's caption rule to a string UNCONDITIONALLY (#214): remove every cue span between\n * the project's delimiters and collapse the whitespace. Public so an IDs-only game - which looks up\n * its own strings - can match the embedded runtime: `flow.stripCaptions(flow.interpolate(text))` when\n * its own captions setting is off. (Embedded play does this automatically for dialogue lines.)\n */\n stripCaptions(raw: string): string {\n return stripCaptions(raw, this.host.captionOpen, this.host.captionClose);\n }\n\n /** Caption-strip a dialogue line ONLY when captions are off; otherwise pass the text through. The\n * internal gate the engine applies to every `line` beat / line-kind prompt. */\n private captionLine(text: string): string {\n return this.host.captionsOn ? text : this.stripCaptions(text);\n }\n\n /**\n * An option's prompt (spec §5): the Option group's `prompt` beat, resolved + interpolated\n * (choice labels are on-screen text, so they interpolate, spec §16). For the degenerate\n * bare-snippet tolerance - or an Option group authored without a prompt - it falls back to the\n * option's first content line. NO look-ahead. Undefined only when even that is absent.\n */\n private promptFor(node: SelectableNode): ChoicePrompt | undefined {\n const beat = this.promptBeatOf(node);\n if (!beat) return undefined;\n const text = this.interpolate(this.resolveString(beat.id));\n // A line-kind prompt is dialogue, so captions apply to it; a text-kind prompt is left as-is.\n return beat.kind === \"line\"\n ? { kind: \"line\", text: this.captionLine(text), character: beat.character, characterName: this.resolveCharacterName(beat.character), direction: beat.direction }\n : { kind: \"text\", text };\n }\n\n /** The prompt BEAT of an option: the Option group's `prompt`, else (tolerance) its first content line. */\n private promptBeatOf(node: SelectableNode): LineBeat | TextBeat | undefined {\n if (node.type === \"group\" && node.prompt) return node.prompt;\n const snippet = node.type === \"snippet\" ? node : this.firstTextSnippetIn(node.children);\n return (snippet?.beats ?? []).find((b): b is LineBeat | TextBeat => b.kind === \"line\" || b.kind === \"text\");\n }\n\n /** The first snippet with a line/text beat within a child list, depth-first in authored order. */\n private firstTextSnippetIn(children: SelectableNode[]): CompiledSnippet | undefined {\n let found: CompiledSnippet | undefined;\n walkNodes<SelectableNode>(children, (n) => {\n if (!found && n.type === \"snippet\" && (n.beats ?? []).some((b) => b.kind === \"line\" || b.kind === \"text\")) {\n found = n;\n }\n });\n return found;\n }\n\n private resolveString(id: string): string {\n if (this.host.emitIds) return id; // IDs-only build: the game resolves text from this id itself\n const active = this.host.strings[id];\n if (active !== undefined) return active;\n // A key the active locale is missing falls back to the default-locale (source) text, but is flagged\n // LOUDLY: an untranslated string is a hard fail authors must notice, not silently paper over. Only a\n // key absent from the default locale too (never extracted) degrades to its bare id.\n const source = this.host.defaultStrings[id];\n return source !== undefined ? `<Untranslated: ${id}> ${source}` : id;\n }\n\n /** A character's player-facing name: the `cast:<name>` string in the active locale, else the default\n * locale, else the authoring `displayName`. Undefined when the character has no display name at all\n * (the host falls back to the `character` token itself). */\n private resolveCharacterName(character: string | undefined): string | undefined {\n if (character === undefined) return undefined;\n if (this.host.emitIds) return undefined; // IDs-only: omit the display name; the game maps the `character` token\n const key = castStringKey(character);\n return this.host.strings[key] ?? this.host.defaultStrings[key] ?? this.host.castDisplay.get(character);\n }\n\n /** Split a ref into scope + name. Tokens: `@scene`, foreign tokens, else `@patter` (incl. bare `@name`). */\n private splitRef(ref: string): { scope: string; name: string } {\n // host.shared.has(\"patter\") is true, so it covers @patter + every foreign token; @scene is explicit.\n let hit = this.host.refSplitCache.get(ref);\n if (!hit) { hit = splitRef(ref, (t) => t === \"scene\" || this.host.shared.has(t)); this.host.refSplitCache.set(ref, hit); }\n return hit;\n }\n\n /** The per-flow registry: the NOT-shared `@patter` globals (the shared ones live on the host). */\n private freshLocal(): ScopeRegistry {\n return new ScopeRegistry().defineOwned(\"patter\", this.host.patterLocalDecls);\n }\n\n /**\n * Seed a scene's `@scene` props (spec §7). The not-shared props seed THIS flow's\n * bag the first time it enters (persist across re-entries thereafter); the shared\n * props seed the host's stage bag the first time ANY flow enters the scene (shared\n * and persistent thereafter - a later flow finds it present and leaves it).\n * `temporary` props are the exception: reseeded to their default on every entry.\n */\n private seedScene(scene: CompiledScene): void {\n const shared = this.host.sceneSharedNames.get(scene.id) ?? new Set<string>();\n if (!this.sceneBags.has(scene.id)) {\n const bag: Record<string, ScalarValue> = {};\n for (const decl of scene.sceneProps ?? []) {\n const name = decl.name.toLowerCase();\n if (!shared.has(name)) bag[name] = sceneDefault(decl);\n }\n this.sceneBags.set(scene.id, bag);\n }\n if (!this.host.stageBags.has(scene.id)) {\n const bag: Record<string, ScalarValue> = {};\n for (const decl of scene.sceneProps ?? []) {\n const name = decl.name.toLowerCase();\n if (shared.has(name)) bag[name] = sceneDefault(decl);\n }\n this.host.stageBags.set(scene.id, bag);\n }\n\n // `temporary` props are reseeded to their default on EVERY entry (\"fresh each\n // playthrough\"), rather than persisting across re-entries like the rest.\n for (const decl of scene.sceneProps ?? []) {\n if (!decl.temporary) continue;\n const name = decl.name.toLowerCase();\n const bag = shared.has(name) ? this.host.stageBags.get(scene.id) : this.sceneBags.get(scene.id);\n if (bag) bag[name] = sceneDefault(decl);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers.\n// ---------------------------------------------------------------------------\n\n/** Collect the speakers under a run of nodes into `out`, in document order (insertion order IS the\n * result order). Groups contribute their option prompt's speaker - a prompt is a line | text beat, so\n * a line prompt is spoken by someone - and `walkNodes` carries the recursion into nested groups. */\nfunction collectCast(nodes: Array<CompiledGroup | CompiledSnippet>, out: Set<string>): void {\n walkNodes<SelectableNode>(nodes, (n) => {\n if (n.type === \"group\") {\n if (n.prompt?.kind === \"line\" && n.prompt.character) out.add(n.prompt.character);\n return;\n }\n for (const beat of n.beats ?? []) if (beat.kind === \"line\" && beat.character) out.add(beat.character);\n });\n}\n\n/** Serialise a `sequence` selector-cursor map to plain snapshots. */\nfunction serialiseSelectors(map: Map<string, SelectorState>): Record<string, SelectorSnapshot> {\n const out: Record<string, SelectorSnapshot> = {};\n for (const [id, st] of map) {\n const v: SelectorSnapshot = {};\n if (st.seq !== undefined) v.seq = st.seq;\n if (st.bag) v.bag = [...st.bag];\n if (st.last !== undefined) v.last = st.last;\n out[id] = v;\n }\n return out;\n}\n\n/** Rebuild a `sequence` selector-cursor map from snapshots. */\nfunction deserialiseSelectors(rec: Record<string, SelectorSnapshot> | undefined): Map<string, SelectorState> {\n const map = new Map<string, SelectorState>();\n for (const [id, v] of Object.entries(rec ?? {})) {\n const st: SelectorState = {};\n if (v.seq !== undefined) st.seq = v.seq;\n if (v.bag) st.bag = [...v.bag];\n if (v.last !== undefined) st.last = v.last;\n map.set(id, st);\n }\n return map;\n}\n\n/** Adapt a Patter `PropertyDecl` to a registry `ScopeDeclaration` (same type vocabulary). */\nfunction toDecl(decl: PropertyDecl): ScopeDeclaration {\n return { name: decl.name, type: decl.type, values: decl.values, default: decl.default };\n}\n\n/** A shared-decl's value for reset-to-default: its declared default, else the type default. */\nfunction declDefault(d: ScopeDeclaration): ScalarValue {\n if (d.default !== undefined) return d.default;\n switch (d.type) {\n case \"number\": return 0;\n case \"string\": return \"\";\n case \"flags\": return [];\n case \"enum\": return d.values?.[0] ?? \"\";\n default: return false; // boolean (and any unknown) → false\n }\n}\n\n/** A host-scope declaration (`@world.x`) → registry declaration. */\nfunction toForeignDecl(decl: HostScopeDecl): ScopeDeclaration {\n return { name: decl.name, type: decl.type, values: decl.values, default: decl.default, writable: decl.writable };\n}\n\n/** The seed value for a host-scope property: its declared default, else the type default. */\nfunction hostScopeDefault(decl: HostScopeDecl): ScalarValue {\n if (decl.default !== undefined) return decl.default;\n switch (decl.type) {\n case \"boolean\": return false;\n case \"number\": return 0;\n case \"string\": return \"\";\n case \"flags\": return [];\n case \"enum\": return decl.values?.[0] ?? \"\";\n }\n}\n\n/** Build a live in-memory `{ get, set }` resolver for a self-backed host scope (the standalone `@world`):\n * a plain bag seeded from declaration defaults. Declared-but-unseeded names still read `undefined`; an\n * opaque scope (no declarations) starts empty and accepts any name. Per-property read-only is enforced at\n * validation, not here (the registry is per-scope), so `set` accepts any name. */\nfunction selfBackedResolver(decls: HostScopeDecl[]): ScopeResolver {\n // Keyed LOWERCASE. The compiler lowercases every property reference, so an AST reads `isnight` where\n // the declaration says `isNight`; seeding the bag verbatim meant any declared name carrying a capital\n // was never found, read as undefined, and silently took the falsy branch. `@patter` and `@scene`\n // already normalise (patterSharedNames / sceneSharedNames); this resolver was the one that did not.\n const key = (name: string): string => name.toLowerCase();\n const bag = new Map<string, ScalarValue>();\n for (const d of decls) bag.set(key(d.name), hostScopeDefault(d));\n return {\n get: (name) => bag.get(key(name)),\n set: (name, value) => { bag.set(key(name), value); },\n };\n}\n\n/** The seed value for a scene-local property (its `default`, else the type default). */\nfunction sceneDefault(decl: PropertyDecl): ScalarValue {\n if (decl.default !== undefined) return decl.default;\n switch (decl.type) {\n case \"boolean\": return false;\n case \"number\": return 0;\n case \"string\": return \"\";\n case \"flags\": return [];\n case \"enum\": return decl.values?.[0] ?? \"\";\n }\n}\n\nfunction truthy(v: ScalarValue): boolean {\n if (typeof v === \"boolean\") return v;\n if (typeof v === \"number\") return v !== 0;\n if (typeof v === \"string\") return v !== \"\";\n return v.length > 0; // string[]\n}\n","// ---------------------------------------------------------------------------\n// Author tags (#215): a cross-cutting label layer baked into the bundle.\n//\n// A node's *accumulated* tags are the union of its own and every ancestor's,\n// ordered outermost-first (scene → block → group(s) → snippet → beat) and\n// deduped. That accumulation is purely structural, it depends only on where a\n// node sits in the tree, not on play state, so it's precomputed ONCE at engine\n// load into a flat `id -> string[]` index and read back as O(1) lookups for both\n// the delivered step `tags` and the `tagsFor*` accessors.\n// ---------------------------------------------------------------------------\n\nimport type { Bundle, CompiledGroup, CompiledSnippet } from \"@patterkit/model\";\n\nfunction dedupe(tags: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const t of tags) if (!seen.has(t)) { seen.add(t); out.push(t); }\n return out;\n}\n\n/**\n * Map every node id (scene / block / group / snippet / beat) to its accumulated\n * tags. Node ids are globally unique within a project (the validator enforces\n * it), so one flat map suffices. Nodes with no tags anywhere up the chain map to\n * an empty array.\n */\nexport function buildTagIndex(bundle: Bundle): Map<string, string[]> {\n const index = new Map<string, string[]>();\n\n const visit = (node: CompiledGroup | CompiledSnippet, inherited: string[]): void => {\n const acc = dedupe([...inherited, ...(node.tags ?? [])]);\n index.set(node.id, acc);\n if (node.type === \"group\") {\n for (const child of node.children) visit(child, acc);\n } else {\n for (const beat of node.beats ?? []) index.set(beat.id, dedupe([...acc, ...(beat.tags ?? [])]));\n }\n };\n\n for (const scene of Object.values(bundle.scenes)) {\n const sceneAcc = dedupe(scene.tags ?? []);\n index.set(scene.id, sceneAcc);\n for (const block of scene.blocks) {\n const blockAcc = dedupe([...sceneAcc, ...(block.tags ?? [])]);\n index.set(block.id, blockAcc);\n for (const child of block.children) visit(child, blockAcc);\n }\n }\n\n return index;\n}\n","// ---------------------------------------------------------------------------\n// describeBundle - the bundle inspector's runtime half.\n//\n// A BUNDLE-level function, deliberately NOT an Engine method. It answers the\n// integrator's question from the imported asset alone, with no engine, no state\n// and nothing running:\n//\n// I dropped a .patterc into my project. What may my game code call, and is\n// this the bundle I think it is?\n//\n// That is a different question from the one the property examiner answers. The\n// examiner (Engine.listProperties + the per-engine state panels) watches and\n// edits a LIVE game. This is static: it is the API boundary made visible, read\n// off the asset in an editor inspector before anything runs.\n//\n// Three readers. The integrator reads the addresses and the host scopes: those\n// are what game code may call and what it must supply. The writer reads them\n// when nothing happens on `runFlow(\"x\", \"some_scene\")` - the address they typed\n// is not in the list. The designer reads the identity and counts to confirm\n// what actually shipped.\n//\n// Everything is in BUNDLE ORDER, never sorted: two runtimes must render the\n// same rows in the same sequence, and bundle order is the only order all four\n// ports can agree on without importing a collation rule.\n//\n// Cheap by construction. Scenes, blocks and declarations are walked once;\n// nothing here parses an expression, resolves a string table, or touches the\n// per-locale text. `beats` is the one count that requires descending to the\n// leaves, and it is a running total taken during the same single walk.\n// ---------------------------------------------------------------------------\n\nimport { effectiveGameId } from \"@patterkit/model\";\nimport type {\n Bundle, CompiledBlock, CompiledGroup, CompiledSnippet, GameDataField,\n GameDataNodeKind, PropertyDecl, PropertyType, ScalarValue,\n} from \"@patterkit/model\";\n\n/** Which bundle this is: identity, staleness fingerprints, and how it ships. */\nexport interface BundleIdentity {\n /** The bundle schema tag (\"patter/bundle@0\"). */\n schema: string;\n /** The project name. A save must agree with this. */\n project: string;\n /** The authored bundle version, if the project stamps one. */\n version?: string;\n /** Fingerprint over the WHOLE bundle: what binds saves and gates staleness. */\n hash?: string;\n /** The same fingerprint with the string tables left out. Equal structureHash\n * plus a different hash means a TEXT-ONLY edit, which is what makes a live\n * hot-swap safe. Showing both lets an integrator tell those apart at sight. */\n structureHash?: string;\n /** Project-wide VO mode. */\n voiced: boolean;\n defaultLocale: string;\n locales: string[];\n /** How strings ship: \"embedded\" (the runtime resolves text) or \"ids\" (the\n * runtime emits beat IDs and the game localises them itself). */\n localisation: \"embedded\" | \"ids\";\n /** True when the source locale was embedded purely for debug playback. Such a\n * build is NOT shippable, which is worth saying loudly in an inspector. */\n sourceDebug: boolean;\n}\n\n/** One scene, and the addresses game code may aim at inside it. */\nexport interface AddressSummary {\n /** The host-facing scene address: what `runFlow` / `goto` take. Derived from\n * the name when the author set no explicit gameId, exactly as the runtime\n * resolves it, so this list is the truth rather than an approximation. */\n gameId: string;\n /** The authored scene name, for recognising the row. */\n name: string;\n /** Block addresses within this scene. A block address is SCENE-SCOPED: the\n * pair is the address, which is why these are nested rather than flattened. */\n blocks: { gameId: string; name: string }[];\n}\n\n/** One author-defined gameData field: part of the host-facing data surface. */\nexport interface GameDataFieldSummary {\n name: string;\n type: string;\n /** Whether the schema carries a fallback. Sparse storage means a node that\n * sets nothing reads this, so a field with no default can arrive absent. */\n hasDefault: boolean;\n /** Allowed values for an enum field: the set host code switches on. */\n values?: string[];\n purpose?: string;\n}\n\n/** The gameData fields declared for one kind of node. */\nexport interface GameDataSummary {\n kind: GameDataNodeKind;\n fields: GameDataFieldSummary[];\n}\n\n/** One declared property. `hasDefault` rather than the value itself: an\n * inspector wants to know whether the host MUST supply something. */\nexport interface PropertySummary {\n name: string;\n type: PropertyType;\n hasDefault: boolean;\n default?: ScalarValue;\n /** Shared across all flows, or kept per-flow. Defaults differ by scope\n * (`@patter` shared, `@scene` per-flow), so it is resolved here. */\n shared: boolean;\n}\n\n/** A host scope (`@world` and friends): what the GAME must supply.\n *\n * The highest-value section of the whole description. Today an integrator\n * discovers a missing world property when a condition silently reads a\n * self-backed default and a branch never fires. */\nexport interface HostScopeSummary {\n /** The token after `@`, e.g. \"world\". */\n token: string;\n /** Scope-level read/write default for its declarations. */\n writable: boolean;\n /** An OPAQUE scope declares no names: any name is accepted, unchecked. The\n * host contract is then \"anything\", which is worth showing as such rather\n * than as an empty property list. */\n opaque: boolean;\n properties: PropertySummary[];\n}\n\n/** Story-owned declarations, for orientation rather than for calling. */\nexport interface OwnedProperties {\n /** Project-level (`@patter`). */\n patter: PropertySummary[];\n /** Per scene (`@scene`), keyed by the scene's host address. */\n scene: { gameId: string; properties: PropertySummary[] }[];\n}\n\n/** \"Is this the right build?\" at a glance. */\nexport interface BundleCounts {\n scenes: number;\n blocks: number;\n groups: number;\n snippets: number;\n /** Snippet beats. This is the SAME population `Engine.getBeatSequence` walks, deliberately, so a\n * tool that lists beats and an inspector that counts them never disagree. Choice prompts are not\n * in it - see `prompts`. */\n beats: number;\n /** Choice-option prompts: beats that live on a group rather than in a snippet.\n *\n * Counted separately rather than folded into `beats` because folding them in would make this\n * number disagree with `getBeatSequence`, and leaving them out entirely would understate a\n * choice-heavy story - a branching script could report a handful of beats and look like the wrong\n * build. Neither silence nor a redefinition; a second row. */\n prompts: number;\n /** Beats that fire a game event rather than producing player-facing words. */\n gameEvents: number;\n /** Cast members the bundle carries (player-facing only; the compiler strips\n * the authoring fields). */\n cast: number;\n}\n\nexport interface BundleDescription {\n identity: BundleIdentity;\n /** Everything game code may aim at, in bundle order. */\n addresses: AddressSummary[];\n /** What the host must supply. */\n hostScopes: HostScopeSummary[];\n /** What the story owns. */\n properties: OwnedProperties;\n /** The author-defined data surface, grouped by node kind. */\n gameData: GameDataSummary[];\n counts: BundleCounts;\n}\n\n/** Resolve a declaration's sharing default, which differs by the scope it sits\n * in: a project-level property is shared, a scene-local one is per-flow. */\nconst isShared = (d: PropertyDecl, scopeDefault: boolean): boolean =>\n d.shared ?? scopeDefault;\n\nfunction summariseProperty(d: PropertyDecl, scopeDefault: boolean): PropertySummary {\n return {\n name: d.name,\n type: d.type,\n hasDefault: d.default !== undefined,\n ...(d.default !== undefined ? { default: d.default } : {}),\n shared: isShared(d, scopeDefault),\n };\n}\n\nfunction summariseField(f: GameDataField): GameDataFieldSummary {\n return {\n name: f.name,\n type: f.type,\n hasDefault: f.default !== undefined,\n ...(f.values ? { values: [...f.values] } : {}),\n ...(f.purpose ? { purpose: f.purpose } : {}),\n };\n}\n\n/** One pass over a block's tree, accumulating counts. Iterative rather than\n * recursive: a deeply nested choice tree should not put an inspector's stack\n * at risk, and the traversal order does not matter for a count. */\nfunction countBlock(block: CompiledBlock, counts: BundleCounts): void {\n counts.blocks++;\n const stack: Array<CompiledGroup | CompiledSnippet> = [...block.children];\n while (stack.length) {\n const node = stack.pop()!;\n if (node.type === \"group\") {\n counts.groups++;\n if (node.prompt) counts.prompts++;\n stack.push(...node.children);\n continue;\n }\n counts.snippets++;\n for (const beat of node.beats ?? []) {\n counts.beats++;\n if (beat.kind === \"gameEvent\") counts.gameEvents++;\n }\n }\n}\n\n/**\n * Describe a compiled bundle: what it is, and what a game may call on it.\n *\n * Pure and allocation-light. Safe to call from an editor inspector on every\n * selection, though a details panel should still build its rows once rather\n * than per repaint.\n */\nexport function describeBundle(bundle: Bundle): BundleDescription {\n const counts: BundleCounts = {\n scenes: 0, blocks: 0, groups: 0, snippets: 0, beats: 0, prompts: 0, gameEvents: 0,\n cast: bundle.cast?.length ?? 0,\n };\n\n const addresses: AddressSummary[] = [];\n const sceneProps: OwnedProperties[\"scene\"] = [];\n for (const scene of Object.values(bundle.scenes)) {\n counts.scenes++;\n const gameId = effectiveGameId(scene);\n addresses.push({\n gameId,\n name: scene.name,\n blocks: scene.blocks.map((b) => ({ gameId: effectiveGameId(b), name: b.name })),\n });\n for (const block of scene.blocks) countBlock(block, counts);\n // Scene-local declarations default to PER-FLOW, unlike project-level ones.\n if (scene.sceneProps?.length) {\n sceneProps.push({ gameId, properties: scene.sceneProps.map((d) => summariseProperty(d, false)) });\n }\n }\n\n const hostScopes: HostScopeSummary[] = (bundle.scopeRegistry?.scopes ?? []).map((s) => ({\n token: s.token,\n writable: s.writable ?? true,\n opaque: s.declarations === undefined,\n // A host scope's values live outside the story, so \"shared\" is not a choice\n // its declarations make; they are world-wide by nature.\n properties: (s.declarations ?? []).map((d) => summariseProperty(d as PropertyDecl, true)),\n }));\n\n const gameData: GameDataSummary[] = Object.entries(bundle.gameDataFields ?? {})\n .filter(([, fields]) => (fields?.length ?? 0) > 0)\n .map(([kind, fields]) => ({\n kind: kind as GameDataNodeKind,\n fields: (fields ?? []).map(summariseField),\n }));\n\n return {\n identity: {\n schema: bundle.schema,\n project: bundle.content.project,\n ...(bundle.content.version !== undefined ? { version: bundle.content.version } : {}),\n ...(bundle.content.hash !== undefined ? { hash: bundle.content.hash } : {}),\n ...(bundle.content.structureHash !== undefined ? { structureHash: bundle.content.structureHash } : {}),\n voiced: bundle.voiced,\n defaultLocale: bundle.locales.default,\n locales: [...bundle.locales.included],\n // Absent means \"embedded\": the back-compat default a bundle written before\n // the field existed relies on.\n localisation: bundle.localisation?.mode ?? \"embedded\",\n sourceDebug: bundle.localisation?.sourceDebug ?? false,\n },\n addresses,\n hostScopes,\n properties: {\n patter: (bundle.properties ?? []).map((d) => summariseProperty(d, true)),\n scene: sceneProps,\n },\n gameData,\n counts,\n };\n}\n","// gameData read helpers (spec: author-defined custom fields per node type). The published bundle\n// carries the field SCHEMA per node type (`bundle.gameDataFields`, each field with its default) plus\n// each node's SPARSE overrides (`node.gameData`). Storage is sparse + merge-at-read: a node holds only\n// the values it overrides, and a reader falls back to the field's default. These pure helpers do that\n// resolution so a host doesn't re-implement it.\n\nimport type { Bundle, GameData, GameDataField, GameDataNodeKind } from \"@patterkit/model\";\n\n/** The author-defined gameData fields declared for a node TYPE in a bundle (empty when none). */\nexport function gameDataFields(bundle: Bundle, kind: GameDataNodeKind): GameDataField[] {\n return bundle.gameDataFields?.[kind] ?? [];\n}\n\n/** One node's effective value for a field: its sparse OVERRIDE if present, else the field's declared\n * default (undefined if neither is set). `fields` is the schema for the node's type. */\nexport function gameDataValue(fields: GameDataField[], node: GameData | undefined, name: string): unknown {\n if (node && Object.prototype.hasOwnProperty.call(node, name)) return node[name];\n return fields.find((f) => f.name === name)?.default;\n}\n\n/** A node's FULL effective gameData: every declared field resolved (override or default), plus any\n * override keys with no matching field (orphans, kept verbatim). Fields left with no value are omitted. */\nexport function effectiveGameData(fields: GameDataField[], node: GameData | undefined): GameData {\n const out: GameData = {};\n for (const f of fields) {\n const v = gameDataValue(fields, node, f.name);\n if (v !== undefined) out[f.name] = v;\n }\n for (const [k, v] of Object.entries(node ?? {})) if (!(k in out)) out[k] = v;\n return out;\n}\n"],"mappings":"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","names":["next","id","effectiveGameId"]}