@patterkit/ops 0.17.1 → 0.18.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.d.ts CHANGED
@@ -5,6 +5,7 @@ import { ScopeDeclaration, ScopeRegistry } from '@wildwinter/scoperegistry';
5
5
  import { ValidationIssue } from '@patterkit/core';
6
6
  import { ConditionIssue } from '@patterkit/compiler';
7
7
  import { ChoiceOption, PlayError } from '@patterkit/runtime';
8
+ import { Dialect } from '@wildwinter/expr';
8
9
  export { isUnsafeEntry } from '@wildwinter/toolkit/archive';
9
10
 
10
11
  /** One file write an op wants performed. `content` is full canonical file text. */
@@ -1304,6 +1305,42 @@ interface ReplacePlan {
1304
1305
  */
1305
1306
  declare function runReplace(loaded: LoadedProject, opts: ReplaceOptions): ReplacePlan;
1306
1307
 
1308
+ /** The quality a stage edit is about: a property in a scope (`patter`, `scene`, or a host scope such as
1309
+ * `world`) by name. A `scene` property lives in one scene, which `scene` names. */
1310
+ interface StageTarget {
1311
+ scope: string;
1312
+ name: string;
1313
+ /** The scene a `scene`-scope quality is declared in. */
1314
+ scene?: string;
1315
+ }
1316
+ /** What a rename touched, for the caller to write and to report. */
1317
+ interface StageRenameResult {
1318
+ /** The scenes whose expressions changed. */
1319
+ scenes: Scene[];
1320
+ /** Expressions rewritten. */
1321
+ expressions: number;
1322
+ }
1323
+ type Ref = (scope: string, name: string) => boolean;
1324
+ /** Patter's dialect, with the project's host scopes, so `@world.act` parses as it compiles. */
1325
+ declare function stageDialect(project: ProjectFile, gameScopes?: GameScopes): Dialect;
1326
+ /**
1327
+ * Rewrite the stage literal `from` to `to` wherever `src` compares it with the quality `isTarget` names (or,
1328
+ * with `assigned`, where the whole expression is that literal: a set effect's value assigned to the quality).
1329
+ * Undefined when nothing changed or the source doesn't parse.
1330
+ */
1331
+ declare function renameStageInExpression(src: string, dialect: Dialect, isTarget: Ref, from: string, to: string, assigned?: boolean): string | undefined;
1332
+ /**
1333
+ * Carry a quality's stage rename, `from` to `to`, into every expression that compares the quality with the
1334
+ * old name or sets it to it. Mutates the scenes in place. The declaration (and its default) is the caller's
1335
+ * to change; this is everything that names the stage beside it.
1336
+ */
1337
+ declare function renameStage(scenes: Scene[], target: StageTarget, from: string, to: string, dialect: Dialect): StageRenameResult;
1338
+ /**
1339
+ * How many expressions order the quality against a stage (`<`, `<=`, `>`, `>=`): the gates whose meaning a
1340
+ * reorder changes, though no value does.
1341
+ */
1342
+ declare function stageGateCount(scenes: Scene[], target: StageTarget, dialect: Dialect): number;
1343
+
1307
1344
  /** One address written down. */
1308
1345
  interface PinnedName {
1309
1346
  /** The node's immutable id. */
@@ -1754,4 +1791,4 @@ declare const REFUSE_MISSING_SHARE = 0.25;
1754
1791
  /** Plan an import. Reads the handoff record named in the document from the project. */
1755
1792
  declare function planEditableImport(loaded: LoadedProject, returned: ReturnedDoc, opts: ImportOptions): ImportPlan;
1756
1793
 
1757
- export { AUDIO_MANIFEST_FILE, AUDIO_MANIFEST_SCHEMA, AUTHORING_HANDLED, type ApplyStats, type AudioEntry, type AudioRung, type AudioSnapshot, type BuildPlan, type BundlePosture, CONFLICT_SIDECAR, type CharacterReport, type Conflict, type ConflictKind, type ContentError, type CoverageAsyncHooks, type CoverageBeat, type CoverageHooks, type CoverageOptions, type CoverageOrder, type CoverageReport, type DecisionPlan, type DecisionResult, type DocumentManifest, type EditableScript, type EditableScriptOptions, type ExportOptions, type FormatResult, GAME_SCOPES_OWNER, type GameScopes, type GameScopesIssue, HANDOFF_DIR, type HandoffIssue, type HygieneIssue, type ImportOptions, type ImportPlan, type ImportProblem, type ImportReport, type InitOptions, type InitResult, type InitVcs, type LoadedProject, type LocCatalog, type LocEntry, type LocaleReport, type MergeFileType, type MergeResult, type MergedShard, NotAPackError, type OpenSuggestion, PATTER_SCOPES_FILE, PATTER_SCOPES_OWNER, type PinPlan, type PinnedName, type PlannedWrite, type PlayEvent, type PlayOptions, type PlayOutcome, type PlayResult, type ProvenanceCheck, RARE_REACH_PCT, REFUSE_MISSING_SHARE, type Random, type ReadBox, type ReadComment, type ReadItem, type ReadMarker, type ReadParagraph, type ReadText, type ReplaceHit, type ReplaceOptions, type ReplacePlan, type ReportData, type ReportTotals, type ResolveEntry, type ReturnedDoc, SCRIPT_FONT_FILES, SCRIPT_FONT_PACKAGES, SHARD_EXTENSIONS, type SceneReport, type ScriptDoc, type ScriptElement, type ScriptFace, type SearchFocus, type SuggestionDecision, type TrackedChange, type UnpackMergeResult, type UnpackResult, UnsafeEntryError, UnsupportedMergeError, type ValidateResult, type VoiceLine, type VoiceScript, type VoicedCounts, type WebExport, type WrittenCounts, applyLoc, applySceneOrder, applySuggestionDecisions, applyWrites, audioFolderRungs, audioFoldersActive, audioManifest, audioManifestWrite, authoringPath, besideBundlePath, bundleOutputPath, catalogToJson, catalogToPo, catalogToXlsx, classesForChannel, compileLoaded, cueLabel, currentBundlePosture, defaultBundlePath, defaultGameScopesDir, detectMergeType, discoverGameScopes, drivableScopes, exportBlockers, exportEditableScript, extractLoc, findProjectFile, formatMarker, gameScopeTokens, gameScopesCatalogue, gameScopesSnapshot, handoffPath, handoffWrite, hostScopeTokens, issueMarkerCodes, jsonToCatalog, leastReachedFirst, listOpenSuggestions, listProjectTags, loadProject, loadProjectLanding, markerLength, newHandoffId, parseHandoff, patterScopesFile, patterScopesStale, patterScopesWrite, pickAudio, planBuild, planEditableImport, planPins, planReturnedWorld, planShareScopes, planWorldSave, poToCatalog, previewRegistry, projectStringSources, proposeCoverageDrivers, qualifierNamer, reachabilityIssues, readEditableDocx, readGameScopes, readHandoff, readHandoffs, readMarker, renderCoverageText, renderPlay, renderReportText, resolveDocumentation, resolveStart, runCoverage, runCoverageAsync, runExport, runExportFull, runExportHtml, runExportWeb, runFormat, runInit, runMerge, runPack, runPlay, runPropertyUsage, runReplace, runReport, runReportXlsx, runResolve, runScriptDoc, runSearch, runStatusBrowse, runTagBrowse, runUnpack, runUnpackMerge, runValidate, runVoiceScript, scanAudio, scanAudioStatus, sceneIdForShard, scriptFontLicencePath, scriptFontLicences, scriptToDocx, scriptToPdf, serialiseHandoff, setCut, sidecarIssues, staleParts, useScriptFontFiles, vcsConfigWrites, voiceScriptToXlsx, worldSettingsScopes, xlsxToCatalog };
1794
+ export { AUDIO_MANIFEST_FILE, AUDIO_MANIFEST_SCHEMA, AUTHORING_HANDLED, type ApplyStats, type AudioEntry, type AudioRung, type AudioSnapshot, type BuildPlan, type BundlePosture, CONFLICT_SIDECAR, type CharacterReport, type Conflict, type ConflictKind, type ContentError, type CoverageAsyncHooks, type CoverageBeat, type CoverageHooks, type CoverageOptions, type CoverageOrder, type CoverageReport, type DecisionPlan, type DecisionResult, type DocumentManifest, type EditableScript, type EditableScriptOptions, type ExportOptions, type FormatResult, GAME_SCOPES_OWNER, type GameScopes, type GameScopesIssue, HANDOFF_DIR, type HandoffIssue, type HygieneIssue, type ImportOptions, type ImportPlan, type ImportProblem, type ImportReport, type InitOptions, type InitResult, type InitVcs, type LoadedProject, type LocCatalog, type LocEntry, type LocaleReport, type MergeFileType, type MergeResult, type MergedShard, NotAPackError, type OpenSuggestion, PATTER_SCOPES_FILE, PATTER_SCOPES_OWNER, type PinPlan, type PinnedName, type PlannedWrite, type PlayEvent, type PlayOptions, type PlayOutcome, type PlayResult, type ProvenanceCheck, RARE_REACH_PCT, REFUSE_MISSING_SHARE, type Random, type ReadBox, type ReadComment, type ReadItem, type ReadMarker, type ReadParagraph, type ReadText, type ReplaceHit, type ReplaceOptions, type ReplacePlan, type ReportData, type ReportTotals, type ResolveEntry, type ReturnedDoc, SCRIPT_FONT_FILES, SCRIPT_FONT_PACKAGES, SHARD_EXTENSIONS, type SceneReport, type ScriptDoc, type ScriptElement, type ScriptFace, type SearchFocus, type StageRenameResult, type StageTarget, type SuggestionDecision, type TrackedChange, type UnpackMergeResult, type UnpackResult, UnsafeEntryError, UnsupportedMergeError, type ValidateResult, type VoiceLine, type VoiceScript, type VoicedCounts, type WebExport, type WrittenCounts, applyLoc, applySceneOrder, applySuggestionDecisions, applyWrites, audioFolderRungs, audioFoldersActive, audioManifest, audioManifestWrite, authoringPath, besideBundlePath, bundleOutputPath, catalogToJson, catalogToPo, catalogToXlsx, classesForChannel, compileLoaded, cueLabel, currentBundlePosture, defaultBundlePath, defaultGameScopesDir, detectMergeType, discoverGameScopes, drivableScopes, exportBlockers, exportEditableScript, extractLoc, findProjectFile, formatMarker, gameScopeTokens, gameScopesCatalogue, gameScopesSnapshot, handoffPath, handoffWrite, hostScopeTokens, issueMarkerCodes, jsonToCatalog, leastReachedFirst, listOpenSuggestions, listProjectTags, loadProject, loadProjectLanding, markerLength, newHandoffId, parseHandoff, patterScopesFile, patterScopesStale, patterScopesWrite, pickAudio, planBuild, planEditableImport, planPins, planReturnedWorld, planShareScopes, planWorldSave, poToCatalog, previewRegistry, projectStringSources, proposeCoverageDrivers, qualifierNamer, reachabilityIssues, readEditableDocx, readGameScopes, readHandoff, readHandoffs, readMarker, renameStage, renameStageInExpression, renderCoverageText, renderPlay, renderReportText, resolveDocumentation, resolveStart, runCoverage, runCoverageAsync, runExport, runExportFull, runExportHtml, runExportWeb, runFormat, runInit, runMerge, runPack, runPlay, runPropertyUsage, runReplace, runReport, runReportXlsx, runResolve, runScriptDoc, runSearch, runStatusBrowse, runTagBrowse, runUnpack, runUnpackMerge, runValidate, runVoiceScript, scanAudio, scanAudioStatus, sceneIdForShard, scriptFontLicencePath, scriptFontLicences, scriptToDocx, scriptToPdf, serialiseHandoff, setCut, sidecarIssues, stageDialect, stageGateCount, staleParts, useScriptFontFiles, vcsConfigWrites, voiceScriptToXlsx, worldSettingsScopes, xlsxToCatalog };
package/dist/index.js CHANGED
@@ -961,99 +961,8 @@ function besideBundlePath(loaded, ext) {
961
961
  // src/validate.ts
962
962
  import { PROJECT_LOCALE_SCENE, projectLayout as projectLayout2 } from "@patterkit/model";
963
963
 
964
- // ../../../expr/packages/expr/src/ast.ts
965
- function deserialiseAst(node) {
966
- switch (node[0]) {
967
- case "b":
968
- return { kind: "bool", value: node[1] };
969
- case "n":
970
- return { kind: "number", value: node[1] };
971
- case "s":
972
- return { kind: "string", value: node[1] };
973
- case "sv":
974
- return { kind: "scopedvar", scope: node[1], name: node[2] };
975
- case "u":
976
- return { kind: "unary", op: node[1], operand: deserialiseAst(node[2]) };
977
- case "bin":
978
- return { kind: "binary", op: node[1], left: deserialiseAst(node[2]), right: deserialiseAst(node[3]) };
979
- case "call": {
980
- const args = node.slice(2).map(deserialiseAst);
981
- return { kind: "call", name: node[1], args };
982
- }
983
- case "fd":
984
- return { kind: "flagdelta", sign: node[1], name: node[2] };
985
- }
986
- }
987
-
988
- // ../../../expr/packages/expr/src/prng.ts
989
- function toUint32(seed) {
990
- if (Number.isNaN(seed) || !Number.isFinite(seed)) return 0;
991
- const modded = Math.trunc(seed) % 4294967296;
992
- return modded < 0 ? modded + 4294967296 : modded;
993
- }
994
- function makePrng(seed) {
995
- let s = toUint32(seed);
996
- return {
997
- next() {
998
- s = s + 1831565813 >>> 0;
999
- let t = Math.imul(s ^ s >>> 15, 1 | s);
1000
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
1001
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
1002
- },
1003
- state() {
1004
- return s;
1005
- }
1006
- };
1007
- }
1008
-
1009
- // ../../../expr/packages/expr/src/latches.ts
1010
- var scopedRef = (ast) => Array.isArray(ast) && ast[0] === "sv" ? `@${String(ast[1])}.${String(ast[2])}` : void 0;
1011
- function latchOf(ast, owner, keyOf2) {
1012
- const direct = scopedRef(ast);
1013
- if (direct !== void 0) return keyOf2(direct, owner);
1014
- if (!Array.isArray(ast)) return void 0;
1015
- if (ast[0] === "bin" && ast[1] === "==") {
1016
- const [, , l, r] = ast;
1017
- for (const [a, b] of [[l, r], [r, l]]) {
1018
- const ref = scopedRef(a);
1019
- if (ref !== void 0 && Array.isArray(b) && b[0] === "b" && b[1] === true) return keyOf2(ref, owner);
1020
- }
1021
- return void 0;
1022
- }
1023
- if (ast[0] === "call" && ast[1] === "check_flags") {
1024
- const ref = scopedRef(ast[2]);
1025
- const args = ast.slice(3);
1026
- if (ref === void 0 || args.length !== 1) return void 0;
1027
- const arg = args[0];
1028
- if (Array.isArray(arg) && arg[0] === "fd" && arg[1] === "+") {
1029
- return `${keyOf2(ref, owner)}:${String(arg[2])}`;
1030
- }
1031
- }
1032
- return void 0;
1033
- }
1034
- function disjuncts(ast) {
1035
- if (Array.isArray(ast) && ast[0] === "bin" && ast[1] === "or") {
1036
- const [, , l, r] = ast;
1037
- return [...disjuncts(l), ...disjuncts(r)];
1038
- }
1039
- return [ast];
1040
- }
1041
- function terms(ast, owner, keyOf2, negated = false, into = []) {
1042
- const latch = latchOf(ast, owner, keyOf2);
1043
- if (latch !== void 0) {
1044
- into.push({ key: latch, negated });
1045
- return into;
1046
- }
1047
- if (!Array.isArray(ast)) return into;
1048
- if (ast[0] === "u" && ast[1] === "not") return terms(ast[2], owner, keyOf2, !negated, into);
1049
- if (ast[0] === "bin" && ast[1] === "and" && !negated) {
1050
- terms(ast[2], owner, keyOf2, false, into);
1051
- terms(ast[3], owner, keyOf2, false, into);
1052
- }
1053
- return into;
1054
- }
1055
-
1056
964
  // src/reachability.ts
965
+ import { disjuncts, latchOf, scopedRef, terms } from "@wildwinter/expr";
1057
966
  var normaliseRef = (ref) => ref.includes(".") ? ref : `@patter.${ref.slice(1)}`;
1058
967
  var SEP = "\0";
1059
968
  var keyOf = (ref, sceneId) => ref.startsWith("@scene.") ? `${sceneId}${SEP}${ref}` : ref;
@@ -1631,7 +1540,7 @@ import { canonicalStringify as canonicalStringify4 } from "@patterkit/core";
1631
1540
  import { DEFAULT_PAD_AFTER } from "@patterkit/model";
1632
1541
 
1633
1542
  // src/playable-runtime.ts
1634
- var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var X=Object.defineProperty;var rt=Object.getOwnPropertyDescriptor;var nt=Object.getOwnPropertyNames;var st=Object.prototype.hasOwnProperty;var it=(n,e)=>{for(var t in e)X(n,t,{get:e[t],enumerable:!0})},ot=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of nt(e))!st.call(n,s)&&s!==t&&X(n,s,{get:()=>e[s],enumerable:!(r=rt(e,s))||r.enumerable});return n};var at=n=>ot(X({},"__esModule",{value:!0}),n);var Ot={};it(Ot,{Checkpoint:()=>V,DEFAULT_PAD_AFTER:()=>x,Engine:()=>z,Flow:()=>P,buildTagIndex:()=>Q,describeBundle:()=>ze,effectiveGameData:()=>Xe,gameDataFields:()=>Ye,gameDataValue:()=>ce});function R(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function E(n){switch(n[0]){case"b":return{kind:"bool",value:n[1]};case"n":return{kind:"number",value:n[1]};case"s":return{kind:"string",value:n[1]};case"sv":return{kind:"scopedvar",scope:n[1],name:n[2]};case"u":return{kind:"unary",op:n[1],operand:E(n[2])};case"bin":return{kind:"binary",op:n[1],left:E(n[2]),right:E(n[3])};case"call":{let e=n.slice(2).map(E);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var u=class extends Error{constructor(e){super(e),this.name="EvalError"}};function $(n,e,t){let r=new Map(t.scopes.map(i=>[i.token,i.missing??"false"])),s=i=>{switch(i.kind){case"bool":return i.value;case"number":return i.value;case"string":return i.value;case"scopedvar":{let o=R(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):R(o,i.name);if(a===void 0){if(r.get(i.scope)==="throw")throw new u(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!R(t.functions,i.name)){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new u(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=Z(a,e);if(c===void 0)throw new u("advance() needs a quality reference (@scope.name of a quality property)");let l=v(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=R(t.functions,i.name);if(!o)throw new u(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new u("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=s(i.operand);if(typeof a!="boolean")throw new u(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new u(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let h=s(i.left);if(typeof h!="boolean")throw new u(`\'and\' requires boolean operands, left is ${typeof h}`);if(!h)return!1;let g=s(i.right);if(typeof g!="boolean")throw new u(`\'and\' requires boolean operands, right is ${typeof g}`);return g}if(i.op==="or"){let h=s(i.left);if(typeof h!="boolean")throw new u(`\'or\' requires boolean operands, left is ${typeof h}`);if(h)return!0;let g=s(i.right);if(typeof g!="boolean")throw new u(`\'or\' requires boolean operands, right is ${typeof g}`);return g}let o=s(i.left),a=s(i.right),c=Z(i.left,e),l=Z(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!ct(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new u(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return v(o,p,">")>v(a,p,">");case">=":return v(o,p,">=")>=v(a,p,">=");case"<":return v(o,p,"<")<v(a,p,"<");case"<=":return v(o,p,"<=")<=v(a,p,"<=");case"+":case"-":case"*":case"/":throw new u(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return Se(o,a);case"!=":return!Se(o,a);case">":return C(o,a,">"),o>a;case">=":return C(o,a,">="),o>=a;case"<":return C(o,a,"<"),o<a;case"<=":return C(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new u(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return C(o,a,"-"),o-a;case"*":return C(o,a,"*"),o*a;case"/":if(C(o,a,"/"),a===0)throw new u("division by zero");return o/a}}}};return s(n)}function Se(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;let t=[...n].sort(),r=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==r[s])return!1;return!0}return n===e}function C(n,e,t){if(typeof n!="number"||typeof e!="number")throw new u(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function Z(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function v(n,e,t){if(typeof n!="string")throw new u(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new u(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var ct=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function O(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function ee(n){let e=O(n);return{next(){e=e+1831565813>>>0;let t=Math.imul(e^e>>>15,1|e);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296},state(){return e}}}var lt={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},pt=[lt];function ye(n,e,t){let r=t?.countingCalls??pt;return _(n,t?.want??!0,e,r)}function _(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=_(n.left,e,t,r),i=_(n.right,e,t,r);return n.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(n.kind==="unary"&&n.op==="not")return _(n.operand,!e,t,r);if(n.kind==="call"){let s=r.find(i=>i.name===n.name);if(s){let i=s.count(n),o=t(n);return e?o?i:0:o?0:1}}return t(n)===e?1:0}var b=class n{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let r=this.norm(t.name);this.decls.set(r,t),this.values[r]=structuredClone(t.default??B(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,r){let s=this.norm(e);if(!r?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:r?.silent??!1,reason:r?.reason};this.values[s]=t;for(let o of[...this.auditors])o(i);if(!i.silent)for(let o of[...this.subscribers])o(i);return i}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>ve(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm,pathPrefix:this.pathPrefix});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e){for(let[t,r]of Object.entries(e))this.values[this.norm(t)]=r}};function ve(n,e,t,r,s=""){let i=r??n.name.toLowerCase();return{name:i,path:s+i,type:n.type,value:e,default:n.default??B(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var dt=n=>n.toLowerCase(),te=1,H=class{scopes=new Map;parked=new Map;rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},i=new b(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,r){this.assertFree(e,r?.owner),this.scopes.set(e,{kind:"owned",bag:t,...r?.owner!==void 0?{owner:r.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(t.load(s),this.parked.delete(e)),this}remove(e,t){let r=this.scopes.get(e);if(!r)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&r.kind==="owned"&&this.parked.set(e,r.bag.save()),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&this.parked.delete(t);return this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??dt,a=new Map;for(let c of r)a.set(o(c.name),c);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:i.writable??!0,norm:o,...i.owner!==void 0?{owner:i.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(r)return r.kind==="owned"?r.bag.get(t):r.resolver.get(r.norm(t))}set(e,t,r,s){let i=this.scopes.get(e);if(!i)throw new Error(`unknown scope \'@${e}\'`);if(i.kind==="owned"){if(!s?.host&&!i.bag.writable(t))throw new Error(`\'@${e}.${t}\' is read-only`);i.bag.set(t,r,s?.host?{host:!0}:void 0);return}let o=i.norm(t);if(!i.resolver.set)throw new Error(`\'@${e}.${t}\' is read-only`);if(!s?.host&&!this.foreignWritable(i,o))throw new Error(`\'@${e}.${t}\' is read-only`);i.resolver.set(o,r)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,r]of this.scopes){let s=r.owner!==void 0?{owner:r.owner}:{};if(r.kind==="owned")for(let i of r.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of r.decls)e.push({scope:t,...s,...ve(o,r.resolver.get(i),this.foreignWritable(r,i),i,`${t}.`)})}return e}toEvalContext(e,t){let r=this.view(t?.aliases),s={};for(let[o,a]of r)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(r);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=r.get(o);return c?i.get(o)?.get(ut(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[r,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${r}\' names \'${s}\', which is not registered`);t.set(r,i)}return t}qualityLadders(e){let t=new Map;for(let[r,s]of e)for(let[i,o]of we(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(r);a||(a=new Map,t.set(r,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[r,s]of this.view(e?.aliases)){let i=we(s);if(i.length===0)continue;let o=new Map;for(let[a,c]of i)o.set(a,{type:c.type,enumValues:c.values,...c.stages!==void 0?{stages:c.stages}:{}});t.set(r,o)}return{properties:t}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());for(let[t,r]of this.parked)e[t]=structuredClone(r);return e}load(e,t){t?.keepParked||this.parked.clear();for(let[r,s]of Object.entries(e)){let i=this.scopes.get(r);i?.kind==="owned"?i.bag.load(s):i||this.parked.set(r,structuredClone(s))}}saveFragment(){return{version:te,scopes:this.save()}}loadFragment(e){if(e.version!==te)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${te})`);this.load(e.scopes)}assertFree(e,t){let r=this.scopes.get(e);if(!r)return;let s=r.owner!==void 0?` by ${r.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function we(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function ut(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function B(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??"";default:return!1}}var be=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function A(n){return n.ctx.host??{}}var re={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new u("random(a, b) requires exactly 2 arguments");let t=A(e).nextRandom;if(!t)throw new u("random() called without a PRNG in context");let r=e.evaluate(n[0]),s=e.evaluate(n[1]);if(typeof r!="number"||typeof s!="number")throw new u("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new u("random(a, b) arguments must be integers");let i=Math.min(r,s),o=Math.max(r,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:ke("check_flags"),eval(n,e){let t=xe(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new u("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:ke("set_flags"),eval(n,e){let t=[...xe(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new u("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let i=t.indexOf(s.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:W("visits"),eval:(n,e)=>A(e).visits?.(U(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:W("seen"),eval:(n,e)=>(A(e).visits?.(U(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:W("patter_visits"),eval:(n,e)=>A(e).patterVisits?.(U(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:W("patter_seen"),eval:(n,e)=>(A(e).patterVisits?.(U(n,e,"patter_seen"))??0)>0}}},Jt=be.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function Ee(n,e){let t=n.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function U(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new u(`${t}(id) requires a string node id`);return r}function W(n){return(e,t)=>{let r=e[0];r&&r.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(id): the argument must be a string id literal (a scene / block / node id)`})}}function xe(n,e,t){if(!n)throw new u(`${t}() requires at least one argument (the flags variable)`);let r=e.evaluate(n);if(Array.isArray(r))return r;if(r===!1||r===null||r===void 0)return[];throw new u(`${t}() first argument must be a flags property`)}function ke(n){return(e,t)=>{if(e.length===0)return;let r=e[0];if(r.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(r.scope)?.get(r.name);if(s&&s.type!=="flags"){let i=r.scope===t.defaultScope?r.name:`${r.scope}.${r.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): \'@${i}\' is not a flags property (got ${s.type})`});return}for(let i=1;i<e.length;i++){let o=e[i];o.kind!=="flagdelta"?t.report({path:[...t.path,"args",i],kind:"wrong-arg-type",severity:"error",message:`${n}(): argument ${i+1} must be +flagName or -flagName`}):s?.type==="flags"&&s.enumValues&&!s.enumValues.includes(o.name)&&t.report({path:[...t.path,"args",i],kind:"unknown-flag-name",severity:"error",message:`${n}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var gt=/^@[A-Za-z0-9_.]+$/;function*ft(n){let e="",t=0;for(;t<n.length;){let r=n[t];if(r==="{"&&n[t+1]==="{"){e+="{",t+=2;continue}if(r==="}"&&n[t+1]==="}"){e+="}",t+=2;continue}if(r==="{"){let s=n.indexOf("}",t+1);if(s!==-1){let i=n.slice(t,s+1),o=n.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:gt.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=r,t+=1}e&&(yield{kind:"text",value:e})}function ht(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function mt(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function St(n){let e="",t=!1;for(let r of n){if(mt(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function Ce(n,e,t){if(e.length===0||n.indexOf(e)<0)return n;let r="",s=0,i=!1;for(;s<n.length;){if(n.startsWith(e,s)){let o=n.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}r+=n.slice(s);break}r+=n[s],s+=1}return i?St(r):n}function De(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of ft(n)){if(r.kind==="text"){t+=r.value;continue}if(!r.ref){t+=r.raw;continue}let s=e(r.ref);t+=s===void 0?"":ht(s)}return t}function D(n,e){for(let t of n){e(t);let r=t.children;r&&D(r,e)}}function yt(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function w(n){let e=n.gameId?.trim();return e||yt(n.name)}var x=.6;function K(n){return`cast:${n}`}function J(n){return`qualifier:${n}`}function Ie(n,e){return`speaker:${n}:${e}`}var Pe="speaker:pattern",Re="{name} ({qualifier})";function Oe(n,e,t){return n.split("{name}").map(r=>r.split("{qualifier}").join(t)).join(e)}var ne={open:"[",close:"]"},Be="SFX";var F=3;function N(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function Q(n){let e=new Map,t=(r,s)=>{let i=N([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,N([...i,...r.prompt.tags??[]])),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,N([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=N(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=N([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var Ae=n=>n.kind==="line"||n.kind==="text",T=n=>typeof n=="number"&&Number.isFinite(n)?n:void 0;function Fe(n){let e=new Map,t=(i,o,a)=>{let c=i.kind==="gameEvent"?void 0:T(i.padAfter),l=c??o;a&&l<0&&(l=0),e.set(i.id,c!==void 0?{own:c,resolved:l}:{resolved:l})},r=(i,o)=>{let a=T(i.padAfterDefault)??o;if(i.type==="group"){i.prompt&&t(i.prompt,a,!1);for(let p of i.children)r(p,a);return}let c=i.beats??[],l=-1;c.forEach((p,h)=>{Ae(p)&&(l=h)}),c.forEach((p,h)=>{Ae(p)&&t(p,a,h===l)})},s=T(n.padAfterDefault)??x;for(let i of Object.values(n.scenes)){let o=T(i.padAfterDefault)??s;for(let a of i.blocks){let c=T(a.padAfterDefault)??o;for(let l of a.children)r(l,c)}}return e}var Ne=new WeakMap,Te=new WeakMap;function wt(n){let e=Te.get(n);return e||(e=Q(n),Te.set(n,e)),e}var Le=new WeakMap;function vt(n){let e=Le.get(n);return e||(e=Fe(n),Le.set(n,e)),e}var je=new WeakMap;function bt(n){let e=je.get(n);if(e)return e;e=new Map;for(let t of Object.values(n.scenes))for(let r of t.blocks)D(r.children,s=>{if(s.type==="group"){s.prompt&&e.set(s.prompt.id,s.prompt);return}for(let i of s.beats??[])e.set(i.id,i)});return je.set(n,e),e}function I(n,e){return n.tagIndex.get(e)?.slice()}var G="Patter",L=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),xt=n=>n.replace(/%2F/g,"/").replace(/%25/g,"%"),Ge=Symbol("ownsRegistry"),y={stage:n=>`patter/scene/${L(n)}`,flow:n=>`patter/flow/${L(n)}/`,flowGlobals:n=>`patter/flow/${L(n)}/patter`,flowScene:(n,e)=>`patter/flow/${L(n)}/scene/${L(e)}`},V=class{constructor(e){this.journal=e}journal},z=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;engineLog=[];engineSeq=0;engineTraceHandlers=new Set;constructor(e,t={}){this.creationOptions=t;let r=t.locale??e.locales.default,s=e.strings;this.allStrings=s,this.currentLocale=r;let i=s[r]??{},o=s[e.locales.default]??{},a=e.localisation,c=a?.mode==="ids"&&!a.sourceDebug;this.sourceDebug=a?.mode==="ids"&&!!a.sourceDebug,this.sourceDebug&&typeof console<"u"&&console.warn("[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.");let l=new Map;for(let d of e.cast??[])d.displayName&&l.set(d.name,d.displayName);let p=new Map;for(let d of e.qualifiers??[])d?.gameId&&p.set(d.gameId,d.name);let h=new Map;for(let d of e.cast??[])for(let[f,S]of Object.entries(d.qualifierLabels??{}))typeof S=="string"&&h.set(`${d.name}\\0${f}`,S);let g=e.speakerPattern??Re;this.defaultSeed=(t.seed??2654435769)>>>0;let le=new Map,pe=new Map,de=new Map;for(let[d,f]of Object.entries(e.scenes)){this.sceneGameIdToId.set(w(f),d);let S=new Map;for(let m of f.blocks)pe.set(m.id,{sceneId:d}),de.set(m.id,m),S.set(w(m),m.id),D(m.children,q=>le.set(q.id,q));this.blockGameIdToId.set(d,S)}let ue=e.properties??[],Y=ue.filter(d=>d.shared??!0).map($e),Ze=ue.filter(d=>!(d.shared??!0)).map($e),et=new Set(Y.map(d=>d.name.toLowerCase())),k=t.registry??new H,ge=!t.registry||t[Ge]===!0,fe=new b(Y,{pathPrefix:"@patter."}),he=[],M=[];try{k.mountOwned("patter",fe,{owner:G}),M.push("patter");let d=new Set;for(let[f,S]of Object.entries(_e(t))){let m=e.scopeRegistry?.scopes.find(tt=>tt.token===f),q=(m?.declarations??[]).map(Ke);k.defineForeign(f,S,q,{writable:m?.writable??!0,owner:G}),M.push(f),d.add(f)}for(let f of ge?e.scopeRegistry?.scopes??[]:[]){if(d.has(f.token)||k.has(f.token))continue;let S=(f.declarations??[]).map(m=>Et(m,f.writable));k.defineOwned(f.token,S,{owner:G}),M.push(f.token),he.push(f.token)}}catch(d){for(let f of M)k.remove(f,{keep:!0});throw d}let me=new Map;for(let[d,f]of Object.entries(e.scenes)){let S=new Set((f.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));me.set(d,S)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(d,f,S)=>this.emitEngine(d,f,S),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:p,speakerLabels:h,speakerPattern:g,nodeIndex:le,blockIndex:pe,blockById:de,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:wt(e),padIndex:vt(e),registry:k,ownsRegistry:ge,patterBag:fe,hostScopes:he,patterSharedDecls:Y,patterLocalDecls:Ze,patterSharedNames:et,sceneSharedNames:me,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(d=>console.warn(`Patterplay: ${d.kind} on ${d.node} in flow \'${d.flow}\' failed, played through: ${d.message}`)),replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??ne).open,captionClose:(e.closedCaptions??ne).close,captionCharacter:e.closedCaptions?.character||Be,refSplitCache:new Map,refSplitRevision:k.revision}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}get buildId(){return this.host.bundle.content?.hash??""}setLocale(e){this.currentLocale=e,this.host.strings=this.allStrings[e]??{}}replaceStrings(e){this.allStrings=e.strings,this.host.strings=this.allStrings[this.currentLocale]??{},this.host.defaultStrings=this.allStrings[this.host.bundle.locales.default]??{}}hotSwap(e){this.refuseInCheckpoint("hotSwap");let t=this.saveGame(),r=o=>(o.setLocale(this.currentLocale),o.setClosedCaptions(this.host.captionsOn),o),s=()=>new n(e,{...this.creationOptions,registry:this.host.registry,[Ge]:this.host.ownsRegistry});this.release(!0);let i=s();try{return i.loadGame(t),r(i)}catch{i.release(!0);let o=s();for(let[a,c]of Object.entries(t.flows)){let l=c.cursor.currentSceneId;try{o.openFlow(a,l!==null?{scene:l}:{})}catch{}}return r(o)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:r,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new P(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(r,s),a}runFlow(e,t,r){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:r}).advanceToStop().played;if(!s.goto(t,r))throw new Error(`runFlow: address not found: ${t}${r===void 0?"":` / ${r}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let r=ie(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=oe(this.host,r,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:r,blockId:s}}if(t!==void 0){if(!this.host.blockIndex.has(t))throw new Error(`unknown block: ${t}`);return{blockId:t}}if(Object.keys(this.host.bundle.scenes).length===0)throw new Error("no scenes in bundle");return{}}resolveSceneRef(e){if(e!=null)return ie(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:oe(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?w(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?w(t):void 0}tagsForBeat(e){return I(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?I(this.host,t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?I(this.host,s):void 0)??[]}gameDataForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.gameData}}gameDataForBlock(e,t){let r=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(r!=null?this.host.blockById.get(r):void 0)?.gameData}}getCast(){let e=[];for(let t of this.host.bundle.cast??[])t?.name&&e.push(t.name);return e}castForScene(e){let t=this.resolveSceneRef(e),r=t!=null?this.host.bundle.scenes[t]:void 0;if(!r)return[];let s=new Set;for(let i of r.blocks)Ve(i.children,s);return[...s]}castForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return Ve(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...w(e)?{gameId:w(e)}:{},name:e.name,...Me(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...w(t)?{gameId:w(t)}:{},name:t.name,...Me(t.gameData),...this.tagsField(t.id),children:t.children.map(r=>this.outlineNode(r))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let r of t.blocks)D(r.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:r.id,snippetId:s.id,beat:this.beatInfo(i)})});return e}outlineNode(e){return e.type==="group"?{type:"group",id:e.id,...this.tagsField(e.id),...e.selector?{selector:e.selector}:{},...e.prompt?{prompt:this.beatInfo(e.prompt)}:{},children:e.children.map(t=>this.outlineNode(t))}:{type:"snippet",id:e.id,...this.tagsField(e.id),beats:(e.beats??[]).map(t=>this.beatInfo(t)),...e.jump?{jumpTo:e.jump.to,...e.jump.mode?{jumpMode:e.jump.mode}:{}}:{}}}beatInfo(e){let t=I(this.host,e.id),r={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){r.character=e.character;let i=this.host.defaultStrings[K(e.character)]??this.host.castDisplay.get(e.character);i!==void 0&&(r.characterName=i)}if(e.direction!==void 0&&(r.direction=e.direction),e.qualifier!==void 0){r.qualifier=e.qualifier;let i=this.host.defaultStrings[J(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);i!==void 0&&(r.qualifierName=i)}let s=j(this.host,e.character,e.qualifier,!0);s!==void 0&&(r.speakerLabel=s)}if(e.kind==="line"||e.kind==="text"){let s=this.host.padIndex.get(e.id);r.padAfter=s?.resolved??x,s?.own!==void 0&&(r.ownPadAfter=s.own);let i=this.host.defaultStrings[e.id];i!==void 0&&(r.text=i)}return e.gameData&&Object.keys(e.gameData).length&&(r.gameData=e.gameData),t&&t.length&&(r.tags=t),r}tagsField(e){let t=I(this.host,e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}listBags(){let e=[{bag:this.host.patterBag}];for(let[t,r]of this.host.stageBags)e.push({bag:r,pathPrefix:`@scene:${t}.`});return e}checkpoint(){if(this.host.journal)throw new Error("a checkpoint is already open: roll it back or commit it first");let e={undo:[],flows:new Set,opened:new Set,selectors:new Set,flowSelectors:new Map};return this.host.journal=e,new V(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let r=t.length-1;r>=0;r--)t[r]();n.forget(e.journal)}commit(e){this.endCheckpoint(e),n.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}captureFlow(e){let t=this.flowsById.get(e);if(!t)throw new Error(`no open flow \'${e}\' to capture`);return{version:F,...t.snapshot(),...Dt(e,this.host.registry.save())}}restoreFlow(e,t){this.refuseInCheckpoint("restoreFlow");let r=t.version;if(r!==2&&r!==F)throw new Error(`unsupported flow state version: ${String(r)}`);let s=t.cursor;if(typeof s!="object"||s===null)throw new Error("malformed flow state: no cursor");if(this.flowsById.has(e))throw new Error(`a flow \'${e}\' is already open; close it before restoring another under that id`);this.assertExternalScopes();let i=y.flow(e);this.host.registry.discardParked(i),this.host.registry.load(Qe(e,t),{keepParked:!0});let o=new P(e,this.host,this.defaultSeed);try{o.restore(t)}catch(a){throw o.close(),this.host.registry.discardParked(i),a}return this.flowsById.set(e,o),o}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(y.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let r of this.flowsById.values())r.releaseBags(e),r.close();this.flowsById.clear();let t=this.host.registry;for(let r of this.host.stageBags.keys())t.remove(y.stage(r),{keep:e});this.host.stageBags.clear();for(let r of["patter",...this.host.hostScopes])t.has(r)&&t.remove(r,{keep:e});for(let r of Object.keys(_e(this.creationOptions)))t.has(r)&&t.remove(r)}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.registry.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e),i=this.host.journal?this.host.registry.get(r,s):void 0;this.host.registry.set(r,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(r,s,i,{host:!0}))}log(){return this.engineLog}clearLog(){this.engineLog.length=0}onTrace(e){return this.engineTraceHandlers.add(e),this.host.tracing=!0,()=>{this.engineTraceHandlers.delete(e),this.host.tracing=this.host.logEnabled||this.engineTraceHandlers.size>0}}emitEngine(e,t,r){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...r?{scene:r}:{}})}getVisitCounts(){return Object.fromEntries(this.host.sharedVisits)}listProperties(){return this.host.patterSharedDecls.map(e=>({name:e.name,path:`@patter.${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:B(e),writable:e.writable??!0}))}splitShared(e){let t=Je(this.host,e);if(t.scope==="scene")throw new Error(`\'${e}\': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);return t}save(){return{patter:this.host.patterBag.save()}}load(e){e.patter&&this.host.patterBag.load(e.patter)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:F,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:Ue(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==F)throw new Error(`unsupported save version: ${String(t)}`);let r=e.flows;if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("malformed save: no flows");this.assertExternalScopes();let s=this.host.registry;for(let[o,a]of this.flowsById)a.releaseBags(o in e.flows),a.close();this.flowsById.clear();for(let o of this.host.stageBags.keys())s.remove(y.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.version===2?Ct(e):e.registry;i&&(this.host.ownsRegistry?s.load(i):s.load(i,{keepParked:!0})),this.host.sharedVisits.clear();for(let[o,a]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(o,a);this.host.sharedSelectors.clear();for(let[o,a]of We(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new P(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},P=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.sceneBagFor(r,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,r){this.id=e,this.host=t,this.rngState=O(r),this.local=this.newLocal(),this.evalCtx={scopes:{},host:{nextRandom:this.rng,visits:s=>this.visitCounts.get(s)??0,patterVisits:s=>this.host.sharedVisits.get(s)??0},qualities:(s,i)=>this.stagesFor(s,i)}}context(){let e=this.host.registry;if(e.revision!==this.ctxRevision){let t=e.toEvalContext(),r=this.evalCtx.scopes;for(let s of Object.keys(r))delete r[s];Object.assign(r,t.scopes),r.patter=this.patterResolver,r.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let r=t.toLowerCase();if(e==="patter")return se(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:se(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??se(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(r)}begin(e,t){if(this.host.journal&&!this.host.journal.opened.has(this))throw new Error("a flow can\'t be started or reset while a checkpoint is open");if(this.releaseBags(!1),this.host.registry.discardParked(y.flow(this.id)),this.mountLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.clearPending(),this.started=!0,t){let r=this.host.blockIndex.get(t);if(!r)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(r.sceneId),this.stack=[{sceneId:r.sceneId,containerId:t,index:0}],this.enter(t)}else{let r=e??Object.keys(this.host.bundle.scenes)[0],s=r?this.host.bundle.scenes[r]:void 0;if(!s)throw new Error(r?`unknown scene: ${r}`:"no scenes in bundle");this.enterSceneSetup(r);let i=s.blocks[0];i&&(this.stack=[{sceneId:r,containerId:i.id,index:0}],this.enter(i.id))}this.settle()}reset(e,t){this.begin(e,t)}start(e,t){this.reset(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=ie(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=oe(this.host,r,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.begin(r,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.clearPending()}clearPending(){this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null}touch(){let e=this.host.journal;if(!e||e.flows.has(this))return;e.flows.add(this);let{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p}=this,h=this.stack.map(g=>({...g}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p,stack:h})})}get isClosed(){return this.closed}get currentScene(){return this.currentSceneId}advance(){if(this.closed)return{type:"end"};if(!this.started)throw new Error("flow has not been started");if(this.touch(),this.pendingPrompt){let e=this.pendingPrompt;return this.pendingPrompt=null,this.pendingPromptOwnerId=null,e.shown?this.promptResult(e.beat,e.shown):this.beatResult(e.beat)}return this.settle(),this.flowEnded?{type:"end"}:this.pendingChoice?{type:"choice",groupId:this.pendingChoice.groupId,options:this.pendingChoice.options}:this.activeSnippet?this.beatResult(this.activeSnippet.beats[this.beatIndex++]):(this.flowEnded=!0,{type:"end"})}advanceToStop(){let e=[];for(;;){let t=this.advance();if(t.type==="choice"||t.type==="end")return{played:e,stop:t};e.push(t)}}settle(){let e=0;for(;;){if(++e>1e4)throw new Error("flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content");if(this.flowEnded||this.pendingChoice)return;if(this.activeSnippet){if(this.beatIndex<(this.activeSnippet.beats?.length??0))return;this.runEffects(this.activeSnippet.onExit,this.activeSnippet.id);let i=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(i);continue}let t=this.stack[this.stack.length-1];if(!t){this.flowEnded=!0;return}t.sceneId!==this.currentSceneId&&(this.currentSceneId=t.sceneId);let r=this.childrenOf(t.containerId);if(!r){this.stack.pop();continue}let s=t.index;for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:r.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:r[t.index]?.id??null}),t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,r]of this.sceneBags)e.push({bag:r,pathPrefix:`${this.id}/@scene:${t}.`});return e}log(){return this.flowLog}clearLog(){this.flowLog.length=0}emit(e){let t=this.currentSceneId??void 0;this.host.emitEngine(this.id,e,t),this.host.logEnabled&&this.flowLog.push({...e,seq:this.flowSeq++,...t?{scene:t}:{}})}getVisitCounts(){return Object.fromEntries(this.visitCounts)}stepFor(e){let t=bt(this.host.bundle).get(e);return t?this.beatResult(t):void 0}getChoices(){return this.pendingChoice?.options??[]}choose(e){let t=this.pendingChoice;if(!t)throw new Error("no choice is pending");let r=t.options.find(o=>o.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);this.touch();let s=t.byId.get(e);this.host.tracing&&this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null;let i=this.host.replayPromptOnChoose?this.authoredPromptOf(s):void 0;this.pendingPrompt=i&&r.prompt?{beat:i,shown:{...r.prompt}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:r}=this.splitRef(e);return t==="patter"?this.patterResolver.get(r):t==="scene"?this.sceneResolver.get(r):this.host.registry.get(t,r)}setProperty(e,t){this.writeProperty(e,t,!0)}writeProperty(e,t,r){let{scope:s,name:i}=this.splitRef(e),o=this.host.journal;if(s==="patter"){if(o){let a=this.host.patterSharedNames.has(i),c=a?this.host.patterBag.get(i):this.local.get(i),l=this.local;c!==void 0&&o.undo.push(a?()=>this.host.registry.set("patter",i,c):()=>l.set(i,c))}this.patterResolver.set(i,t)}else if(s==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);if(o){let a=this.sceneBagFor(this.currentSceneId,i),c=a?.get(i);a&&c!==void 0&&o.undo.push(()=>a.set(i,c))}this.sceneResolver.set(i,t)}else{let a=o?this.host.registry.get(s,i):void 0;this.host.registry.set(s,i,t,r?{host:!0}:void 0),o&&a!==void 0&&o.undo.push(()=>this.host.registry.set(s,i,a,{host:!0}))}}snapshot(){return{rngState:this.rngState,visits:Object.fromEntries(this.visitCounts),cursor:{flowEnded:this.flowEnded,currentSceneId:this.currentSceneId,stack:this.stack.map(e=>{let t=this.childrenOf(e.containerId)?.[e.index];return t?{...e,nextId:t.id}:{...e}}),activeSnippetId:this.activeSnippet?.id??null,beatIndex:this.beatIndex,pendingChoice:this.pendingChoice?{groupId:this.pendingChoice.groupId,options:this.pendingChoice.options.map(e=>({...e}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:kt(this.pendingPrompt.shown)}:{},selectors:Ue(this.selectors)}}}restore(e){if(this.host.journal)throw new Error("a flow can\'t be restored while a checkpoint is open");this.restoreCursor(e),this.releaseBags(!0),this.mountLocal();for(let t of new Set([this.currentSceneId,...this.stack.map(r=>r.sceneId)]))t!==null&&this.host.bundle.scenes[t]&&this.ensureSceneBags(t)}restoreCursor(e){this.rngState=O(e.rngState),this.visitCounts=new Map(Object.entries(e.visits??{}));let t=e.cursor;if(this.started=!0,this.flowEnded=t.flowEnded,this.beatIndex=t.beatIndex,this.currentSceneId=t.currentSceneId,this.stack=t.stack.map(r=>{let{nextId:s,...i}=r;if(s!==void 0){let o=this.childrenOf(i.containerId)?.findIndex(a=>a.id===s)??-1;if(o>=0)return{...i,index:o}}return{...i}}),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=We(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let r=new Map,s=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);if(!o)continue;r.set(i.id,o);let{prompt:a,...c}=i,l=a?.kind==="line"&&a.speakerLabel===void 0?j(this.host,a.character,a.qualifier):void 0;s.push({...c,...a?{prompt:{...a,padAfter:a.padAfter??this.promptPadOf(o),...l!==void 0?{speakerLabel:l}:{}}}:{}})}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:r})}if(this.pendingPrompt=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId),s=r?this.authoredPromptOf(r):void 0,i=t.pendingPrompt,o=i?.kind==="line"&&i.speakerLabel===void 0?j(this.host,i.character,i.qualifier):void 0;s?this.pendingPrompt={beat:s,...i?{shown:{...i,padAfter:i.padAfter??this.padOf(s.id),...o!==void 0?{speakerLabel:o}:{}}}:{}}:this.pendingPromptOwnerId=null}}enterSceneSetup(e){let t=this.host.bundle.scenes[e];if(!t)throw new Error(`unknown scene: ${e}`);this.currentSceneId=e,this.enter(e),this.seedScene(t),this.runEffects(t.onEntry,t.id)}enterChild(e){if(this.enter(e.id),e.type==="snippet"){this.beginSnippet(e);return}let t=e.selector??"run";if(t==="run"){this.stack.push({sceneId:this.currentSceneId,containerId:e.id,index:0});return}if(t==="choice"){this.setupChoice(e);return}let r=this.selectChild(e);r&&this.enterChild(r)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let r=this.host.nodeIndex.get(e);if(r&&r.type==="group")return r.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],r=new Map,s=[];for(let o of e.children){if(o.fallback===!0){s.push(o);continue}if(o.sticky!==!0&&(this.visitCounts.get(o.id)??0)>=1)continue;let a=this.eligible(o),c=o.secretUntilEligible===!0;!a&&c||(t.push({id:o.id,prompt:this.promptFor(o),eligible:a,gameData:o.gameData}),r.set(o.id,o))}if(t.some(o=>o.eligible)){this.host.tracing&&this.emit({type:"choice",group:e.id,options:t.map(o=>({id:o.id,eligible:o.eligible}))}),this.pendingChoice={groupId:e.id,options:t,byId:r};return}let i=s.find(o=>this.eligible(o));if(i){this.enterChild(i);return}this.host.tracing&&this.emit({type:"dry",group:e.id}),this.host.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(this.host.tracing&&this.emit({type:"jump",to:e,mode:t}),e==="END"){this.flowEnded=!0,this.stack=[];return}let r,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}r=e,s=a.id}else{let a=this.host.blockIndex.get(e);if(!a)throw new Error(`jump target not found: ${e}`);a.sceneId!==this.currentSceneId&&this.enterSceneSetup(a.sceneId),r=a.sceneId,s=e}this.enter(s);let o={sceneId:r,containerId:s,index:0};t==="call"?this.stack.push(o):this.stack=[o]}selectChild(e){let t=e.children.map(c=>({id:c.id,eligible:this.eligible(c)})),r=e.children.filter((c,l)=>t[l].eligible),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(this.host.tracing&&this.emit({type:"select",group:e.id,selector:e.selector??"default",...e.selector==="sequence"?{order:s,exhaust:i}:{},children:t,picked:c?.id??null}),c);if(r.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(r[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(r,i,a):s==="specificity"?this.pickSpecificity(r,i,a):this.pickSequential(r,i,a));default:return null}}pickSequential(e,t,r){let s=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,r){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(g=>g.id),a=new Set(e.map(g=>g.id));if(r.bag===void 0&&(r.bag=o()),!r.bag.some(g=>a.has(g))){if(t==="once")return null;if(i){let g=e[s-1];return r.last=g.id,g}if(r.bag=o(),r.bag.length===0)return null}let c=r.bag.filter(g=>a.has(g)),l=r.last!==void 0&&c.length>1?c.indexOf(r.last):-1,p=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&p>=l&&p++;let h=c[p];return r.bag.splice(r.bag.indexOf(h),1),r.last=h,e.find(g=>g.id===h)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(p=>p.id));let l=new Set(r.bag);if(s=e.filter(p=>l.has(p.id)),s.length===0)return t==="stick"&&r.last!==void 0?e.find(p=>p.id===r.last)??null:null}let i=-1,a=s.map(l=>{let p=this.specScore(l);return p>i&&(i=p),{c:l,s:p}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=r.last!==void 0?a.findIndex(h=>h.id===r.last):-1,p=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&p>=l&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(l=>l!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,r){return ye(e,i=>{try{return He($(i,this.context(),re))}catch(o){return this.reportError("best-match",r.id,r.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=this.host.journal,s;if(r&&(s=e.shared?r.selectors:r.flowSelectors.get(this),s||(s=new Set,r.flowSelectors.set(this,s))),r&&s&&!s.has(e.id)){s.add(e.id);let o=t.get(e.id),a=o?{...o,...o.bag?{bag:[...o.bag]}:{}}:void 0;r.undo.push(()=>{a?t.set(e.id,a):t.delete(e.id)})}let i=t.get(e.id);return i||(i={},t.set(e.id,i)),i}runEffects(e,t){for(let r of e??[]){let s;try{s=this.evalExpr(r.value);let i=this.host.tracing?this.getProperty(r.target):void 0;this.writeProperty(r.target,s,!1),this.host.tracing&&this.emit({type:"write",target:r.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,r.value,i)}}}eligible(e){if(!e.condition)return!0;try{return He(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,r,s){let i=s instanceof Error?s.message:String(s),o=r?.src;this.host.onError({flow:this.id,kind:e,node:t,...o?{source:o}:{},message:i}),this.host.tracing&&this.emit({type:"diagnostic",kind:e,node:t,...o?{source:o}:{},message:i})}evalExpr(e){return $(this.conditionAst(e),this.context(),re)}conditionAst(e){let t=Ne.get(e);return t||(t=E(e.ast),Ne.set(e,t)),t}enter(e){let t=this.visitCounts,r=t.get(e);t.set(e,(r??0)+1);let s=this.host.sharedVisits,i=s.get(e);s.set(e,(i??0)+1),this.host.journal&&this.host.journal.undo.push(()=>{r===void 0?t.delete(e):t.set(e,r),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=ee(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=I(this.host,e.id),r=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:e.gameData,...r};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),padAfter:this.padOf(e.id),gameData:e.gameData,...r};case"line":{let s=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?s:this.interpolate(s)),c=i&&a.length===0;return{type:"line",id:e.id,text:a,character:c?void 0:e.character,characterName:c?void 0:this.resolveCharacterName(e.character),direction:c?void 0:e.direction,qualifier:c?void 0:e.qualifier,qualifierName:c?void 0:this.resolveQualifierName(e.qualifier),speakerLabel:c?void 0:j(this.host,e.character,e.qualifier),padAfter:this.padOf(e.id),gameData:e.gameData,...r}}}}interpolate(e){return De(e,t=>this.getProperty(t))}stripCaptions(e){return Ce(e,this.host.captionOpen,this.host.captionClose)}captionLine(e){return this.host.captionsOn?e:this.stripCaptions(e)}promptFor(e){let t=this.promptBeatOf(e);if(!t)return;let r=this.interpolate(this.resolveString(t.id)),s=this.padOf(t.id);return t.kind==="line"?{kind:"line",text:this.captionLine(r),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction,qualifier:t.qualifier,qualifierName:this.resolveQualifierName(t.qualifier),speakerLabel:j(this.host,t.character,t.qualifier),padAfter:s}:{kind:"text",text:r,padAfter:s}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let r=I(this.host,e.id),s=r&&r.length?{tags:r}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,padAfter:this.padOf(e.id),gameData:e.gameData,...s}:{type:"line",id:e.id,text:t.text,character:t.character,characterName:t.characterName,direction:t.direction,qualifier:t.qualifier,qualifierName:t.qualifierName,speakerLabel:t.speakerLabel,padAfter:this.padOf(e.id),gameData:e.gameData,...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(r=>r.kind==="line"||r.kind==="text")}firstTextSnippetIn(e){let t;return D(e,r=>{!t&&r.type==="snippet"&&(r.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=r)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let r=this.host.defaultStrings[e];return r!==void 0?`<Untranslated: ${e}> ${r}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=K(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}promptPadOf(e){let t=this.promptBeatOf(e);return t?this.padOf(t.id):x}padOf(e){return this.host.padIndex.get(e)?.resolved??x}resolveQualifierName(e){if(e===void 0||this.host.emitIds)return;let t=J(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return Je(this.host,e)}newLocal(){return new b(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=y.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:G}),this.registered.add(e)}releaseBags(e){for(let t of this.registered)this.host.registry.remove(t,{keep:e});this.registered.clear(),this.sceneBags.clear()}sceneBagFor(e,t){if(this.host.bundle.scenes[e])return this.ensureSceneBags(e),this.host.sceneSharedNames.get(e)?.has(t.toLowerCase())?this.host.stageBags.get(e):this.sceneBags.get(e)}ensureSceneBags(e){let t=this.host.sceneSharedNames.get(e)??new Set,r=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal;if(!this.sceneBags.has(e)){let i=new b(r.filter(c=>!t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),o=y.flowScene(this.id,e),a=qe(this.host.registry,o,i,s!==null);this.registered.add(o),this.sceneBags.set(e,i),s&&s.undo.push(()=>{this.sceneBags.get(e)===i&&(this.host.registry.remove(o,a?{keep:!0}:void 0),this.registered.delete(o),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let i=new b(r.filter(a=>t.has(a.name.toLowerCase())),{pathPrefix:"@scene."}),o=qe(this.host.registry,y.stage(e),i,s!==null);this.host.stageBags.set(e,i),s&&s.undo.push(()=>{this.host.stageBags.get(e)===i&&(this.host.registry.remove(y.stage(e),o?{keep:!0}:void 0),this.host.stageBags.delete(e))})}}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let r of e.sceneProps??[]){if(!r.temporary)continue;let s=r.name.toLowerCase(),i=t.has(s)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);if(!i)continue;let o=this.host.journal?i.get(s):void 0;o!==void 0&&this.host.journal.undo.push(()=>i.set(s,o)),i.set(s,B(r))}}};function Ve(n,e){D(n,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let r of t.beats??[])r.kind==="line"&&r.character&&e.add(r.character)})}function j(n,e,t,r=!1){if(!e||n.emitIds)return;let s=c=>(r?void 0:n.strings[c])??n.defaultStrings[c],i=s(K(e))??n.castDisplay.get(e)??e;if(t===void 0)return i;let o=n.speakerLabels.get(`${e}\\0${t}`);if(o!==void 0)return s(Ie(e,t))??o;let a=s(J(t))??n.qualifierDisplay.get(t)??t;return Oe(s(Pe)??n.speakerPattern,i,a)}function Me(n){return n&&Object.keys(n).length?{gameData:n}:{}}function kt(n){let e={kind:n.kind,text:n.text};return n.character!==void 0&&(e.character=n.character),n.characterName!==void 0&&(e.characterName=n.characterName),n.direction!==void 0&&(e.direction=n.direction),n.qualifier!==void 0&&(e.qualifier=n.qualifier),n.qualifierName!==void 0&&(e.qualifierName=n.qualifierName),n.speakerLabel!==void 0&&(e.speakerLabel=n.speakerLabel),typeof n.padAfter=="number"&&(e.padAfter=n.padAfter),e}function Ue(n){let e={};for(let[t,r]of n){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e[t]=s}return e}function We(n){let e=new Map;for(let[t,r]of Object.entries(n??{})){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e.set(t,s)}return e}function qe(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:G}),r&&JSON.stringify(t.save())!==s}function $e(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function _e(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function se(n,e,t){let r=n.qualityLadders.get(e);if(!r){r=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!r.has(i)&&r.set(i,s.stages)}n.qualityLadders.set(e,r)}return r}function Ke(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function Et(n,e){let t=Ke(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function ie(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function oe(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Je(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=Ee(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function Ct(n){let e={};n.shared?.patter&&(e.patter=n.shared.patter);for(let[t,r]of Object.entries(n.stageBags??{}))e[y.stage(t)]=r;for(let[t,r]of Object.entries(n.flows??{}))Object.assign(e,Qe(t,r));return e}function Qe(n,e){let t={};e.scopes?.patter&&(t[y.flowGlobals(n)]=e.scopes.patter);for(let[r,s]of Object.entries(e.sceneBags??{}))t[y.flowScene(n,r)]=s;return t}function Dt(n,e){let t={scopes:{},sceneBags:{}},r=y.flow(n);for(let[s,i]of Object.entries(e)){if(!s.startsWith(r))continue;let o=s.slice(r.length);o==="patter"?t.scopes.patter=i:o.startsWith("scene/")&&(t.sceneBags[xt(o.slice(6))]=i)}return t}function He(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var It=(n,e)=>n.shared??e;function ae(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:It(n,e)}}function Pt(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function Rt(n,e){e.blocks++;let t=[...n.children];for(;t.length;){let r=t.pop();if(r.type==="group"){e.groups++,r.prompt&&e.prompts++,t.push(...r.children);continue}e.snippets++;for(let s of r.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function ze(n){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:n.cast?.length??0},t=[],r=[];for(let o of Object.values(n.scenes)){e.scenes++;let a=w(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:w(c),name:c.name}))});for(let c of o.blocks)Rt(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>ae(c,!1))})}let s=(n.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>ae(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(Pt)}));return{identity:{schema:n.schema,project:n.content.project,...n.content.version!==void 0?{version:n.content.version}:{},...n.content.hash!==void 0?{hash:n.content.hash}:{},...n.content.structureHash!==void 0?{structureHash:n.content.structureHash}:{},voiced:n.voiced,defaultLocale:n.locales.default,locales:[...n.locales.included],localisation:n.localisation?.mode??"embedded",sourceDebug:n.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(n.properties??[]).map(o=>ae(o,!0)),scene:r},gameData:i,counts:e}}function Ye(n,e){return n.gameDataFields?.[e]??[]}function ce(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function Xe(n,e){let t={};for(let r of n){let s=ce(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))r in t||(t[r]=s);return t}return at(Ot);})();\n';
1543
+ var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var re=Object.defineProperty;var at=Object.getOwnPropertyDescriptor;var ct=Object.getOwnPropertyNames;var lt=Object.prototype.hasOwnProperty;var pt=(n,e)=>{for(var t in e)re(n,t,{get:e[t],enumerable:!0})},dt=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of ct(e))!lt.call(n,s)&&s!==t&&re(n,s,{get:()=>e[s],enumerable:!(r=at(e,s))||r.enumerable});return n};var ut=n=>dt(re({},"__esModule",{value:!0}),n);var jt={};pt(jt,{Checkpoint:()=>_,DEFAULT_PAD_AFTER:()=>x,Engine:()=>ee,Flow:()=>P,buildTagIndex:()=>Z,compareStages:()=>O,describeBundle:()=>tt,effectiveGameData:()=>nt,gameDataFields:()=>rt,gameDataValue:()=>ge,nextStage:()=>B,stageIndex:()=>R});function A(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function E(n){switch(n[0]){case"b":return{kind:"bool",value:n[1]};case"n":return{kind:"number",value:n[1]};case"s":return{kind:"string",value:n[1]};case"sv":return{kind:"scopedvar",scope:n[1],name:n[2]};case"u":return{kind:"unary",op:n[1],operand:E(n[2])};case"bin":return{kind:"binary",op:n[1],left:E(n[2]),right:E(n[3])};case"call":{let e=n.slice(2).map(E);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var u=class extends Error{constructor(e){super(e),this.name="EvalError"}};function W(n,e,t){let r=new Map(t.scopes.map(i=>[i.token,i.missing??"false"])),s=i=>{switch(i.kind){case"bool":return i.value;case"number":return i.value;case"string":return i.value;case"scopedvar":{let o=A(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):A(o,i.name);if(a===void 0){if(r.get(i.scope)==="throw")throw new u(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!A(t.functions,i.name)){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new u(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=ne(a,e);if(c===void 0)throw new u("advance() needs a quality reference (@scope.name of a quality property)");let l=b(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=A(t.functions,i.name);if(!o)throw new u(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new u("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=s(i.operand);if(typeof a!="boolean")throw new u(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new u(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let h=s(i.left);if(typeof h!="boolean")throw new u(`\'and\' requires boolean operands, left is ${typeof h}`);if(!h)return!1;let g=s(i.right);if(typeof g!="boolean")throw new u(`\'and\' requires boolean operands, right is ${typeof g}`);return g}if(i.op==="or"){let h=s(i.left);if(typeof h!="boolean")throw new u(`\'or\' requires boolean operands, left is ${typeof h}`);if(h)return!0;let g=s(i.right);if(typeof g!="boolean")throw new u(`\'or\' requires boolean operands, right is ${typeof g}`);return g}let o=s(i.left),a=s(i.right),c=ne(i.left,e),l=ne(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!gt(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new u(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return b(o,p,">")>b(a,p,">");case">=":return b(o,p,">=")>=b(a,p,">=");case"<":return b(o,p,"<")<b(a,p,"<");case"<=":return b(o,p,"<=")<=b(a,p,"<=");case"+":case"-":case"*":case"/":throw new u(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return xe(o,a);case"!=":return!xe(o,a);case">":return C(o,a,">"),o>a;case">=":return C(o,a,">="),o>=a;case"<":return C(o,a,"<"),o<a;case"<=":return C(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new u(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return C(o,a,"-"),o-a;case"*":return C(o,a,"*"),o*a;case"/":if(C(o,a,"/"),a===0)throw new u("division by zero");return o/a}}}};return s(n)}function xe(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;let t=[...n].sort(),r=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==r[s])return!1;return!0}return n===e}function C(n,e,t){if(typeof n!="number"||typeof e!="number")throw new u(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function ne(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function b(n,e,t){if(typeof n!="string")throw new u(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new u(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var gt=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function F(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function se(n){let e=F(n);return{next(){e=e+1831565813>>>0;let t=Math.imul(e^e>>>15,1|e);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296},state(){return e}}}var oe=n=>Array.isArray(n)?n:n.stages??[],ft=n=>Array.isArray(n)?"flags":n==null?"nothing":typeof n;function R(n,e){return typeof e=="string"?oe(n).indexOf(e):-1}function ie(n,e){if(typeof e!="string")throw new u(`a stage is a name, got ${ft(e)}`);let t=n.indexOf(e);if(t<0)throw new u(`"${e}" is not a stage of this quality (stages: ${n.join(", ")})`);return t}function O(n,e,t){let r=oe(n),s=ie(r,e)-ie(r,t);return s<0?-1:s>0?1:0}function B(n,e){let t=oe(n);return t[Math.min(ie(t,e)+1,t.length-1)]}var ht={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},mt=[ht];function ke(n,e,t){let r=t?.countingCalls??mt;return K(n,t?.want??!0,e,r)}function K(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=K(n.left,e,t,r),i=K(n.right,e,t,r);return n.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(n.kind==="unary"&&n.op==="not")return K(n.operand,!e,t,r);if(n.kind==="call"){let s=r.find(i=>i.name===n.name);if(s){let i=s.count(n),o=t(n);return e?o?i:0:o?0:1}}return t(n)===e?1:0}var v=class n{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let r=this.norm(t.name);this.decls.set(r,t),this.values[r]=structuredClone(t.default??N(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,r){let s=this.norm(e);if(!r?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:r?.silent??!1,reason:r?.reason};this.values[s]=t;for(let o of[...this.auditors])o(i);if(!i.silent)for(let o of[...this.subscribers])o(i);return i}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>Ce(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm,pathPrefix:this.pathPrefix});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e){for(let[t,r]of Object.entries(e))this.values[this.norm(t)]=r}};function Ce(n,e,t,r,s=""){let i=r??n.name.toLowerCase();return{name:i,path:s+i,type:n.type,value:e,default:n.default??N(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var St=n=>n.toLowerCase(),ae=1,Q=class{scopes=new Map;parked=new Map;rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},i=new v(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,r){this.assertFree(e,r?.owner),this.scopes.set(e,{kind:"owned",bag:t,...r?.owner!==void 0?{owner:r.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(t.load(s),this.parked.delete(e)),this}remove(e,t){let r=this.scopes.get(e);if(!r)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&r.kind==="owned"&&this.parked.set(e,r.bag.save()),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&this.parked.delete(t);return this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??St,a=new Map;for(let c of r)a.set(o(c.name),c);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:i.writable??!0,norm:o,...i.owner!==void 0?{owner:i.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(r)return r.kind==="owned"?r.bag.get(t):r.resolver.get(r.norm(t))}set(e,t,r,s){let i=this.scopes.get(e);if(!i)throw new Error(`unknown scope \'@${e}\'`);if(i.kind==="owned"){if(!s?.host&&!i.bag.writable(t))throw new Error(`\'@${e}.${t}\' is read-only`);i.bag.set(t,r,s?.host?{host:!0}:void 0);return}let o=i.norm(t);if(!i.resolver.set)throw new Error(`\'@${e}.${t}\' is read-only`);if(!s?.host&&!this.foreignWritable(i,o))throw new Error(`\'@${e}.${t}\' is read-only`);i.resolver.set(o,r)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,r]of this.scopes){let s=r.owner!==void 0?{owner:r.owner}:{};if(r.kind==="owned")for(let i of r.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of r.decls)e.push({scope:t,...s,...Ce(o,r.resolver.get(i),this.foreignWritable(r,i),i,`${t}.`)})}return e}toEvalContext(e,t){let r=this.view(t?.aliases),s={};for(let[o,a]of r)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(r);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=r.get(o);return c?i.get(o)?.get(yt(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[r,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${r}\' names \'${s}\', which is not registered`);t.set(r,i)}return t}qualityLadders(e){let t=new Map;for(let[r,s]of e)for(let[i,o]of Ee(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(r);a||(a=new Map,t.set(r,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[r,s]of this.view(e?.aliases)){let i=Ee(s);if(i.length===0)continue;let o=new Map;for(let[a,c]of i)o.set(a,{type:c.type,enumValues:c.values,...c.stages!==void 0?{stages:c.stages}:{}});t.set(r,o)}return{properties:t}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());for(let[t,r]of this.parked)e[t]=structuredClone(r);return e}load(e,t){t?.keepParked||this.parked.clear();for(let[r,s]of Object.entries(e)){let i=this.scopes.get(r);i?.kind==="owned"?i.bag.load(s):i||this.parked.set(r,structuredClone(s))}}saveFragment(){return{version:ae,scopes:this.save()}}loadFragment(e){if(e.version!==ae)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${ae})`);this.load(e.scopes)}assertFree(e,t){let r=this.scopes.get(e);if(!r)return;let s=r.owner!==void 0?` by ${r.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function Ee(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function yt(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function N(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??"";default:return!1}}var De=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function T(n){return n.ctx.host??{}}var ce={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new u("random(a, b) requires exactly 2 arguments");let t=T(e).nextRandom;if(!t)throw new u("random() called without a PRNG in context");let r=e.evaluate(n[0]),s=e.evaluate(n[1]);if(typeof r!="number"||typeof s!="number")throw new u("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new u("random(a, b) arguments must be integers");let i=Math.min(r,s),o=Math.max(r,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:Pe("check_flags"),eval(n,e){let t=Ie(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new u("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:Pe("set_flags"),eval(n,e){let t=[...Ie(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new u("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let i=t.indexOf(s.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:z("visits"),eval:(n,e)=>T(e).visits?.(J(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:z("seen"),eval:(n,e)=>(T(e).visits?.(J(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:z("patter_visits"),eval:(n,e)=>T(e).patterVisits?.(J(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:z("patter_seen"),eval:(n,e)=>(T(e).patterVisits?.(J(n,e,"patter_seen"))??0)>0}}},ir=De.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function Re(n,e){let t=n.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function J(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new u(`${t}(id) requires a string node id`);return r}function z(n){return(e,t)=>{let r=e[0];r&&r.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(id): the argument must be a string id literal (a scene / block / node id)`})}}function Ie(n,e,t){if(!n)throw new u(`${t}() requires at least one argument (the flags variable)`);let r=e.evaluate(n);if(Array.isArray(r))return r;if(r===!1||r===null||r===void 0)return[];throw new u(`${t}() first argument must be a flags property`)}function Pe(n){return(e,t)=>{if(e.length===0)return;let r=e[0];if(r.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(r.scope)?.get(r.name);if(s&&s.type!=="flags"){let i=r.scope===t.defaultScope?r.name:`${r.scope}.${r.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): \'@${i}\' is not a flags property (got ${s.type})`});return}for(let i=1;i<e.length;i++){let o=e[i];o.kind!=="flagdelta"?t.report({path:[...t.path,"args",i],kind:"wrong-arg-type",severity:"error",message:`${n}(): argument ${i+1} must be +flagName or -flagName`}):s?.type==="flags"&&s.enumValues&&!s.enumValues.includes(o.name)&&t.report({path:[...t.path,"args",i],kind:"unknown-flag-name",severity:"error",message:`${n}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var wt=/^@[A-Za-z0-9_.]+$/;function*bt(n){let e="",t=0;for(;t<n.length;){let r=n[t];if(r==="{"&&n[t+1]==="{"){e+="{",t+=2;continue}if(r==="}"&&n[t+1]==="}"){e+="}",t+=2;continue}if(r==="{"){let s=n.indexOf("}",t+1);if(s!==-1){let i=n.slice(t,s+1),o=n.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:wt.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=r,t+=1}e&&(yield{kind:"text",value:e})}function vt(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function xt(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function kt(n){let e="",t=!1;for(let r of n){if(xt(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function Oe(n,e,t){if(e.length===0||n.indexOf(e)<0)return n;let r="",s=0,i=!1;for(;s<n.length;){if(n.startsWith(e,s)){let o=n.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}r+=n.slice(s);break}r+=n[s],s+=1}return i?kt(r):n}function Be(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of bt(n)){if(r.kind==="text"){t+=r.value;continue}if(!r.ref){t+=r.raw;continue}let s=e(r.ref);t+=s===void 0?"":vt(s)}return t}function D(n,e){for(let t of n){e(t);let r=t.children;r&&D(r,e)}}function Et(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function w(n){let e=n.gameId?.trim();return e||Et(n.name)}var x=.6;function Y(n){return`cast:${n}`}function X(n){return`qualifier:${n}`}function Ae(n,e){return`speaker:${n}:${e}`}var Fe="speaker:pattern",Ne="{name} ({qualifier})";function Te(n,e,t){return n.split("{name}").map(r=>r.split("{qualifier}").join(t)).join(e)}var le={open:"[",close:"]"},Le="SFX";var L=3;function j(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function Z(n){let e=new Map,t=(r,s)=>{let i=j([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,j([...i,...r.prompt.tags??[]])),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,j([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=j(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=j([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var je=n=>n.kind==="line"||n.kind==="text",V=n=>typeof n=="number"&&Number.isFinite(n)?n:void 0;function Ve(n){let e=new Map,t=(i,o,a)=>{let c=i.kind==="gameEvent"?void 0:V(i.padAfter),l=c??o;a&&l<0&&(l=0),e.set(i.id,c!==void 0?{own:c,resolved:l}:{resolved:l})},r=(i,o)=>{let a=V(i.padAfterDefault)??o;if(i.type==="group"){i.prompt&&t(i.prompt,a,!1);for(let p of i.children)r(p,a);return}let c=i.beats??[],l=-1;c.forEach((p,h)=>{je(p)&&(l=h)}),c.forEach((p,h)=>{je(p)&&t(p,a,h===l)})},s=V(n.padAfterDefault)??x;for(let i of Object.values(n.scenes)){let o=V(i.padAfterDefault)??s;for(let a of i.blocks){let c=V(a.padAfterDefault)??o;for(let l of a.children)r(l,c)}}return e}var Ge=new WeakMap,qe=new WeakMap;function Ct(n){let e=qe.get(n);return e||(e=Z(n),qe.set(n,e)),e}var Me=new WeakMap;function Dt(n){let e=Me.get(n);return e||(e=Ve(n),Me.set(n,e)),e}var $e=new WeakMap;function It(n){let e=$e.get(n);if(e)return e;e=new Map;for(let t of Object.values(n.scenes))for(let r of t.blocks)D(r.children,s=>{if(s.type==="group"){s.prompt&&e.set(s.prompt.id,s.prompt);return}for(let i of s.beats??[])e.set(i.id,i)});return $e.set(n,e),e}function I(n,e){return n.tagIndex.get(e)?.slice()}var M="Patter",G=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),Pt=n=>n.replace(/%2F/g,"/").replace(/%25/g,"%"),_e=Symbol("ownsRegistry"),y={stage:n=>`patter/scene/${G(n)}`,flow:n=>`patter/flow/${G(n)}/`,flowGlobals:n=>`patter/flow/${G(n)}/patter`,flowScene:(n,e)=>`patter/flow/${G(n)}/scene/${G(e)}`},_=class{constructor(e){this.journal=e}journal},ee=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;engineLog=[];engineSeq=0;engineTraceHandlers=new Set;constructor(e,t={}){this.creationOptions=t;let r=t.locale??e.locales.default,s=e.strings;this.allStrings=s,this.currentLocale=r;let i=s[r]??{},o=s[e.locales.default]??{},a=e.localisation,c=a?.mode==="ids"&&!a.sourceDebug;this.sourceDebug=a?.mode==="ids"&&!!a.sourceDebug,this.sourceDebug&&typeof console<"u"&&console.warn("[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.");let l=new Map;for(let d of e.cast??[])d.displayName&&l.set(d.name,d.displayName);let p=new Map;for(let d of e.qualifiers??[])d?.gameId&&p.set(d.gameId,d.name);let h=new Map;for(let d of e.cast??[])for(let[f,S]of Object.entries(d.qualifierLabels??{}))typeof S=="string"&&h.set(`${d.name}\\0${f}`,S);let g=e.speakerPattern??Ne;this.defaultSeed=(t.seed??2654435769)>>>0;let fe=new Map,he=new Map,me=new Map;for(let[d,f]of Object.entries(e.scenes)){this.sceneGameIdToId.set(w(f),d);let S=new Map;for(let m of f.blocks)he.set(m.id,{sceneId:d}),me.set(m.id,m),S.set(w(m),m.id),D(m.children,U=>fe.set(U.id,U));this.blockGameIdToId.set(d,S)}let Se=e.properties??[],te=Se.filter(d=>d.shared??!0).map(Ke),st=Se.filter(d=>!(d.shared??!0)).map(Ke),it=new Set(te.map(d=>d.name.toLowerCase())),k=t.registry??new Q,ye=!t.registry||t[_e]===!0,we=new v(te,{pathPrefix:"@patter."}),be=[],H=[];try{k.mountOwned("patter",we,{owner:M}),H.push("patter");let d=new Set;for(let[f,S]of Object.entries(Qe(t))){let m=e.scopeRegistry?.scopes.find(ot=>ot.token===f),U=(m?.declarations??[]).map(Xe);k.defineForeign(f,S,U,{writable:m?.writable??!0,owner:M}),H.push(f),d.add(f)}for(let f of ye?e.scopeRegistry?.scopes??[]:[]){if(d.has(f.token)||k.has(f.token))continue;let S=(f.declarations??[]).map(m=>Bt(m,f.writable));k.defineOwned(f.token,S,{owner:M}),H.push(f.token),be.push(f.token)}}catch(d){for(let f of H)k.remove(f,{keep:!0});throw d}let ve=new Map;for(let[d,f]of Object.entries(e.scenes)){let S=new Set((f.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));ve.set(d,S)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(d,f,S)=>this.emitEngine(d,f,S),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:p,speakerLabels:h,speakerPattern:g,nodeIndex:fe,blockIndex:he,blockById:me,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:Ct(e),padIndex:Dt(e),registry:k,ownsRegistry:ye,patterBag:we,hostScopes:be,patterSharedDecls:te,patterLocalDecls:st,patterSharedNames:it,sceneSharedNames:ve,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(d=>console.warn(`Patterplay: ${d.kind} on ${d.node} in flow \'${d.flow}\' failed, played through: ${d.message}`)),replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??le).open,captionClose:(e.closedCaptions??le).close,captionCharacter:e.closedCaptions?.character||Le,refSplitCache:new Map,refSplitRevision:k.revision}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}get buildId(){return this.host.bundle.content?.hash??""}setLocale(e){this.currentLocale=e,this.host.strings=this.allStrings[e]??{}}replaceStrings(e){this.allStrings=e.strings,this.host.strings=this.allStrings[this.currentLocale]??{},this.host.defaultStrings=this.allStrings[this.host.bundle.locales.default]??{}}hotSwap(e){this.refuseInCheckpoint("hotSwap");let t=this.saveGame(),r=o=>(o.setLocale(this.currentLocale),o.setClosedCaptions(this.host.captionsOn),o),s=()=>new n(e,{...this.creationOptions,registry:this.host.registry,[_e]:this.host.ownsRegistry});this.release(!0);let i=s();try{return i.loadGame(t),r(i)}catch{i.release(!0);let o=s();for(let[a,c]of Object.entries(t.flows)){let l=c.cursor.currentSceneId;try{o.openFlow(a,l!==null?{scene:l}:{})}catch{}}return r(o)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:r,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new P(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(r,s),a}runFlow(e,t,r){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:r}).advanceToStop().played;if(!s.goto(t,r))throw new Error(`runFlow: address not found: ${t}${r===void 0?"":` / ${r}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let r=pe(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=de(this.host,r,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:r,blockId:s}}if(t!==void 0){if(!this.host.blockIndex.has(t))throw new Error(`unknown block: ${t}`);return{blockId:t}}if(Object.keys(this.host.bundle.scenes).length===0)throw new Error("no scenes in bundle");return{}}resolveSceneRef(e){if(e!=null)return pe(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:de(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?w(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?w(t):void 0}tagsForBeat(e){return I(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?I(this.host,t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?I(this.host,s):void 0)??[]}gameDataForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.gameData}}gameDataForBlock(e,t){let r=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(r!=null?this.host.blockById.get(r):void 0)?.gameData}}getCast(){let e=[];for(let t of this.host.bundle.cast??[])t?.name&&e.push(t.name);return e}castForScene(e){let t=this.resolveSceneRef(e),r=t!=null?this.host.bundle.scenes[t]:void 0;if(!r)return[];let s=new Set;for(let i of r.blocks)He(i.children,s);return[...s]}castForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return He(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...w(e)?{gameId:w(e)}:{},name:e.name,...Ue(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...w(t)?{gameId:w(t)}:{},name:t.name,...Ue(t.gameData),...this.tagsField(t.id),children:t.children.map(r=>this.outlineNode(r))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let r of t.blocks)D(r.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:r.id,snippetId:s.id,beat:this.beatInfo(i)})});return e}outlineNode(e){return e.type==="group"?{type:"group",id:e.id,...this.tagsField(e.id),...e.selector?{selector:e.selector}:{},...e.prompt?{prompt:this.beatInfo(e.prompt)}:{},children:e.children.map(t=>this.outlineNode(t))}:{type:"snippet",id:e.id,...this.tagsField(e.id),beats:(e.beats??[]).map(t=>this.beatInfo(t)),...e.jump?{jumpTo:e.jump.to,...e.jump.mode?{jumpMode:e.jump.mode}:{}}:{}}}beatInfo(e){let t=I(this.host,e.id),r={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){r.character=e.character;let i=this.host.defaultStrings[Y(e.character)]??this.host.castDisplay.get(e.character);i!==void 0&&(r.characterName=i)}if(e.direction!==void 0&&(r.direction=e.direction),e.qualifier!==void 0){r.qualifier=e.qualifier;let i=this.host.defaultStrings[X(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);i!==void 0&&(r.qualifierName=i)}let s=q(this.host,e.character,e.qualifier,!0);s!==void 0&&(r.speakerLabel=s)}if(e.kind==="line"||e.kind==="text"){let s=this.host.padIndex.get(e.id);r.padAfter=s?.resolved??x,s?.own!==void 0&&(r.ownPadAfter=s.own);let i=this.host.defaultStrings[e.id];i!==void 0&&(r.text=i)}return e.gameData&&Object.keys(e.gameData).length&&(r.gameData=e.gameData),t&&t.length&&(r.tags=t),r}tagsField(e){let t=I(this.host,e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}listBags(){let e=[{bag:this.host.patterBag}];for(let[t,r]of this.host.stageBags)e.push({bag:r,pathPrefix:`@scene:${t}.`});return e}checkpoint(){if(this.host.journal)throw new Error("a checkpoint is already open: roll it back or commit it first");let e={undo:[],flows:new Set,opened:new Set,selectors:new Set,flowSelectors:new Map};return this.host.journal=e,new _(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let r=t.length-1;r>=0;r--)t[r]();n.forget(e.journal)}commit(e){this.endCheckpoint(e),n.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}captureFlow(e){let t=this.flowsById.get(e);if(!t)throw new Error(`no open flow \'${e}\' to capture`);return{version:L,...t.snapshot(),...Ft(e,this.host.registry.save())}}restoreFlow(e,t){this.refuseInCheckpoint("restoreFlow");let r=t.version;if(r!==2&&r!==L)throw new Error(`unsupported flow state version: ${String(r)}`);let s=t.cursor;if(typeof s!="object"||s===null)throw new Error("malformed flow state: no cursor");if(this.flowsById.has(e))throw new Error(`a flow \'${e}\' is already open; close it before restoring another under that id`);this.assertExternalScopes();let i=y.flow(e);this.host.registry.discardParked(i),this.host.registry.load(et(e,t),{keepParked:!0});let o=new P(e,this.host,this.defaultSeed);try{o.restore(t)}catch(a){throw o.close(),this.host.registry.discardParked(i),a}return this.flowsById.set(e,o),o}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(y.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let r of this.flowsById.values())r.releaseBags(e),r.close();this.flowsById.clear();let t=this.host.registry;for(let r of this.host.stageBags.keys())t.remove(y.stage(r),{keep:e});this.host.stageBags.clear();for(let r of["patter",...this.host.hostScopes])t.has(r)&&t.remove(r,{keep:e});for(let r of Object.keys(Qe(this.creationOptions)))t.has(r)&&t.remove(r)}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.registry.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e),i=this.host.journal?this.host.registry.get(r,s):void 0;this.host.registry.set(r,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(r,s,i,{host:!0}))}stageIndex(e,t){return R(this.ladderOf(e),t)}compareStages(e,t,r){return O(this.ladderOf(e),t,r)}nextStage(e,t){return B(this.ladderOf(e),t)}ladderOf(e){let{scope:t,name:r}=this.splitShared(e),s=Ot(this.host,t,r);if(!s)throw new Error(`\'${e}\' is not a quality`);return s}log(){return this.engineLog}clearLog(){this.engineLog.length=0}onTrace(e){return this.engineTraceHandlers.add(e),this.host.tracing=!0,()=>{this.engineTraceHandlers.delete(e),this.host.tracing=this.host.logEnabled||this.engineTraceHandlers.size>0}}emitEngine(e,t,r){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...r?{scene:r}:{}})}getVisitCounts(){return Object.fromEntries(this.host.sharedVisits)}listProperties(){return this.host.patterSharedDecls.map(e=>({name:e.name,path:`@patter.${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:N(e),writable:e.writable??!0}))}splitShared(e){let t=Ze(this.host,e);if(t.scope==="scene")throw new Error(`\'${e}\': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);return t}save(){return{patter:this.host.patterBag.save()}}load(e){e.patter&&this.host.patterBag.load(e.patter)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:L,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:ze(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==L)throw new Error(`unsupported save version: ${String(t)}`);let r=e.flows;if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("malformed save: no flows");this.assertExternalScopes();let s=this.host.registry;for(let[o,a]of this.flowsById)a.releaseBags(o in e.flows),a.close();this.flowsById.clear();for(let o of this.host.stageBags.keys())s.remove(y.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.version===2?At(e):e.registry;i&&(this.host.ownsRegistry?s.load(i):s.load(i,{keepParked:!0})),this.host.sharedVisits.clear();for(let[o,a]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(o,a);this.host.sharedSelectors.clear();for(let[o,a]of Ye(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new P(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},P=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.sceneBagFor(r,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,r){this.id=e,this.host=t,this.rngState=F(r),this.local=this.newLocal(),this.evalCtx={scopes:{},host:{nextRandom:this.rng,visits:s=>this.visitCounts.get(s)??0,patterVisits:s=>this.host.sharedVisits.get(s)??0},qualities:(s,i)=>this.stagesFor(s,i)}}context(){let e=this.host.registry;if(e.revision!==this.ctxRevision){let t=e.toEvalContext(),r=this.evalCtx.scopes;for(let s of Object.keys(r))delete r[s];Object.assign(r,t.scopes),r.patter=this.patterResolver,r.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let r=t.toLowerCase();if(e==="patter")return $(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:$(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??$(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(r)}begin(e,t){if(this.host.journal&&!this.host.journal.opened.has(this))throw new Error("a flow can\'t be started or reset while a checkpoint is open");if(this.releaseBags(!1),this.host.registry.discardParked(y.flow(this.id)),this.mountLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.clearPending(),this.started=!0,t){let r=this.host.blockIndex.get(t);if(!r)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(r.sceneId),this.stack=[{sceneId:r.sceneId,containerId:t,index:0}],this.enter(t)}else{let r=e??Object.keys(this.host.bundle.scenes)[0],s=r?this.host.bundle.scenes[r]:void 0;if(!s)throw new Error(r?`unknown scene: ${r}`:"no scenes in bundle");this.enterSceneSetup(r);let i=s.blocks[0];i&&(this.stack=[{sceneId:r,containerId:i.id,index:0}],this.enter(i.id))}this.settle()}reset(e,t){this.begin(e,t)}start(e,t){this.reset(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=pe(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=de(this.host,r,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.begin(r,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.clearPending()}clearPending(){this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null}touch(){let e=this.host.journal;if(!e||e.flows.has(this))return;e.flows.add(this);let{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p}=this,h=this.stack.map(g=>({...g}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p,stack:h})})}get isClosed(){return this.closed}get currentScene(){return this.currentSceneId}advance(){if(this.closed)return{type:"end"};if(!this.started)throw new Error("flow has not been started");if(this.touch(),this.pendingPrompt){let e=this.pendingPrompt;return this.pendingPrompt=null,this.pendingPromptOwnerId=null,e.shown?this.promptResult(e.beat,e.shown):this.beatResult(e.beat)}return this.settle(),this.flowEnded?{type:"end"}:this.pendingChoice?{type:"choice",groupId:this.pendingChoice.groupId,options:this.pendingChoice.options}:this.activeSnippet?this.beatResult(this.activeSnippet.beats[this.beatIndex++]):(this.flowEnded=!0,{type:"end"})}advanceToStop(){let e=[];for(;;){let t=this.advance();if(t.type==="choice"||t.type==="end")return{played:e,stop:t};e.push(t)}}settle(){let e=0;for(;;){if(++e>1e4)throw new Error("flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content");if(this.flowEnded||this.pendingChoice)return;if(this.activeSnippet){if(this.beatIndex<(this.activeSnippet.beats?.length??0))return;this.runEffects(this.activeSnippet.onExit,this.activeSnippet.id);let i=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(i);continue}let t=this.stack[this.stack.length-1];if(!t){this.flowEnded=!0;return}t.sceneId!==this.currentSceneId&&(this.currentSceneId=t.sceneId);let r=this.childrenOf(t.containerId);if(!r){this.stack.pop();continue}let s=t.index;for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:r.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:r[t.index]?.id??null}),t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,r]of this.sceneBags)e.push({bag:r,pathPrefix:`${this.id}/@scene:${t}.`});return e}log(){return this.flowLog}clearLog(){this.flowLog.length=0}emit(e){let t=this.currentSceneId??void 0;this.host.emitEngine(this.id,e,t),this.host.logEnabled&&this.flowLog.push({...e,seq:this.flowSeq++,...t?{scene:t}:{}})}getVisitCounts(){return Object.fromEntries(this.visitCounts)}stepFor(e){let t=It(this.host.bundle).get(e);return t?this.beatResult(t):void 0}getChoices(){return this.pendingChoice?.options??[]}choose(e){let t=this.pendingChoice;if(!t)throw new Error("no choice is pending");let r=t.options.find(o=>o.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);this.touch();let s=t.byId.get(e);this.host.tracing&&this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null;let i=this.host.replayPromptOnChoose?this.authoredPromptOf(s):void 0;this.pendingPrompt=i&&r.prompt?{beat:i,shown:{...r.prompt}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:r}=this.splitRef(e);return t==="patter"?this.patterResolver.get(r):t==="scene"?this.sceneResolver.get(r):this.host.registry.get(t,r)}setProperty(e,t){this.writeProperty(e,t,!0)}stageIndex(e,t){return R(this.ladderOf(e),t)}compareStages(e,t,r){return O(this.ladderOf(e),t,r)}nextStage(e,t){return B(this.ladderOf(e),t)}ladderOf(e){let{scope:t,name:r}=this.splitRef(e);this.context();let s=this.stagesFor(t,r);if(!s)throw new Error(`\'${e}\' is not a quality`);return s}writeProperty(e,t,r){let{scope:s,name:i}=this.splitRef(e),o=this.host.journal;if(s==="patter"){if(o){let a=this.host.patterSharedNames.has(i),c=a?this.host.patterBag.get(i):this.local.get(i),l=this.local;c!==void 0&&o.undo.push(a?()=>this.host.registry.set("patter",i,c):()=>l.set(i,c))}this.patterResolver.set(i,t)}else if(s==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);if(o){let a=this.sceneBagFor(this.currentSceneId,i),c=a?.get(i);a&&c!==void 0&&o.undo.push(()=>a.set(i,c))}this.sceneResolver.set(i,t)}else{let a=o?this.host.registry.get(s,i):void 0;this.host.registry.set(s,i,t,r?{host:!0}:void 0),o&&a!==void 0&&o.undo.push(()=>this.host.registry.set(s,i,a,{host:!0}))}}snapshot(){return{rngState:this.rngState,visits:Object.fromEntries(this.visitCounts),cursor:{flowEnded:this.flowEnded,currentSceneId:this.currentSceneId,stack:this.stack.map(e=>{let t=this.childrenOf(e.containerId)?.[e.index];return t?{...e,nextId:t.id}:{...e}}),activeSnippetId:this.activeSnippet?.id??null,beatIndex:this.beatIndex,pendingChoice:this.pendingChoice?{groupId:this.pendingChoice.groupId,options:this.pendingChoice.options.map(e=>({...e}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:Rt(this.pendingPrompt.shown)}:{},selectors:ze(this.selectors)}}}restore(e){if(this.host.journal)throw new Error("a flow can\'t be restored while a checkpoint is open");this.restoreCursor(e),this.releaseBags(!0),this.mountLocal();for(let t of new Set([this.currentSceneId,...this.stack.map(r=>r.sceneId)]))t!==null&&this.host.bundle.scenes[t]&&this.ensureSceneBags(t)}restoreCursor(e){this.rngState=F(e.rngState),this.visitCounts=new Map(Object.entries(e.visits??{}));let t=e.cursor;if(this.started=!0,this.flowEnded=t.flowEnded,this.beatIndex=t.beatIndex,this.currentSceneId=t.currentSceneId,this.stack=t.stack.map(r=>{let{nextId:s,...i}=r;if(s!==void 0){let o=this.childrenOf(i.containerId)?.findIndex(a=>a.id===s)??-1;if(o>=0)return{...i,index:o}}return{...i}}),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=Ye(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let r=new Map,s=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);if(!o)continue;r.set(i.id,o);let{prompt:a,...c}=i,l=a?.kind==="line"&&a.speakerLabel===void 0?q(this.host,a.character,a.qualifier):void 0;s.push({...c,...a?{prompt:{...a,padAfter:a.padAfter??this.promptPadOf(o),...l!==void 0?{speakerLabel:l}:{}}}:{}})}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:r})}if(this.pendingPrompt=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId),s=r?this.authoredPromptOf(r):void 0,i=t.pendingPrompt,o=i?.kind==="line"&&i.speakerLabel===void 0?q(this.host,i.character,i.qualifier):void 0;s?this.pendingPrompt={beat:s,...i?{shown:{...i,padAfter:i.padAfter??this.padOf(s.id),...o!==void 0?{speakerLabel:o}:{}}}:{}}:this.pendingPromptOwnerId=null}}enterSceneSetup(e){let t=this.host.bundle.scenes[e];if(!t)throw new Error(`unknown scene: ${e}`);this.currentSceneId=e,this.enter(e),this.seedScene(t),this.runEffects(t.onEntry,t.id)}enterChild(e){if(this.enter(e.id),e.type==="snippet"){this.beginSnippet(e);return}let t=e.selector??"run";if(t==="run"){this.stack.push({sceneId:this.currentSceneId,containerId:e.id,index:0});return}if(t==="choice"){this.setupChoice(e);return}let r=this.selectChild(e);r&&this.enterChild(r)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let r=this.host.nodeIndex.get(e);if(r&&r.type==="group")return r.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],r=new Map,s=[];for(let o of e.children){if(o.fallback===!0){s.push(o);continue}if(o.sticky!==!0&&(this.visitCounts.get(o.id)??0)>=1)continue;let a=this.eligible(o),c=o.secretUntilEligible===!0;!a&&c||(t.push({id:o.id,prompt:this.promptFor(o),eligible:a,gameData:o.gameData}),r.set(o.id,o))}if(t.some(o=>o.eligible)){this.host.tracing&&this.emit({type:"choice",group:e.id,options:t.map(o=>({id:o.id,eligible:o.eligible}))}),this.pendingChoice={groupId:e.id,options:t,byId:r};return}let i=s.find(o=>this.eligible(o));if(i){this.enterChild(i);return}this.host.tracing&&this.emit({type:"dry",group:e.id}),this.host.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(this.host.tracing&&this.emit({type:"jump",to:e,mode:t}),e==="END"){this.flowEnded=!0,this.stack=[];return}let r,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}r=e,s=a.id}else{let a=this.host.blockIndex.get(e);if(!a)throw new Error(`jump target not found: ${e}`);a.sceneId!==this.currentSceneId&&this.enterSceneSetup(a.sceneId),r=a.sceneId,s=e}this.enter(s);let o={sceneId:r,containerId:s,index:0};t==="call"?this.stack.push(o):this.stack=[o]}selectChild(e){let t=e.children.map(c=>({id:c.id,eligible:this.eligible(c)})),r=e.children.filter((c,l)=>t[l].eligible),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(this.host.tracing&&this.emit({type:"select",group:e.id,selector:e.selector??"default",...e.selector==="sequence"?{order:s,exhaust:i}:{},children:t,picked:c?.id??null}),c);if(r.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(r[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(r,i,a):s==="specificity"?this.pickSpecificity(r,i,a):this.pickSequential(r,i,a));default:return null}}pickSequential(e,t,r){let s=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,r){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(g=>g.id),a=new Set(e.map(g=>g.id));if(r.bag===void 0&&(r.bag=o()),!r.bag.some(g=>a.has(g))){if(t==="once")return null;if(i){let g=e[s-1];return r.last=g.id,g}if(r.bag=o(),r.bag.length===0)return null}let c=r.bag.filter(g=>a.has(g)),l=r.last!==void 0&&c.length>1?c.indexOf(r.last):-1,p=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&p>=l&&p++;let h=c[p];return r.bag.splice(r.bag.indexOf(h),1),r.last=h,e.find(g=>g.id===h)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(p=>p.id));let l=new Set(r.bag);if(s=e.filter(p=>l.has(p.id)),s.length===0)return t==="stick"&&r.last!==void 0?e.find(p=>p.id===r.last)??null:null}let i=-1,a=s.map(l=>{let p=this.specScore(l);return p>i&&(i=p),{c:l,s:p}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=r.last!==void 0?a.findIndex(h=>h.id===r.last):-1,p=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&p>=l&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(l=>l!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,r){return ke(e,i=>{try{return Je(W(i,this.context(),ce))}catch(o){return this.reportError("best-match",r.id,r.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=this.host.journal,s;if(r&&(s=e.shared?r.selectors:r.flowSelectors.get(this),s||(s=new Set,r.flowSelectors.set(this,s))),r&&s&&!s.has(e.id)){s.add(e.id);let o=t.get(e.id),a=o?{...o,...o.bag?{bag:[...o.bag]}:{}}:void 0;r.undo.push(()=>{a?t.set(e.id,a):t.delete(e.id)})}let i=t.get(e.id);return i||(i={},t.set(e.id,i)),i}runEffects(e,t){for(let r of e??[]){let s;try{s=this.evalExpr(r.value);let i=this.host.tracing?this.getProperty(r.target):void 0;this.writeProperty(r.target,s,!1),this.host.tracing&&this.emit({type:"write",target:r.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,r.value,i)}}}eligible(e){if(!e.condition)return!0;try{return Je(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,r,s){let i=s instanceof Error?s.message:String(s),o=r?.src;this.host.onError({flow:this.id,kind:e,node:t,...o?{source:o}:{},message:i}),this.host.tracing&&this.emit({type:"diagnostic",kind:e,node:t,...o?{source:o}:{},message:i})}evalExpr(e){return W(this.conditionAst(e),this.context(),ce)}conditionAst(e){let t=Ge.get(e);return t||(t=E(e.ast),Ge.set(e,t)),t}enter(e){let t=this.visitCounts,r=t.get(e);t.set(e,(r??0)+1);let s=this.host.sharedVisits,i=s.get(e);s.set(e,(i??0)+1),this.host.journal&&this.host.journal.undo.push(()=>{r===void 0?t.delete(e):t.set(e,r),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=se(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=I(this.host,e.id),r=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:e.gameData,...r};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),padAfter:this.padOf(e.id),gameData:e.gameData,...r};case"line":{let s=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?s:this.interpolate(s)),c=i&&a.length===0;return{type:"line",id:e.id,text:a,character:c?void 0:e.character,characterName:c?void 0:this.resolveCharacterName(e.character),direction:c?void 0:e.direction,qualifier:c?void 0:e.qualifier,qualifierName:c?void 0:this.resolveQualifierName(e.qualifier),speakerLabel:c?void 0:q(this.host,e.character,e.qualifier),padAfter:this.padOf(e.id),gameData:e.gameData,...r}}}}interpolate(e){return Be(e,t=>this.getProperty(t))}stripCaptions(e){return Oe(e,this.host.captionOpen,this.host.captionClose)}captionLine(e){return this.host.captionsOn?e:this.stripCaptions(e)}promptFor(e){let t=this.promptBeatOf(e);if(!t)return;let r=this.interpolate(this.resolveString(t.id)),s=this.padOf(t.id);return t.kind==="line"?{kind:"line",text:this.captionLine(r),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction,qualifier:t.qualifier,qualifierName:this.resolveQualifierName(t.qualifier),speakerLabel:q(this.host,t.character,t.qualifier),padAfter:s}:{kind:"text",text:r,padAfter:s}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let r=I(this.host,e.id),s=r&&r.length?{tags:r}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,padAfter:this.padOf(e.id),gameData:e.gameData,...s}:{type:"line",id:e.id,text:t.text,character:t.character,characterName:t.characterName,direction:t.direction,qualifier:t.qualifier,qualifierName:t.qualifierName,speakerLabel:t.speakerLabel,padAfter:this.padOf(e.id),gameData:e.gameData,...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(r=>r.kind==="line"||r.kind==="text")}firstTextSnippetIn(e){let t;return D(e,r=>{!t&&r.type==="snippet"&&(r.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=r)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let r=this.host.defaultStrings[e];return r!==void 0?`<Untranslated: ${e}> ${r}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=Y(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}promptPadOf(e){let t=this.promptBeatOf(e);return t?this.padOf(t.id):x}padOf(e){return this.host.padIndex.get(e)?.resolved??x}resolveQualifierName(e){if(e===void 0||this.host.emitIds)return;let t=X(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return Ze(this.host,e)}newLocal(){return new v(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=y.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:M}),this.registered.add(e)}releaseBags(e){for(let t of this.registered)this.host.registry.remove(t,{keep:e});this.registered.clear(),this.sceneBags.clear()}sceneBagFor(e,t){if(this.host.bundle.scenes[e])return this.ensureSceneBags(e),this.host.sceneSharedNames.get(e)?.has(t.toLowerCase())?this.host.stageBags.get(e):this.sceneBags.get(e)}ensureSceneBags(e){let t=this.host.sceneSharedNames.get(e)??new Set,r=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal;if(!this.sceneBags.has(e)){let i=new v(r.filter(c=>!t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),o=y.flowScene(this.id,e),a=We(this.host.registry,o,i,s!==null);this.registered.add(o),this.sceneBags.set(e,i),s&&s.undo.push(()=>{this.sceneBags.get(e)===i&&(this.host.registry.remove(o,a?{keep:!0}:void 0),this.registered.delete(o),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let i=new v(r.filter(a=>t.has(a.name.toLowerCase())),{pathPrefix:"@scene."}),o=We(this.host.registry,y.stage(e),i,s!==null);this.host.stageBags.set(e,i),s&&s.undo.push(()=>{this.host.stageBags.get(e)===i&&(this.host.registry.remove(y.stage(e),o?{keep:!0}:void 0),this.host.stageBags.delete(e))})}}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let r of e.sceneProps??[]){if(!r.temporary)continue;let s=r.name.toLowerCase(),i=t.has(s)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);if(!i)continue;let o=this.host.journal?i.get(s):void 0;o!==void 0&&this.host.journal.undo.push(()=>i.set(s,o)),i.set(s,N(r))}}};function He(n,e){D(n,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let r of t.beats??[])r.kind==="line"&&r.character&&e.add(r.character)})}function q(n,e,t,r=!1){if(!e||n.emitIds)return;let s=c=>(r?void 0:n.strings[c])??n.defaultStrings[c],i=s(Y(e))??n.castDisplay.get(e)??e;if(t===void 0)return i;let o=n.speakerLabels.get(`${e}\\0${t}`);if(o!==void 0)return s(Ae(e,t))??o;let a=s(X(t))??n.qualifierDisplay.get(t)??t;return Te(s(Fe)??n.speakerPattern,i,a)}function Ue(n){return n&&Object.keys(n).length?{gameData:n}:{}}function Rt(n){let e={kind:n.kind,text:n.text};return n.character!==void 0&&(e.character=n.character),n.characterName!==void 0&&(e.characterName=n.characterName),n.direction!==void 0&&(e.direction=n.direction),n.qualifier!==void 0&&(e.qualifier=n.qualifier),n.qualifierName!==void 0&&(e.qualifierName=n.qualifierName),n.speakerLabel!==void 0&&(e.speakerLabel=n.speakerLabel),typeof n.padAfter=="number"&&(e.padAfter=n.padAfter),e}function ze(n){let e={};for(let[t,r]of n){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e[t]=s}return e}function Ye(n){let e=new Map;for(let[t,r]of Object.entries(n??{})){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e.set(t,s)}return e}function We(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:M}),r&&JSON.stringify(t.save())!==s}function Ke(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Qe(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function $(n,e,t){let r=n.qualityLadders.get(e);if(!r){r=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!r.has(i)&&r.set(i,s.stages)}n.qualityLadders.set(e,r)}return r}function Ot(n,e,t){let r=t.toLowerCase();return e==="patter"?$(n,"patter",()=>[...n.patterSharedDecls,...n.patterLocalDecls]).get(r):n.registry.toEvalContext().qualities?.(e,t)??$(n,`host/${e}`,()=>n.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(r)}function Xe(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function Bt(n,e){let t=Xe(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function pe(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function de(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Ze(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=Re(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function At(n){let e={};n.shared?.patter&&(e.patter=n.shared.patter);for(let[t,r]of Object.entries(n.stageBags??{}))e[y.stage(t)]=r;for(let[t,r]of Object.entries(n.flows??{}))Object.assign(e,et(t,r));return e}function et(n,e){let t={};e.scopes?.patter&&(t[y.flowGlobals(n)]=e.scopes.patter);for(let[r,s]of Object.entries(e.sceneBags??{}))t[y.flowScene(n,r)]=s;return t}function Ft(n,e){let t={scopes:{},sceneBags:{}},r=y.flow(n);for(let[s,i]of Object.entries(e)){if(!s.startsWith(r))continue;let o=s.slice(r.length);o==="patter"?t.scopes.patter=i:o.startsWith("scene/")&&(t.sceneBags[Pt(o.slice(6))]=i)}return t}function Je(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var Nt=(n,e)=>n.shared??e;function ue(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:Nt(n,e)}}function Tt(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function Lt(n,e){e.blocks++;let t=[...n.children];for(;t.length;){let r=t.pop();if(r.type==="group"){e.groups++,r.prompt&&e.prompts++,t.push(...r.children);continue}e.snippets++;for(let s of r.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function tt(n){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:n.cast?.length??0},t=[],r=[];for(let o of Object.values(n.scenes)){e.scenes++;let a=w(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:w(c),name:c.name}))});for(let c of o.blocks)Lt(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>ue(c,!1))})}let s=(n.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>ue(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(Tt)}));return{identity:{schema:n.schema,project:n.content.project,...n.content.version!==void 0?{version:n.content.version}:{},...n.content.hash!==void 0?{hash:n.content.hash}:{},...n.content.structureHash!==void 0?{structureHash:n.content.structureHash}:{},voiced:n.voiced,defaultLocale:n.locales.default,locales:[...n.locales.included],localisation:n.localisation?.mode??"embedded",sourceDebug:n.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(n.properties??[]).map(o=>ue(o,!0)),scene:r},gameData:i,counts:e}}function rt(n,e){return n.gameDataFields?.[e]??[]}function ge(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function nt(n,e){let t={};for(let r of n){let s=ge(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))r in t||(t[r]=s);return t}return ut(jt);})();\n';
1635
1544
  var PLAYABLE_TIMING_JS = 'var GameSubtitles=(()=>{var l=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var u=(t,e)=>{for(var s in e)l(t,s,{get:e[s],enumerable:!0})},f=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of c(e))!_.call(t,i)&&i!==s&&l(t,i,{get:()=>e[i],enumerable:!(n=a(e,i))||n.enumerable});return t};var p=t=>f(l({},"__esModule",{value:!0}),t);var d={};u(d,{estimateDuration:()=>h});function h(t,{charsPerSecond:e=14,minSeconds:s=1.5,maxSeconds:n=18}={}){let i=0;for(let o of t??"")o!=="\\xAD"&&i++;let r=e>0?e:14;return Math.min(Math.max(i/r,s),n)}return p(d);})();\n';
1636
1545
 
1637
1546
  // src/play-timeline.ts
@@ -2078,6 +1987,7 @@ function renderPlay(result) {
2078
1987
  // src/coverage.ts
2079
1988
  import { Engine as Engine2 } from "@patterkit/runtime";
2080
1989
  import { isContentlessBeat as isContentlessBeat2, walkNodes } from "@patterkit/model";
1990
+ import { deserialiseAst, makePrng } from "@wildwinter/expr";
2081
1991
  var CADENCE_PROB = {
2082
1992
  rarely: 0.15,
2083
1993
  sometimes: 0.4,
@@ -4819,6 +4729,170 @@ function runReplace(loaded, opts) {
4819
4729
  return plan;
4820
4730
  }
4821
4731
 
4732
+ // src/stages.ts
4733
+ import { parse, unparse } from "@wildwinter/expr";
4734
+ import { dialectWithForeignScopes } from "@patterkit/dialect";
4735
+ import { hostScopesToSpec as hostScopesToSpec2, projectScopes as projectScopes2 } from "@patterkit/compiler";
4736
+ import { walkNodes as walkNodes5 } from "@patterkit/model";
4737
+ var COMPARISONS = /* @__PURE__ */ new Set(["==", "!=", "<", "<=", ">", ">="]);
4738
+ var ORDERING = /* @__PURE__ */ new Set(["<", "<=", ">", ">="]);
4739
+ function stageDialect(project, gameScopes) {
4740
+ return dialectWithForeignScopes(hostScopesToSpec2(projectScopes2(project, gameScopes?.merged).host));
4741
+ }
4742
+ function renameStageInExpression(src, dialect, isTarget, from, to, assigned = false) {
4743
+ let ast;
4744
+ try {
4745
+ ast = parse(src, dialect);
4746
+ } catch {
4747
+ return void 0;
4748
+ }
4749
+ let hits = 0;
4750
+ const isRef = (n) => n.kind === "scopedvar" && isTarget(n.scope, n.name);
4751
+ const walk = (n) => {
4752
+ switch (n.kind) {
4753
+ case "binary": {
4754
+ let left = walk(n.left);
4755
+ let right = walk(n.right);
4756
+ if (COMPARISONS.has(n.op)) {
4757
+ if (isRef(left) && right.kind === "string" && right.value === from) {
4758
+ right = { kind: "string", value: to };
4759
+ hits++;
4760
+ }
4761
+ if (isRef(right) && left.kind === "string" && left.value === from) {
4762
+ left = { kind: "string", value: to };
4763
+ hits++;
4764
+ }
4765
+ }
4766
+ return { ...n, left, right };
4767
+ }
4768
+ case "unary":
4769
+ return { ...n, operand: walk(n.operand) };
4770
+ case "call":
4771
+ return { ...n, args: n.args.map(walk) };
4772
+ default:
4773
+ return n;
4774
+ }
4775
+ };
4776
+ let next = walk(ast);
4777
+ if (assigned && next.kind === "string" && next.value === from) {
4778
+ next = { kind: "string", value: to };
4779
+ hits++;
4780
+ }
4781
+ if (hits === 0) return void 0;
4782
+ const wanted = JSON.stringify(next);
4783
+ const esc2 = from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4784
+ const quotedTo = (q) => `${q}${to.replace(/\\/g, "\\\\").replace(new RegExp(q, "g"), `\\${q}`)}${q}`;
4785
+ const found = [...src.matchAll(new RegExp(`(["'])${esc2}\\1`, "g"))];
4786
+ const says = (text) => {
4787
+ try {
4788
+ return JSON.stringify(parse(text, dialect)) === wanted;
4789
+ } catch {
4790
+ return false;
4791
+ }
4792
+ };
4793
+ const replaced = (mask) => {
4794
+ let text = "";
4795
+ let at = 0;
4796
+ found.forEach((m, i) => {
4797
+ if (!(mask & 1 << i)) return;
4798
+ text += src.slice(at, m.index) + quotedTo(m[1]);
4799
+ at = m.index + m[0].length;
4800
+ });
4801
+ return text + src.slice(at);
4802
+ };
4803
+ if (found.length > 0 && found.length <= 8) {
4804
+ const all = (1 << found.length) - 1;
4805
+ if (says(replaced(all))) return replaced(all);
4806
+ for (let mask = all - 1; mask > 0; mask--) if (says(replaced(mask))) return replaced(mask);
4807
+ }
4808
+ return unparse(next, { defaultScope: dialect.defaultScope });
4809
+ }
4810
+ var refersTo = (target) => (scope, name) => scope === target.scope && name === target.name;
4811
+ function effectTarget(ref, dialect) {
4812
+ try {
4813
+ const n = parse(ref, dialect);
4814
+ return n.kind === "scopedvar" ? { scope: n.scope, name: n.name } : void 0;
4815
+ } catch {
4816
+ return void 0;
4817
+ }
4818
+ }
4819
+ function sceneSites(scene) {
4820
+ const sites = [];
4821
+ const effects = (list) => {
4822
+ for (const e of list ?? []) {
4823
+ sites.push({ get: () => e.value, set: (src) => {
4824
+ e.value = src;
4825
+ }, assignedTo: e.target });
4826
+ }
4827
+ };
4828
+ effects(scene.onEntry);
4829
+ for (const block of scene.blocks) {
4830
+ walkNodes5(block.children, (node) => {
4831
+ if (typeof node.condition === "string") {
4832
+ sites.push({ get: () => node.condition, set: (src) => {
4833
+ node.condition = src;
4834
+ } });
4835
+ }
4836
+ if (node.type === "snippet") {
4837
+ effects(node.onEnter);
4838
+ effects(node.onExit);
4839
+ }
4840
+ });
4841
+ }
4842
+ return sites;
4843
+ }
4844
+ var reach = (scenes, target) => target.scope === "scene" ? scenes.filter((s) => s.id === target.scene) : scenes;
4845
+ function renameStage(scenes, target, from, to, dialect) {
4846
+ const result = { scenes: [], expressions: 0 };
4847
+ if (to.trim() === "" || from === to) return result;
4848
+ const isTarget = refersTo(target);
4849
+ for (const scene of reach(scenes, target)) {
4850
+ let changed = false;
4851
+ for (const site of sceneSites(scene)) {
4852
+ const src = site.get();
4853
+ if (src === void 0) continue;
4854
+ const t = site.assignedTo !== void 0 ? effectTarget(site.assignedTo, dialect) : void 0;
4855
+ const next = renameStageInExpression(src, dialect, isTarget, from, to, t !== void 0 && isTarget(t.scope, t.name));
4856
+ if (next === void 0) continue;
4857
+ site.set(next);
4858
+ result.expressions++;
4859
+ changed = true;
4860
+ }
4861
+ if (changed) result.scenes.push(scene);
4862
+ }
4863
+ return result;
4864
+ }
4865
+ function stageGateCount(scenes, target, dialect) {
4866
+ const isTarget = refersTo(target);
4867
+ const orders = (n) => {
4868
+ switch (n.kind) {
4869
+ case "binary": {
4870
+ const ref = (m) => m.kind === "scopedvar" && isTarget(m.scope, m.name);
4871
+ if (ORDERING.has(n.op) && (ref(n.left) || ref(n.right))) return true;
4872
+ return orders(n.left) || orders(n.right);
4873
+ }
4874
+ case "unary":
4875
+ return orders(n.operand);
4876
+ case "call":
4877
+ return n.args.some(orders);
4878
+ default:
4879
+ return false;
4880
+ }
4881
+ };
4882
+ let count = 0;
4883
+ for (const scene of reach(scenes, target)) {
4884
+ for (const site of sceneSites(scene)) {
4885
+ const src = site.get();
4886
+ if (src === void 0) continue;
4887
+ try {
4888
+ if (orders(parse(src, dialect))) count++;
4889
+ } catch {
4890
+ }
4891
+ }
4892
+ }
4893
+ return count;
4894
+ }
4895
+
4822
4896
  // src/pin.ts
4823
4897
  import { readFileSync as readFileSync9 } from "fs";
4824
4898
  import { canonicalStringify as canonicalStringify9, parseSource as parseSource5 } from "@patterkit/core";
@@ -5070,7 +5144,7 @@ function containedWrite(dir, name) {
5070
5144
  // src/suggestions.ts
5071
5145
  import { readFileSync as readFileSync11 } from "fs";
5072
5146
  import { canonicalStringify as canonicalStringify11, parseSource as parseSource7 } from "@patterkit/core";
5073
- import { walkNodes as walkNodes5, AUTHORING_SCHEMA as AUTHORING_SCHEMA3 } from "@patterkit/model";
5147
+ import { walkNodes as walkNodes6, AUTHORING_SCHEMA as AUTHORING_SCHEMA3 } from "@patterkit/model";
5074
5148
  function staleParts(s, liveText, line) {
5075
5149
  const stale = [];
5076
5150
  if (liveText !== s.baseline) stale.push("the text");
@@ -5101,7 +5175,7 @@ function indexPlaces(loaded) {
5101
5175
  places.set(scene.id, { sceneId: scene.id });
5102
5176
  for (const block of scene.blocks) {
5103
5177
  places.set(block.id, { sceneId: scene.id });
5104
- walkNodes5(block.children, (node) => {
5178
+ walkNodes6(block.children, (node) => {
5105
5179
  places.set(node.id, { sceneId: scene.id });
5106
5180
  if (node.type === "group") {
5107
5181
  if (node.prompt) places.set(node.prompt.id, { sceneId: scene.id });
@@ -5169,7 +5243,7 @@ var Working = class {
5169
5243
  function beatIn(scene, id) {
5170
5244
  let found;
5171
5245
  for (const block of scene.blocks) {
5172
- walkNodes5(block.children, (node) => {
5246
+ walkNodes6(block.children, (node) => {
5173
5247
  if (found || node.type !== "snippet") return;
5174
5248
  found = (node.beats ?? []).find((b) => b.id === id);
5175
5249
  });
@@ -6278,6 +6352,8 @@ export {
6278
6352
  readHandoff,
6279
6353
  readHandoffs,
6280
6354
  readMarker,
6355
+ renameStage,
6356
+ renameStageInExpression,
6281
6357
  renderCoverageText,
6282
6358
  renderPlay,
6283
6359
  renderReportText,
@@ -6318,6 +6394,8 @@ export {
6318
6394
  serialiseScopesFile,
6319
6395
  setCut,
6320
6396
  sidecarIssues,
6397
+ stageDialect,
6398
+ stageGateCount,
6321
6399
  staleParts,
6322
6400
  useScriptFontFiles,
6323
6401
  vcsConfigWrites,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/ops",
3
- "version": "0.17.1",
3
+ "version": "0.18.0",
4
4
  "description": "The shared operations layer: every project operation (load, validate, export, format, play, ...) as a pure function, consumed by the patter CLI and Patterpad alike.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -33,10 +33,12 @@
33
33
  "@fontsource/noto-sans": "^5.3.0",
34
34
  "@fontsource/noto-sans-math": "^5.3.0",
35
35
  "@ibm/plex-mono": "1.0.0",
36
- "@patterkit/compiler": "0.7.0",
37
- "@patterkit/core": "0.7.0",
36
+ "@patterkit/compiler": "0.7.1",
37
+ "@patterkit/core": "0.7.1",
38
+ "@patterkit/dialect": "0.2.11",
38
39
  "@patterkit/model": "0.14.0",
39
- "@patterkit/runtime": "0.26.0",
40
+ "@patterkit/runtime": "0.27.0",
41
+ "@wildwinter/expr": "^0.5.2",
40
42
  "@wildwinter/toolkit": "^0.1.0",
41
43
  "@xmldom/xmldom": "^0.8.15",
42
44
  "docx": "^9.7.1",