@patterkit/ops 0.19.0 → 0.20.1
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 +148 -9
- package/dist/index.js +624 -122
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readdirSync as readdirSync2, readFileSync as readFileSync2, statSync, existsSync as existsSync2 } from "fs";
|
|
3
3
|
import { join as join2, dirname as dirname2, resolve as resolve2, basename } from "path";
|
|
4
4
|
import { parseSource } from "@patterkit/core";
|
|
5
|
-
import { isCaseOnlyPropertyName, projectLayout } from "@patterkit/model";
|
|
5
|
+
import { EXTENSION_SHARD, EXTENSIONS_DIR, MANIFEST_EXTENSION, isCaseOnlyPropertyName, projectLayout } from "@patterkit/model";
|
|
6
6
|
|
|
7
7
|
// src/game-scopes.ts
|
|
8
8
|
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
@@ -251,6 +251,46 @@ function planShareScopes(root, project, dir) {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
// src/load.ts
|
|
254
|
+
var NO_EXTENSIONS = () => ({ manifests: [], extensionShards: [], extensionShardFiles: [], extensionIssues: [] });
|
|
255
|
+
function loadExtensions(root, layout) {
|
|
256
|
+
const out = NO_EXTENSIONS();
|
|
257
|
+
const manifestDir = join2(root, EXTENSIONS_DIR);
|
|
258
|
+
const manifestFiles = readDirSafe(manifestDir).filter((e) => e.isFile() && !e.name.startsWith(".") && e.name.endsWith(MANIFEST_EXTENSION)).map((e) => join2(manifestDir, e.name)).sort();
|
|
259
|
+
for (const path of manifestFiles) {
|
|
260
|
+
try {
|
|
261
|
+
out.manifests.push({ path, id: basename(path).slice(0, -MANIFEST_EXTENSION.length), manifest: parseSource(readFileSync2(path, "utf8")) });
|
|
262
|
+
} catch (e) {
|
|
263
|
+
out.extensionIssues.push({ file: path, message: `the extension's manifest will not parse: ${e instanceof Error ? e.message : String(e)}` });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
for (const file of walkFiles(join2(root, layout.extensions), EXTENSION_SHARD)) {
|
|
267
|
+
let parsed;
|
|
268
|
+
try {
|
|
269
|
+
parsed = parseSource(readFileSync2(file, "utf8"));
|
|
270
|
+
} catch (e) {
|
|
271
|
+
out.extensionIssues.push({ file, message: `the extension data will not parse, so it is left out: ${e instanceof Error ? e.message : String(e)}` });
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const problem = extensionShardProblem(parsed);
|
|
275
|
+
if (problem) {
|
|
276
|
+
out.extensionIssues.push({ file, message: `${problem}, so it is left out` });
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
out.extensionShards.push(parsed);
|
|
280
|
+
out.extensionShardFiles.push(file);
|
|
281
|
+
}
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
function extensionShardProblem(v) {
|
|
285
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return "not an extension data file";
|
|
286
|
+
const o = v;
|
|
287
|
+
if (typeof o["schema"] !== "string" || !o["schema"].startsWith("patter/extension@")) return `schema is '${String(o["schema"])}', expected 'patter/extension@...'`;
|
|
288
|
+
if (typeof o["extension"] !== "string" || o["extension"] === "") return "it names no extension";
|
|
289
|
+
if (typeof o["scene"] !== "string" || o["scene"] === "") return "it names no scene";
|
|
290
|
+
const entries = o["entries"];
|
|
291
|
+
if (typeof entries !== "object" || entries === null || Array.isArray(entries)) return "its entries are not keyed by node kind";
|
|
292
|
+
return void 0;
|
|
293
|
+
}
|
|
254
294
|
function withGameScopes(root, project) {
|
|
255
295
|
const { gameScopes, missing } = discoverGameScopes(root, project);
|
|
256
296
|
return { ...gameScopes ? { gameScopes } : {}, ...missing ? { gameScopesMissing: missing } : {} };
|
|
@@ -436,7 +476,7 @@ function loadProjectLanding(startPath, opts) {
|
|
|
436
476
|
}
|
|
437
477
|
}
|
|
438
478
|
}
|
|
439
|
-
return { root, projectFile, project, scenes, locales, sceneFiles, localeFiles, authoring: [], authoringFiles: [], ...withGameScopes(root, project) };
|
|
479
|
+
return { root, projectFile, project, scenes, locales, sceneFiles, localeFiles, authoring: [], authoringFiles: [], ...withGameScopes(root, project), ...NO_EXTENSIONS() };
|
|
440
480
|
}
|
|
441
481
|
function applySceneOrder(scenes, order) {
|
|
442
482
|
if (!order?.length) return;
|
|
@@ -479,7 +519,19 @@ function loadProject(startPath) {
|
|
|
479
519
|
authoring.push(parseFile(f, "patter/authoring", "schema"));
|
|
480
520
|
authoringFiles.push(f);
|
|
481
521
|
}
|
|
482
|
-
return {
|
|
522
|
+
return {
|
|
523
|
+
root,
|
|
524
|
+
projectFile,
|
|
525
|
+
project,
|
|
526
|
+
scenes,
|
|
527
|
+
locales,
|
|
528
|
+
sceneFiles,
|
|
529
|
+
localeFiles,
|
|
530
|
+
authoring,
|
|
531
|
+
authoringFiles,
|
|
532
|
+
...withGameScopes(root, project),
|
|
533
|
+
...loadExtensions(root, layout)
|
|
534
|
+
};
|
|
483
535
|
}
|
|
484
536
|
|
|
485
537
|
// src/write.ts
|
|
@@ -494,11 +546,12 @@ function applyWrites(writes) {
|
|
|
494
546
|
|
|
495
547
|
// src/validate.ts
|
|
496
548
|
import { existsSync as existsSync4, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
497
|
-
import { basename as basename4, isAbsolute as isAbsolute2, join as join5, relative as relative3 } from "path";
|
|
549
|
+
import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute2, join as join5, relative as relative3 } from "path";
|
|
498
550
|
|
|
499
551
|
// src/merge.ts
|
|
500
|
-
import { canonicalStringify as canonicalStringify2 } from "@patterkit/core";
|
|
552
|
+
import { canonicalStringify as canonicalStringify2, parseSource as parseSource2 } from "@patterkit/core";
|
|
501
553
|
import { DEFAULT_QUALIFIERS } from "@patterkit/model";
|
|
554
|
+
import { markUnresolved, mergeExtensionData, unresolvedProblems, withoutMark } from "@wildwinter/toolkit/extensions";
|
|
502
555
|
var UnsupportedMergeError = class extends Error {
|
|
503
556
|
};
|
|
504
557
|
var eq = (a, b) => canonicalStringify2(a) === canonicalStringify2(b);
|
|
@@ -510,7 +563,13 @@ function sidecarIssues(sidecarPaths) {
|
|
|
510
563
|
message: "unresolved merge conflict - resolve it and delete the .patterconflict sidecar before committing"
|
|
511
564
|
}));
|
|
512
565
|
}
|
|
513
|
-
var MERGE_TYPES = {
|
|
566
|
+
var MERGE_TYPES = {
|
|
567
|
+
flow: "flow",
|
|
568
|
+
strings: "loc",
|
|
569
|
+
authoring: "authoring",
|
|
570
|
+
project: "project",
|
|
571
|
+
extension: "extension"
|
|
572
|
+
};
|
|
514
573
|
function detectMergeType(file) {
|
|
515
574
|
const s = typeof file.schema === "string" ? file.schema : "";
|
|
516
575
|
const name = /^patter\/([a-z]+)(?:@|$)/.exec(s)?.[1];
|
|
@@ -520,16 +579,32 @@ function detectMergeType(file) {
|
|
|
520
579
|
}
|
|
521
580
|
function runMerge(base, ours, theirs, opts) {
|
|
522
581
|
const type = opts?.type ?? detectMergeType(ours);
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
582
|
+
if (type === "extension") {
|
|
583
|
+
const r2 = mergeExtensionData(base, ours, theirs, { host: "patter", ...opts?.manifest !== void 0 ? { manifest: opts.manifest } : {} });
|
|
584
|
+
return { type, merged: r2.merged, conflicts: [...r2.conflicts], warnings: [...r2.warnings] };
|
|
585
|
+
}
|
|
586
|
+
const b = withoutMark(base), o = withoutMark(ours), t = withoutMark(theirs);
|
|
587
|
+
const r = type === "loc" ? mergeLoc(b, o, t) : type === "authoring" ? mergeAuthoring(b, o, t) : type === "flow" ? mergeFlow(b, o, t) : mergeProject(b, o, t);
|
|
588
|
+
return { ...r, merged: markUnresolved(r.merged, r.conflicts, { base, ours, theirs }) };
|
|
589
|
+
}
|
|
590
|
+
function unresolvedMarkIssues(files) {
|
|
591
|
+
const issues = [];
|
|
592
|
+
for (const { file, text } of files) {
|
|
593
|
+
if (!text.includes("unresolved")) continue;
|
|
594
|
+
let shard;
|
|
595
|
+
try {
|
|
596
|
+
shard = parseSource2(text);
|
|
597
|
+
} catch {
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
for (const p of unresolvedProblems(shard)) {
|
|
601
|
+
issues.push({
|
|
602
|
+
file,
|
|
603
|
+
message: `unresolved merge at ${p.path}: the shard holds our side of a clash - resolve it, then remove the entry from "unresolved"`
|
|
604
|
+
});
|
|
605
|
+
}
|
|
532
606
|
}
|
|
607
|
+
return issues;
|
|
533
608
|
}
|
|
534
609
|
function deletedKind(base, ours, theirs) {
|
|
535
610
|
const deleted = base !== void 0 && (ours === void 0 || theirs === void 0);
|
|
@@ -542,7 +617,7 @@ function merge3(base, ours, theirs, id, path, conflicts) {
|
|
|
542
617
|
conflicts.push({ id, path, base, ours, theirs, kind: deletedKind(base, ours, theirs) });
|
|
543
618
|
return ours;
|
|
544
619
|
}
|
|
545
|
-
function mergeMap(base, ours, theirs, prefix, conflicts,
|
|
620
|
+
function mergeMap(base, ours, theirs, prefix, conflicts, resolve8) {
|
|
546
621
|
const out = {};
|
|
547
622
|
for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
|
|
548
623
|
const b = base[k], o = ours[k], t = theirs[k];
|
|
@@ -551,7 +626,7 @@ function mergeMap(base, ours, theirs, prefix, conflicts, resolve7) {
|
|
|
551
626
|
else if (eq(b, o)) v = t;
|
|
552
627
|
else if (eq(b, t)) v = o;
|
|
553
628
|
else {
|
|
554
|
-
const r =
|
|
629
|
+
const r = resolve8?.(k, b, o, t);
|
|
555
630
|
if (r && r.resolved) v = r.value;
|
|
556
631
|
else {
|
|
557
632
|
conflicts.push({ id: k, path: `${prefix}.${k}`, base: b, ours: o, theirs: t, kind: r ? r.kind : deletedKind(b, o, t) });
|
|
@@ -697,13 +772,13 @@ function mergeEditRecord(o, t) {
|
|
|
697
772
|
if (Object.keys(loc).length > 0) merged.localisedAt = loc;
|
|
698
773
|
return merged;
|
|
699
774
|
}
|
|
700
|
-
function mergeKeyedByName(base, ours, theirs, path, conflicts,
|
|
775
|
+
function mergeKeyedByName(base, ours, theirs, path, conflicts, resolve8) {
|
|
701
776
|
const byName = (arr) => {
|
|
702
777
|
const m = {};
|
|
703
778
|
for (const p of arr) if (isObj(p) && typeof p.name === "string") m[p.name] = p;
|
|
704
779
|
return m;
|
|
705
780
|
};
|
|
706
|
-
const merged = mergeMap(byName(base), byName(ours), byName(theirs), path, conflicts,
|
|
781
|
+
const merged = mergeMap(byName(base), byName(ours), byName(theirs), path, conflicts, resolve8?.(path, conflicts));
|
|
707
782
|
return Object.keys(merged).sort().map((name) => merged[name]);
|
|
708
783
|
}
|
|
709
784
|
function mergeCastMember(path, conflicts) {
|
|
@@ -718,6 +793,7 @@ function mergeCastMember(path, conflicts) {
|
|
|
718
793
|
return { resolved: true, value: out };
|
|
719
794
|
};
|
|
720
795
|
}
|
|
796
|
+
var PROJECT_HANDLED = /* @__PURE__ */ new Set(["properties", "cast", "gameDataFields", "qualifiers", "locales", "extensions"]);
|
|
721
797
|
function mergeProject(base, ours, theirs) {
|
|
722
798
|
const conflicts = [];
|
|
723
799
|
const merged = {};
|
|
@@ -736,6 +812,9 @@ function mergeProject(base, ours, theirs) {
|
|
|
736
812
|
if (v !== void 0) merged[k] = v;
|
|
737
813
|
} else if (k === "locales") {
|
|
738
814
|
merged.locales = mergeLocales(asMap(base.locales), asMap(ours.locales), asMap(theirs.locales), conflicts);
|
|
815
|
+
} else if (k === "extensions" && [base[k], ours[k], theirs[k]].every((x) => x === void 0 || isObj(x))) {
|
|
816
|
+
const v = mergeProjectExtensions(asMap(base[k]), asMap(ours[k]), asMap(theirs[k]), conflicts);
|
|
817
|
+
if (Object.keys(v).length > 0) merged[k] = v;
|
|
739
818
|
} else {
|
|
740
819
|
const v = merge3(base[k], ours[k], theirs[k], "", k, conflicts);
|
|
741
820
|
if (v !== void 0) merged[k] = v;
|
|
@@ -743,6 +822,31 @@ function mergeProject(base, ours, theirs) {
|
|
|
743
822
|
}
|
|
744
823
|
return { type: "project", merged, conflicts, warnings: [] };
|
|
745
824
|
}
|
|
825
|
+
function mergeProjectExtensions(b, o, t, conflicts) {
|
|
826
|
+
const out = {};
|
|
827
|
+
for (const id of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
|
|
828
|
+
const bv = b[id], ov = o[id], tv = t[id];
|
|
829
|
+
const path = `extensions.${id}`;
|
|
830
|
+
if (!isObj(ov) || !isObj(tv) || bv !== void 0 && !isObj(bv)) {
|
|
831
|
+
const v = merge3(bv, ov, tv, id, path, conflicts);
|
|
832
|
+
if (v !== void 0) out[id] = v;
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
const bo = asMap(bv);
|
|
836
|
+
const entry = {};
|
|
837
|
+
for (const f of /* @__PURE__ */ new Set([...Object.keys(bo), ...Object.keys(ov), ...Object.keys(tv)])) {
|
|
838
|
+
if (f === "settings" && [bo[f], ov[f], tv[f]].every((x) => x === void 0 || isObj(x))) {
|
|
839
|
+
const settings = mergeMap(asMap(bo[f]), asMap(ov[f]), asMap(tv[f]), `${path}.settings`, conflicts);
|
|
840
|
+
if (Object.keys(settings).length > 0) entry[f] = settings;
|
|
841
|
+
} else {
|
|
842
|
+
const v = merge3(bo[f], ov[f], tv[f], id, `${path}.${f}`, conflicts);
|
|
843
|
+
if (v !== void 0) entry[f] = v;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
out[id] = entry;
|
|
847
|
+
}
|
|
848
|
+
return out;
|
|
849
|
+
}
|
|
746
850
|
function mergeGameDataFields(b, o, t, conflicts) {
|
|
747
851
|
const out = {};
|
|
748
852
|
for (const kind of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
|
|
@@ -916,14 +1020,14 @@ function checkDuplicateIds(root, conflicts) {
|
|
|
916
1020
|
n.children.forEach(walk);
|
|
917
1021
|
};
|
|
918
1022
|
walk(root);
|
|
919
|
-
for (const id of dup) conflicts.push({ id, path:
|
|
1023
|
+
for (const id of dup) conflicts.push({ id, path: `scene[${id}]`, base: void 0, ours: void 0, theirs: void 0, kind: "structural" });
|
|
920
1024
|
}
|
|
921
1025
|
var idMap = (nodes) => new Map(nodes.filter((n) => n.id).map((n) => [n.id, n]));
|
|
922
1026
|
var arrEq = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
|
|
923
1027
|
|
|
924
1028
|
// src/validate.ts
|
|
925
1029
|
import { validateProject } from "@patterkit/core";
|
|
926
|
-
import { validateConditions, validateInterpolation, hostScopesToSpec, projectScopes } from "@patterkit/compiler";
|
|
1030
|
+
import { validateConditions, validateInterpolation, hostScopesToSpec, projectScopes, extensionPass } from "@patterkit/compiler";
|
|
927
1031
|
|
|
928
1032
|
// src/compile.ts
|
|
929
1033
|
import { basename as basename2, extname, isAbsolute, resolve as resolve3 } from "path";
|
|
@@ -949,7 +1053,19 @@ function compileScenes(loaded) {
|
|
|
949
1053
|
}));
|
|
950
1054
|
}
|
|
951
1055
|
function compileLoaded(loaded) {
|
|
952
|
-
return exportBundle({
|
|
1056
|
+
return exportBundle({
|
|
1057
|
+
project: loaded.project,
|
|
1058
|
+
scenes: compileScenes(loaded),
|
|
1059
|
+
locales: loaded.locales,
|
|
1060
|
+
gameScopes: loaded.gameScopes?.merged,
|
|
1061
|
+
extensions: loadedExtensions(loaded)
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
function loadedExtensions(loaded) {
|
|
1065
|
+
return {
|
|
1066
|
+
manifests: loaded.manifests,
|
|
1067
|
+
shards: loaded.extensionShards.map((shard, i) => ({ path: loaded.extensionShardFiles[i], shard }))
|
|
1068
|
+
};
|
|
953
1069
|
}
|
|
954
1070
|
function defaultBundlePath(loaded) {
|
|
955
1071
|
return `../patter-dist/${basename2(loaded.projectFile).replace(/\.patterproj$/, "")}.patterc`;
|
|
@@ -967,7 +1083,7 @@ function besideBundlePath(loaded, ext) {
|
|
|
967
1083
|
}
|
|
968
1084
|
|
|
969
1085
|
// src/validate.ts
|
|
970
|
-
import { PROJECT_LOCALE_SCENE, projectLayout as projectLayout2 } from "@patterkit/model";
|
|
1086
|
+
import { EXTENSION_SHARD as EXTENSION_SHARD3, PROJECT_LOCALE_SCENE, projectLayout as projectLayout2 } from "@patterkit/model";
|
|
971
1087
|
|
|
972
1088
|
// src/reachability.ts
|
|
973
1089
|
import { disjuncts, latchOf, scopedRef, terms } from "@wildwinter/expr";
|
|
@@ -1130,9 +1246,10 @@ function reachabilityIssues(loaded, compiled) {
|
|
|
1130
1246
|
|
|
1131
1247
|
// src/pack.ts
|
|
1132
1248
|
import JSZip from "jszip";
|
|
1133
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
1249
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync3 } from "fs";
|
|
1134
1250
|
import { dirname as dirname4, join as join4, relative as relative2, sep } from "path";
|
|
1135
|
-
import { parseSource as
|
|
1251
|
+
import { parseSource as parseSource3 } from "@patterkit/core";
|
|
1252
|
+
import { EXTENSION_SHARD as EXTENSION_SHARD2, EXTENSIONS_DIR as EXTENSIONS_DIR2, MANIFEST_EXTENSION as MANIFEST_EXTENSION2 } from "@patterkit/model";
|
|
1136
1253
|
import { ARCHIVE_ENTRY_OPTS } from "@wildwinter/toolkit/archive";
|
|
1137
1254
|
|
|
1138
1255
|
// src/handoff.ts
|
|
@@ -1219,12 +1336,12 @@ function readMarker(text) {
|
|
|
1219
1336
|
}
|
|
1220
1337
|
|
|
1221
1338
|
// src/pack.ts
|
|
1222
|
-
var SHARD_EXTENSIONS = [".patterflow", ".patterloc", ".patterx", ".patterproj"];
|
|
1339
|
+
var SHARD_EXTENSIONS = [".patterflow", ".patterloc", ".patterx", ".patterproj", EXTENSION_SHARD2];
|
|
1223
1340
|
var ENTRY_OPTS = ARCHIVE_ENTRY_OPTS;
|
|
1224
1341
|
async function runPack(startPath) {
|
|
1225
1342
|
const projectFile = findProjectFile(startPath);
|
|
1226
1343
|
const root = dirname4(projectFile);
|
|
1227
|
-
const project =
|
|
1344
|
+
const project = parseSource3(readFileSync4(projectFile, "utf8"));
|
|
1228
1345
|
const unresolved = sidecarIssues(walkFiles(root, CONFLICT_SIDECAR));
|
|
1229
1346
|
if (unresolved.length) {
|
|
1230
1347
|
throw new Error(
|
|
@@ -1232,7 +1349,15 @@ async function runPack(startPath) {
|
|
|
1232
1349
|
` + unresolved.map((i) => ` ${i.file}`).join("\n")
|
|
1233
1350
|
);
|
|
1234
1351
|
}
|
|
1235
|
-
const
|
|
1352
|
+
const shardFiles = SHARD_EXTENSIONS.flatMap((ext) => walkFiles(root, ext)).map((abs) => ({ abs, rel: relative2(root, abs).split(sep).join("/") }));
|
|
1353
|
+
const marked = [...new Set(unresolvedMarkIssues(shardFiles.map((f) => ({ file: f.rel, text: readFileSync4(f.abs, "utf8") }))).map((i) => i.file))];
|
|
1354
|
+
if (marked.length) {
|
|
1355
|
+
throw new Error(
|
|
1356
|
+
`${marked.length} shard(s) still marked with an unresolved merge - resolve the clashes each names under "unresolved", then remove the entries, before packing:
|
|
1357
|
+
` + marked.map((f) => ` ${f}`).join("\n")
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
const files = [...shardFiles, ...extensionManifests(root)].sort((a, b) => a.rel.localeCompare(b.rel));
|
|
1236
1361
|
const scopes = gameScopesSnapshot(root, project);
|
|
1237
1362
|
const handoffs = walkFiles(join4(root, HANDOFF_DIR), ".json").map((abs) => ({ rel: relative2(root, abs).split(sep).join("/"), text: readFileSync4(abs, "utf8") })).filter((f) => {
|
|
1238
1363
|
try {
|
|
@@ -1255,6 +1380,19 @@ async function runPack(startPath) {
|
|
|
1255
1380
|
for (const f of handoffs) zip.file(f.rel, f.text, ENTRY_OPTS);
|
|
1256
1381
|
return zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE", streamFiles: false });
|
|
1257
1382
|
}
|
|
1383
|
+
function extensionManifests(root) {
|
|
1384
|
+
let names;
|
|
1385
|
+
try {
|
|
1386
|
+
names = readdirSync3(join4(root, EXTENSIONS_DIR2), { withFileTypes: true }).filter((e) => e.isFile() && !e.name.startsWith(".") && e.name.endsWith(MANIFEST_EXTENSION2)).map((e) => e.name);
|
|
1387
|
+
} catch {
|
|
1388
|
+
return [];
|
|
1389
|
+
}
|
|
1390
|
+
return names.map((name) => ({ abs: join4(root, EXTENSIONS_DIR2, name), rel: `${EXTENSIONS_DIR2}/${name}` }));
|
|
1391
|
+
}
|
|
1392
|
+
var isManifestEntry = (name) => {
|
|
1393
|
+
const rest = name.startsWith(`${EXTENSIONS_DIR2}/`) ? name.slice(EXTENSIONS_DIR2.length + 1) : void 0;
|
|
1394
|
+
return rest !== void 0 && !rest.includes("/") && rest.endsWith(MANIFEST_EXTENSION2) && rest.length > MANIFEST_EXTENSION2.length;
|
|
1395
|
+
};
|
|
1258
1396
|
|
|
1259
1397
|
// src/validate.ts
|
|
1260
1398
|
function runValidate(loaded) {
|
|
@@ -1266,7 +1404,7 @@ function runValidate(loaded) {
|
|
|
1266
1404
|
const conditions = validateConditions({ project, scenes }, { foreignScopes, gameScopes: merged });
|
|
1267
1405
|
const interpolation = validateInterpolation({ project, scenes, locales }, { foreignScopes, gameScopes: merged });
|
|
1268
1406
|
const gameScopes = gameScopesIssues(loaded, scopes.notes);
|
|
1269
|
-
const hygiene = checkHygiene([loaded.projectFile, ...Object.values(loaded.sceneFiles), ...loaded.localeFiles, ...loaded.authoringFiles]);
|
|
1407
|
+
const hygiene = checkHygiene([loaded.projectFile, ...Object.values(loaded.sceneFiles), ...loaded.localeFiles, ...loaded.authoringFiles, ...loaded.extensionShardFiles]);
|
|
1270
1408
|
const tree = walkFilesByExt(loaded.root, [...SHARD_EXTENSIONS, CONFLICT_SIDECAR, ".patterc"]);
|
|
1271
1409
|
const localisation = locIssues(loaded);
|
|
1272
1410
|
let compiled;
|
|
@@ -1277,8 +1415,9 @@ function runValidate(loaded) {
|
|
|
1277
1415
|
}
|
|
1278
1416
|
const staleBundles = checkBundles(loaded, compiled, tree.get(".patterc"));
|
|
1279
1417
|
if (compiled) gameScopes.push(...unplayableScopes(loaded, compiled));
|
|
1280
|
-
const unresolvedMerges = sidecarIssues(tree.get(CONFLICT_SIDECAR));
|
|
1418
|
+
const unresolvedMerges = [...sidecarIssues(tree.get(CONFLICT_SIDECAR)), ...markedShards(SHARD_EXTENSIONS.flatMap((ext) => tree.get(ext)))];
|
|
1281
1419
|
const orphans = orphanShards(loaded, tree);
|
|
1420
|
+
const extensions = [...extensionIssues(loaded), ...gitattributesHint(loaded)];
|
|
1282
1421
|
const structuralErrors = structural.filter((i) => i.severity !== "warning");
|
|
1283
1422
|
const reachability = compiled && structuralErrors.length === 0 && !conditions.some(isError) ? reachabilityIssues(loaded, compiled) : [];
|
|
1284
1423
|
return {
|
|
@@ -1292,7 +1431,8 @@ function runValidate(loaded) {
|
|
|
1292
1431
|
orphans,
|
|
1293
1432
|
gameScopes,
|
|
1294
1433
|
localisation,
|
|
1295
|
-
|
|
1434
|
+
extensions,
|
|
1435
|
+
ok: !localisation.some(isError) && !extensions.some(isError) && structuralErrors.length === 0 && !conditions.some(isError) && !interpolation.some(isError) && hygiene.length === 0 && staleBundles.length === 0 && unresolvedMerges.length === 0 && orphans.length === 0 && !gameScopes.some(isError)
|
|
1296
1436
|
};
|
|
1297
1437
|
}
|
|
1298
1438
|
var isError = (i) => i.severity === "error";
|
|
@@ -1306,9 +1446,53 @@ function exportBlockers(loaded) {
|
|
|
1306
1446
|
return [
|
|
1307
1447
|
...structural.map((i) => `[${i.code}] ${i.message}`),
|
|
1308
1448
|
...[...conditions, ...interpolation].map((i) => `[${i.field}] ${i.nodeId}: ${i.message} (${i.src})`),
|
|
1309
|
-
...locIssues(loaded).filter(isError).map((i) => `[localisation] ${i.file}: ${i.message}`)
|
|
1449
|
+
...locIssues(loaded).filter(isError).map((i) => `[localisation] ${i.file}: ${i.message}`),
|
|
1450
|
+
...extensionIssues(loaded).filter(isError).map((i) => `[extension] ${i.file}: ${i.message}`)
|
|
1310
1451
|
];
|
|
1311
1452
|
}
|
|
1453
|
+
function extensionIssues(loaded) {
|
|
1454
|
+
const loadIssues = loaded.extensionIssues.map((i) => ({ severity: "error", file: i.file, message: i.message }));
|
|
1455
|
+
if (loaded.project.extensions === void 0 && loaded.manifests.length === 0 && loaded.extensionShards.length === 0) return loadIssues;
|
|
1456
|
+
const { issues } = extensionPass({
|
|
1457
|
+
project: loaded.project,
|
|
1458
|
+
scenes: loaded.scenes,
|
|
1459
|
+
...loadedExtensions(loaded),
|
|
1460
|
+
projectPath: loaded.projectFile,
|
|
1461
|
+
sceneFiles: loaded.sceneFiles
|
|
1462
|
+
});
|
|
1463
|
+
return [...loadIssues, ...issues];
|
|
1464
|
+
}
|
|
1465
|
+
function gitattributesHint(loaded) {
|
|
1466
|
+
if (loaded.extensionShardFiles.length === 0) return [];
|
|
1467
|
+
const files = [];
|
|
1468
|
+
let dir = loaded.root, repo = false;
|
|
1469
|
+
for (; ; ) {
|
|
1470
|
+
const f = join5(dir, ".gitattributes");
|
|
1471
|
+
if (existsSync4(f)) files.push(f);
|
|
1472
|
+
if (existsSync4(join5(dir, ".git"))) {
|
|
1473
|
+
repo = true;
|
|
1474
|
+
break;
|
|
1475
|
+
}
|
|
1476
|
+
const up = dirname5(dir);
|
|
1477
|
+
if (up === dir) break;
|
|
1478
|
+
dir = up;
|
|
1479
|
+
}
|
|
1480
|
+
const consulted = repo ? files : files.filter((f) => dirname5(f) === loaded.root);
|
|
1481
|
+
const texts = consulted.map((f) => {
|
|
1482
|
+
try {
|
|
1483
|
+
return readFileSync5(f, "utf8");
|
|
1484
|
+
} catch {
|
|
1485
|
+
return "";
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
if (!texts.some((t) => /\bmerge=patter\b/.test(t))) return [];
|
|
1489
|
+
if (texts.some((t) => /^\s*\*\.patterextension\b.*\bmerge=patter\b/m.test(t))) return [];
|
|
1490
|
+
return [{
|
|
1491
|
+
severity: "warning",
|
|
1492
|
+
file: consulted[0],
|
|
1493
|
+
message: 'the project has extension data, but .gitattributes has no merge rule for it, so git merges it as plain text: add the line "*.patterextension text eol=lf merge=patter"'
|
|
1494
|
+
}];
|
|
1495
|
+
}
|
|
1312
1496
|
function gameScopesIssues(loaded, notes) {
|
|
1313
1497
|
const out = [];
|
|
1314
1498
|
if (loaded.gameScopesMissing) out.push({ file: loaded.projectFile, severity: "error", message: loaded.gameScopesMissing });
|
|
@@ -1334,7 +1518,8 @@ function orphanShards(loaded, tree = walkFilesByExt(loaded.root, SHARD_EXTENSION
|
|
|
1334
1518
|
".patterflow": { what: "scene", home: layout.flow },
|
|
1335
1519
|
".patterloc": { what: "strings", home: layout.strings },
|
|
1336
1520
|
".patterx": { what: "authoring", home: layout.authoring },
|
|
1337
|
-
".patterproj": { what: "project", home: null }
|
|
1521
|
+
".patterproj": { what: "project", home: null },
|
|
1522
|
+
[EXTENSION_SHARD3]: { what: "extension data", home: layout.extensions }
|
|
1338
1523
|
};
|
|
1339
1524
|
const inside = (file, dir) => {
|
|
1340
1525
|
const r = relative3(dir, file);
|
|
@@ -1398,6 +1583,34 @@ function locIssues(loaded) {
|
|
|
1398
1583
|
return out;
|
|
1399
1584
|
}
|
|
1400
1585
|
var hygieneCache = /* @__PURE__ */ new Map();
|
|
1586
|
+
var markCache = /* @__PURE__ */ new Map();
|
|
1587
|
+
function markedShards(files) {
|
|
1588
|
+
const wanted = new Set(files);
|
|
1589
|
+
for (const file of markCache.keys()) if (!wanted.has(file)) markCache.delete(file);
|
|
1590
|
+
const issues = [];
|
|
1591
|
+
for (const file of files) {
|
|
1592
|
+
let mtimeMs, text;
|
|
1593
|
+
try {
|
|
1594
|
+
mtimeMs = statSync2(file).mtimeMs;
|
|
1595
|
+
} catch {
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
const hit = markCache.get(file);
|
|
1599
|
+
if (hit && hit.mtimeMs === mtimeMs) {
|
|
1600
|
+
issues.push(...hit.issues);
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
try {
|
|
1604
|
+
text = readFileSync5(file, "utf8");
|
|
1605
|
+
} catch {
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
1608
|
+
const found = unresolvedMarkIssues([{ file, text }]);
|
|
1609
|
+
markCache.set(file, { mtimeMs, issues: found });
|
|
1610
|
+
issues.push(...found);
|
|
1611
|
+
}
|
|
1612
|
+
return issues;
|
|
1613
|
+
}
|
|
1401
1614
|
function checkHygiene(files) {
|
|
1402
1615
|
const wanted = new Set(files);
|
|
1403
1616
|
for (const file of hygieneCache.keys()) if (!wanted.has(file)) hygieneCache.delete(file);
|
|
@@ -1433,10 +1646,11 @@ function checkHygiene(files) {
|
|
|
1433
1646
|
}
|
|
1434
1647
|
|
|
1435
1648
|
// src/export.ts
|
|
1649
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
1436
1650
|
import { canonicalStringify as canonicalStringify3 } from "@patterkit/core";
|
|
1437
1651
|
|
|
1438
1652
|
// src/audio-index.ts
|
|
1439
|
-
import { readdirSync as
|
|
1653
|
+
import { readdirSync as readdirSync4 } from "fs";
|
|
1440
1654
|
import { join as join6, relative as relative4, resolve as resolve4, sep as sep2 } from "path";
|
|
1441
1655
|
import { deriveRecordingFolders, DEFAULT_RECORDING_STATUSES } from "@patterkit/model";
|
|
1442
1656
|
var AUDIO_MANIFEST_SCHEMA = "patter/audio@0";
|
|
@@ -1472,7 +1686,7 @@ function scanAudio(loaded) {
|
|
|
1472
1686
|
return pickAudio(rungs.map((r) => {
|
|
1473
1687
|
let files;
|
|
1474
1688
|
try {
|
|
1475
|
-
files =
|
|
1689
|
+
files = readdirSync4(r.dir);
|
|
1476
1690
|
} catch {
|
|
1477
1691
|
files = void 0;
|
|
1478
1692
|
}
|
|
@@ -1501,11 +1715,19 @@ function audioManifestWrite(loaded, snapshot) {
|
|
|
1501
1715
|
// src/export.ts
|
|
1502
1716
|
function refuseUnresolvedMerge(loaded) {
|
|
1503
1717
|
const issues = sidecarIssues(walkFiles(loaded.root, CONFLICT_SIDECAR));
|
|
1504
|
-
if (
|
|
1505
|
-
|
|
1506
|
-
|
|
1718
|
+
if (issues.length) {
|
|
1719
|
+
throw new Error(
|
|
1720
|
+
`${issues.length} unresolved merge conflict(s) - resolve them and delete the .patterconflict sidecar(s) first:
|
|
1507
1721
|
` + issues.map((i) => ` ${i.file}`).join("\n")
|
|
1508
|
-
|
|
1722
|
+
);
|
|
1723
|
+
}
|
|
1724
|
+
const marked = [...new Set(unresolvedMarkIssues(SHARD_EXTENSIONS.flatMap((ext) => walkFiles(loaded.root, ext)).map((file) => ({ file, text: readFileSync6(file, "utf8") }))).map((i) => i.file))];
|
|
1725
|
+
if (marked.length) {
|
|
1726
|
+
throw new Error(
|
|
1727
|
+
`${marked.length} shard(s) still marked with an unresolved merge - resolve the clashes each names under "unresolved", then remove the entries:
|
|
1728
|
+
` + marked.map((f) => ` ${f}`).join("\n")
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1509
1731
|
}
|
|
1510
1732
|
function refuseInvalid(loaded, opts) {
|
|
1511
1733
|
if (opts.allowInvalid) return;
|
|
@@ -1548,7 +1770,7 @@ import { canonicalStringify as canonicalStringify4 } from "@patterkit/core";
|
|
|
1548
1770
|
import { DEFAULT_PAD_AFTER } from "@patterkit/model";
|
|
1549
1771
|
|
|
1550
1772
|
// src/playable-runtime.ts
|
|
1551
|
-
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var ne=Object.defineProperty;var ct=Object.getOwnPropertyDescriptor;var lt=Object.getOwnPropertyNames;var pt=Object.prototype.hasOwnProperty;var dt=(n,e)=>{for(var t in e)ne(n,t,{get:e[t],enumerable:!0})},ut=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of lt(e))!pt.call(n,s)&&s!==t&&ne(n,s,{get:()=>e[s],enumerable:!(r=ct(e,s))||r.enumerable});return n};var gt=n=>ut(ne({},"__esModule",{value:!0}),n);var Mt={};dt(Mt,{Checkpoint:()=>_,DEFAULT_PAD_AFTER:()=>k,Engine:()=>ee,Flow:()=>R,buildTagIndex:()=>Z,compareStages:()=>O,describeBundle:()=>rt,effectiveGameData:()=>st,gameDataFields:()=>nt,gameDataValue:()=>he,nextStage:()=>B,stageIndex:()=>P});function N(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=N(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):N(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"&&!N(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=se(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=N(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 f=s(i.right);if(typeof f!="boolean")throw new u(`\'and\' requires boolean operands, right is ${typeof f}`);return f}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 f=s(i.right);if(typeof f!="boolean")throw new u(`\'or\' requires boolean operands, right is ${typeof f}`);return f}let o=s(i.left),a=s(i.right),c=se(i.left,e),l=se(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!ft(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 ke(o,a);case"!=":return!ke(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 ke(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 se(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 ft=(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 ie(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 ae=n=>Array.isArray(n)?n:n.stages??[],ht=n=>Array.isArray(n)?"flags":n==null?"nothing":typeof n;function P(n,e){return typeof e=="string"?ae(n).indexOf(e):-1}function oe(n,e){if(typeof e!="string")throw new u(`a stage is a name, got ${ht(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=ae(n),s=oe(r,e)-oe(r,t);return s<0?-1:s>0?1:0}function B(n,e){let t=ae(n);return t[Math.min(oe(t,e)+1,t.length-1)]}function ce(n,e,t){switch(n.type){case"boolean":return typeof e=="boolean";case"number":return typeof e=="number";case"string":case"text":case"multiline":return typeof e=="string";case"enum":return typeof e!="string"?!1:e===""&&t?.noneChosen?!0:!Array.isArray(n.values)||n.values.includes(e);case"flags":return Array.isArray(e)&&e.every(r=>typeof r=="string"&&(!Array.isArray(n.values)||n.values.includes(r)));case"quality":return typeof e=="string"&&(!Array.isArray(n.stages)||n.stages.includes(e));default:return!0}}var mt={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},yt=[mt];function Ee(n,e,t){let r=t?.countingCalls??yt;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 x=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??A(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])=>De(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,t){let r=new Map;for(let[i,o]of Object.entries(e)){let a=this.norm(i);this.values[a]=structuredClone(o),r.set(a,o)}let s=[];if(!t?.refit)return s;for(let[i,o]of this.decls){if(!r.has(i))continue;let a=r.get(i);ce(o,a)||(this.values[i]=structuredClone(A(o)),s.push({name:i,value:a}))}return s}};function De(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??A(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(),le=1,Q=class{scopes=new Map;parked=new Map;parkedRefit=new Set;misfits=[];rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},i=new x(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&&(this.note(e,t.load(s,{refit:this.parkedRefit.has(e)})),this.parked.delete(e),this.parkedRefit.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.parkedRefit.delete(e)),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear(),this.parkedRefit.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&(this.parked.delete(t),this.parkedRefit.delete(t));return this}takeMisfits(e){if(!e){let r=this.misfits;return this.misfits=[],r}let t=this.misfits.filter(r=>e(r.scope));return this.misfits=this.misfits.filter(r=>!e(r.scope)),t}note(e,t){for(let r of t)this.misfits.push({scope:e,name:r.name,value:r.value})}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,...De(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(wt(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 Ce(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=Ce(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(),this.parkedRefit.clear());let r=t?.refit===!0;for(let[s,i]of Object.entries(e)){let o=this.scopes.get(s);o?.kind==="owned"?this.note(s,o.bag.load(i,{refit:r})):o||(this.parked.set(s,structuredClone(i)),r?this.parkedRefit.add(s):this.parkedRefit.delete(s))}}saveFragment(){return{version:le,scopes:this.save()}}loadFragment(e){if(e.version!==le)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${le})`);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 Ce(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function wt(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function A(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":case"text":case"multiline":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??"";default:return!1}}var Ie=[{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 pe={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=Re(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=[...Re(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}}},ur=Ie.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function Oe(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 Re(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 bt=/^@[A-Za-z0-9_.]+$/;function*vt(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:bt.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 xt(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function kt(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function Et(n){let e="",t=!1;for(let r of n){if(kt(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function Be(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?Et(r):n}function Ae(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of vt(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?"":xt(s)}return t}function D(n,e){for(let t of n){e(t);let r=t.children;r&&D(r,e)}}function Ct(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||Ct(n.name)}var k=.6;function Y(n){return`cast:${n}`}function X(n){return`qualifier:${n}`}function Ne(n,e){return`speaker:${n}:${e}`}var Fe="speaker:pattern",Te="{name} ({qualifier})";function Le(n,e,t){return n.split("{name}").map(r=>r.split("{qualifier}").join(t)).join(e)}var de={open:"[",close:"]"},Ve="SFX";var L=3;function V(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=V([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,V([...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,V([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=V(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=V([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var Me=n=>n.kind==="line"||n.kind==="text",M=n=>typeof n=="number"&&Number.isFinite(n)?n:void 0;function je(n){let e=new Map,t=(i,o,a)=>{let c=i.kind==="gameEvent"?void 0:M(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=M(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)=>{Me(p)&&(l=h)}),c.forEach((p,h)=>{Me(p)&&t(p,a,h===l)})},s=M(n.padAfterDefault)??k;for(let i of Object.values(n.scenes)){let o=M(i.padAfterDefault)??s;for(let a of i.blocks){let c=M(a.padAfterDefault)??o;for(let l of a.children)r(l,c)}}return e}var Ge=new WeakMap,qe=new WeakMap;function Dt(n){let e=qe.get(n);return e||(e=Z(n),qe.set(n,e)),e}var $e=new WeakMap;function It(n){let e=$e.get(n);return e||(e=je(n),$e.set(n,e)),e}var _e=new WeakMap;function Rt(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 q="Patter",j=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),Pt=n=>n.replace(/%2F/g,"/").replace(/%25/g,"%"),He=Symbol("ownsRegistry"),S={stage:n=>`patter/scene/${j(n)}`,flow:n=>`patter/flow/${j(n)}/`,flowGlobals:n=>`patter/flow/${j(n)}/patter`,flowScene:(n,e)=>`patter/flow/${j(n)}/scene/${j(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[g,y]of Object.entries(d.qualifierLabels??{}))typeof y=="string"&&h.set(`${d.name}\\0${g}`,y);let f=e.speakerPattern??Te;this.defaultSeed=(t.seed??2654435769)>>>0;let me=new Map,ye=new Map,Se=new Map;for(let[d,g]of Object.entries(e.scenes)){this.sceneGameIdToId.set(w(g),d);let y=new Map;for(let m of g.blocks)ye.set(m.id,{sceneId:d}),Se.set(m.id,m),y.set(w(m),m.id),D(m.children,U=>me.set(U.id,U));this.blockGameIdToId.set(d,y)}let we=e.properties??[],te=we.filter(d=>d.shared??!0).map(Qe),it=we.filter(d=>!(d.shared??!0)).map(Qe),ot=new Set(te.map(d=>d.name.toLowerCase())),v=t.registry??new Q,be=!t.registry||t[He]===!0,ve=new x(te,{pathPrefix:"@patter."}),re=[],H=[];try{v.mountOwned("patter",ve,{owner:q}),H.push("patter");let d=new Set;for(let[g,y]of Object.entries(Je(t))){let m=e.scopeRegistry?.scopes.find(at=>at.token===g),U=(m?.declarations??[]).map(Ze);v.defineForeign(g,y,U,{writable:m?.writable??!0,owner:q}),H.push(g),d.add(g)}for(let g of be?e.scopeRegistry?.scopes??[]:[]){if(d.has(g.token)||v.has(g.token))continue;let y=(g.declarations??[]).map(m=>At(m,g.writable));v.defineOwned(g.token,y,{owner:q}),H.push(g.token),re.push(g.token)}}catch(d){for(let g of H)v.remove(g,{keep:!0});throw d}let xe=new Map;for(let[d,g]of Object.entries(e.scenes)){let y=new Set((g.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));xe.set(d,y)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(d,g,y)=>this.emitEngine(d,g,y),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:p,speakerLabels:h,speakerPattern:f,nodeIndex:me,blockIndex:ye,blockById:Se,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:Dt(e),padIndex:It(e),registry:v,ownsRegistry:be,patterBag:ve,hostScopes:re,patterSharedDecls:te,patterLocalDecls:it,patterSharedNames:ot,sceneSharedNames:xe,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}`)),reportRefits:()=>{let d=v.takeMisfits(g=>g==="patter"||g.startsWith("patter/")||re.includes(g));if(d.length!==0)if(t.onRefit)t.onRefit(d);else for(let g of d)console.warn(`Patterplay: the save\'s ${g.scope}.${g.name} was ${JSON.stringify(g.value)}, which no longer fits its declaration; it is back at its default`)},replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??de).open,captionClose:(e.closedCaptions??de).close,captionCharacter:e.closedCaptions?.character||Ve,refSplitCache:new Map,refSplitRevision:v.revision},this.host.reportRefits()}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=c=>(c.setLocale(this.currentLocale),c.setClosedCaptions(this.host.captionsOn),c),s=()=>new n(e,{...this.creationOptions,registry:this.host.registry,[He]:this.host.ownsRegistry}),i=new Set(["patter",...this.host.hostScopes]),o=this.host.ownsRegistry?void 0:Object.fromEntries(Object.entries(this.host.registry.save()).filter(([c])=>i.has(c)||c.startsWith("patter/")));this.release(!0),o&&this.host.registry.load(o,{keepParked:!0,refit:!0});let a=s();try{return a.loadGame(t),r(a)}catch{a.release(!0);let c=this.host.ownsRegistry?t.registry:o;c&&this.host.registry.load(c,{keepParked:!0,refit:!0});let l=s();for(let[p,h]of Object.entries(t.flows)){let f=h.cursor.currentSceneId;try{l.openFlow(p,f!==null?{scene:f}:{})}catch{}}return r(l)}}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 R(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=ue(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=ge(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 ue(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:ge(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)Ue(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 Ue(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,...We(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...w(t)?{gameId:w(t)}:{},name:t.name,...We(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=G(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??k,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=S.flow(e);this.host.registry.discardParked(i),this.host.registry.load(tt(e,t),{keepParked:!0,refit:!0});let o=new R(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),this.host.reportRefits(),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(S.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(S.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(Je(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 P(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=Bt(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:A(e),writable:e.writable??!0}))}splitShared(e){let t=et(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:Ye(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(S.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.version===2?Nt(e):e.registry;i&&(this.host.ownsRegistry?s.load(i,{refit:!0}):s.load(i,{keepParked:!0,refit:!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 Xe(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new R(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}this.host.reportRefits()}},R=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(S.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=ue(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=ge(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(f=>({...f}));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=Rt(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 P(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:Ot(this.pendingPrompt.shown)}:{},selectors:Ye(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=Xe(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?G(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?G(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(f=>f.id),a=new Set(e.map(f=>f.id));if(r.bag===void 0&&(r.bag=o()),!r.bag.some(f=>a.has(f))){if(t==="once")return null;if(i){let f=e[s-1];return r.last=f.id,f}if(r.bag=o(),r.bag.length===0)return null}let c=r.bag.filter(f=>a.has(f)),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(f=>f.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 Ee(e,i=>{try{return ze(W(i,this.context(),pe))}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 ze(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(),pe)}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=ie(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:G(this.host,e.character,e.qualifier),padAfter:this.padOf(e.id),gameData:e.gameData,...r}}}}interpolate(e){return Ae(e,t=>this.getProperty(t))}stripCaptions(e){return Be(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:G(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):k}padOf(e){return this.host.padIndex.get(e)?.resolved??k}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 et(this.host,e)}newLocal(){return new x(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=S.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:q}),this.registered.add(e),this.host.reportRefits()}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,i=!this.sceneBags.has(e)||!this.host.stageBags.has(e);if(!this.sceneBags.has(e)){let o=new x(r.filter(l=>!t.has(l.name.toLowerCase())),{pathPrefix:"@scene."}),a=S.flowScene(this.id,e),c=Ke(this.host.registry,a,o,s!==null);this.registered.add(a),this.sceneBags.set(e,o),s&&s.undo.push(()=>{this.sceneBags.get(e)===o&&(this.host.registry.remove(a,c?{keep:!0}:void 0),this.registered.delete(a),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let o=new x(r.filter(c=>t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),a=Ke(this.host.registry,S.stage(e),o,s!==null);this.host.stageBags.set(e,o),s&&s.undo.push(()=>{this.host.stageBags.get(e)===o&&(this.host.registry.remove(S.stage(e),a?{keep:!0}:void 0),this.host.stageBags.delete(e))})}i&&this.host.reportRefits()}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,A(r))}}};function Ue(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 G(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(Ne(e,t))??o;let a=s(X(t))??n.qualifierDisplay.get(t)??t;return Le(s(Fe)??n.speakerPattern,i,a)}function We(n){return n&&Object.keys(n).length?{gameData:n}:{}}function Ot(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 Ye(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 Xe(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 Ke(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:q}),r&&JSON.stringify(t.save())!==s}function Qe(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Je(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 Bt(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 Ze(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function At(n,e){let t=Ze(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function ue(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function ge(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function et(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=Oe(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function Nt(n){let e={};n.shared?.patter&&(e.patter=n.shared.patter);for(let[t,r]of Object.entries(n.stageBags??{}))e[S.stage(t)]=r;for(let[t,r]of Object.entries(n.flows??{}))Object.assign(e,tt(t,r));return e}function tt(n,e){let t={};e.scopes?.patter&&(t[S.flowGlobals(n)]=e.scopes.patter);for(let[r,s]of Object.entries(e.sceneBags??{}))t[S.flowScene(n,r)]=s;return t}function Ft(n,e){let t={scopes:{},sceneBags:{}},r=S.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 ze(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var Tt=(n,e)=>n.shared??e;function fe(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:Tt(n,e)}}function Lt(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function Vt(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 rt(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)Vt(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>fe(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=>fe(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(Lt)}));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=>fe(o,!0)),scene:r},gameData:i,counts:e}}function nt(n,e){return n.gameDataFields?.[e]??[]}function he(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function st(n,e){let t={};for(let r of n){let s=he(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 gt(Mt);})();\n';
|
|
1773
|
+
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var ie=Object.defineProperty;var gt=Object.getOwnPropertyDescriptor;var ft=Object.getOwnPropertyNames;var ht=Object.prototype.hasOwnProperty;var mt=(r,e)=>{for(var t in e)ie(r,t,{get:e[t],enumerable:!0})},yt=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of ft(e))!ht.call(r,s)&&s!==t&&ie(r,s,{get:()=>e[s],enumerable:!(n=gt(e,s))||n.enumerable});return r};var St=r=>yt(ie({},"__esModule",{value:!0}),r);var _t={};mt(_t,{Checkpoint:()=>U,DEFAULT_PAD_AFTER:()=>E,Engine:()=>ne,Flow:()=>O,buildTagIndex:()=>te,compareStages:()=>N,describeBundle:()=>st,effectiveGameData:()=>ot,extensionDecls:()=>at,extensionSettings:()=>ct,extensionsOf:()=>lt,gameDataFields:()=>it,gameDataValue:()=>ye,nextStage:()=>B,stageIndex:()=>P});function T(r,e){return r!==void 0&&Object.prototype.hasOwnProperty.call(r,e)?r[e]:void 0}function R(r){switch(r[0]){case"b":return{kind:"bool",value:r[1]};case"n":return{kind:"number",value:r[1]};case"s":return{kind:"string",value:r[1]};case"sv":return{kind:"scopedvar",scope:r[1],name:r[2]};case"u":return{kind:"unary",op:r[1],operand:R(r[2])};case"bin":return{kind:"binary",op:r[1],left:R(r[2]),right:R(r[3])};case"call":{let e=r.slice(2).map(R);return{kind:"call",name:r[1],args:e}}case"fd":return{kind:"flagdelta",sign:r[1],name:r[2]}}}var u=class extends Error{constructor(e){super(e),this.name="EvalError"}};function J(r,e,t){let n=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=T(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):T(o,i.name);if(a===void 0){if(n.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"&&!T(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=oe(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=T(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 f=s(i.right);if(typeof f!="boolean")throw new u(`\'and\' requires boolean operands, right is ${typeof f}`);return f}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 f=s(i.right);if(typeof f!="boolean")throw new u(`\'or\' requires boolean operands, right is ${typeof f}`);return f}let o=s(i.left),a=s(i.right),c=oe(i.left,e),l=oe(i.right,e),d=c??l;if(d!==void 0){if(c&&l&&!wt(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,d,">")>v(a,d,">");case">=":return v(o,d,">=")>=v(a,d,">=");case"<":return v(o,d,"<")<v(a,d,"<");case"<=":return v(o,d,"<=")<=v(a,d,"<=");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 Re(o,a);case"!=":return!Re(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(r)}function Re(r,e){if(Array.isArray(r)||Array.isArray(e)){if(!Array.isArray(r)||!Array.isArray(e)||r.length!==e.length)return!1;let t=[...r].sort(),n=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==n[s])return!1;return!0}return r===e}function C(r,e,t){if(typeof r!="number"||typeof e!="number")throw new u(`\'${t}\' requires numeric operands, got ${typeof r} and ${typeof e}`)}function oe(r,e){if(!(r.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(r.scope,r.name)}function v(r,e,t){if(typeof r!="string")throw new u(`\'${t}\' on a quality compares stages, got ${typeof r}`);let n=e.indexOf(r);if(n<0)throw new u(`"${r}" is not a stage of this quality (stages: ${e.join(", ")})`);return n}var wt=(r,e)=>r.length===e.length&&r.every((t,n)=>t===e[n]);function L(r){if(Number.isNaN(r)||!Number.isFinite(r))return 0;let e=Math.trunc(r)%4294967296;return e<0?e+4294967296:e}function ae(r){let e=L(r);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 le=r=>Array.isArray(r)?r:r.stages??[],vt=r=>Array.isArray(r)?"flags":r==null?"nothing":typeof r;function P(r,e){return typeof e=="string"?le(r).indexOf(e):-1}function ce(r,e){if(typeof e!="string")throw new u(`a stage is a name, got ${vt(e)}`);let t=r.indexOf(e);if(t<0)throw new u(`"${e}" is not a stage of this quality (stages: ${r.join(", ")})`);return t}function N(r,e,t){let n=le(r),s=ce(n,e)-ce(n,t);return s<0?-1:s>0?1:0}function B(r,e){let t=le(r);return t[Math.min(ce(t,e)+1,t.length-1)]}function de(r,e,t){switch(r.type){case"boolean":return typeof e=="boolean";case"number":return typeof e=="number";case"string":case"text":case"multiline":return typeof e=="string";case"enum":return typeof e!="string"?!1:e===""&&t?.noneChosen?!0:!Array.isArray(r.values)||r.values.includes(e);case"flags":return Array.isArray(e)&&e.every(n=>typeof n=="string"&&(!Array.isArray(r.values)||r.values.includes(n)));case"quality":return typeof e=="string"&&(!Array.isArray(r.stages)||r.stages.includes(e));default:return!0}}var bt={name:"check_flags",count:r=>Math.max(1,r.args.length-1)},xt=[bt];function Ce(r,e,t){let n=t?.countingCalls??xt;return Q(r,t?.want??!0,e,n)}function Q(r,e,t,n){if(r.kind==="binary"&&(r.op==="and"||r.op==="or")){let s=Q(r.left,e,t,n),i=Q(r.right,e,t,n);return r.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(r.kind==="unary"&&r.op==="not")return Q(r.operand,!e,t,n);if(r.kind==="call"){let s=n.find(i=>i.name===r.name);if(s){let i=s.count(r),o=t(r);return e?o?i:0:o?0:1}}return t(r)===e?1:0}var k=class r{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(n=>n.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let n=this.norm(t.name);this.decls.set(n,t),this.values[n]=structuredClone(t.default??A(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,n){let s=this.norm(e);if(!n?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:n?.silent??!1,reason:n?.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])=>Ie(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new r([],{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,t){let n=new Map;for(let[i,o]of Object.entries(e)){let a=this.norm(i);this.values[a]=structuredClone(o),n.set(a,o)}let s=[];if(!t?.refit)return s;for(let[i,o]of this.decls){if(!n.has(i))continue;let a=n.get(i);de(o,a)||(this.values[i]=structuredClone(A(o)),s.push({name:i,value:a}))}return s}};function Ie(r,e,t,n,s=""){let i=n??r.name.toLowerCase();return{name:i,path:s+i,type:r.type,value:e,default:r.default??A(r),...r.values!==void 0?{values:r.values}:{},...r.stages!==void 0?{stages:r.stages}:{},writable:t??r.writable??!0}}var kt=r=>r.toLowerCase(),pe=1,z=class{scopes=new Map;parked=new Map;parkedRefit=new Set;misfits=[];rev=0;get revision(){return this.rev}defineOwned(e,t,n){let s=typeof n=="string"?{pathPrefix:n}:n??{},i=new k(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,n){this.assertFree(e,n?.owner),this.scopes.set(e,{kind:"owned",bag:t,...n?.owner!==void 0?{owner:n.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(this.note(e,t.load(s,{refit:this.parkedRefit.has(e)})),this.parked.delete(e),this.parkedRefit.delete(e)),this}remove(e,t){let n=this.scopes.get(e);if(!n)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&n.kind==="owned"&&(this.parked.set(e,n.bag.save()),this.parkedRefit.delete(e)),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear(),this.parkedRefit.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&(this.parked.delete(t),this.parkedRefit.delete(t));return this}takeMisfits(e){if(!e){let n=this.misfits;return this.misfits=[],n}let t=this.misfits.filter(n=>e(n.scope));return this.misfits=this.misfits.filter(n=>!e(n.scope)),t}note(e,t){for(let n of t)this.misfits.push({scope:e,name:n.name,value:n.value})}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,n=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??kt,a=new Map;for(let c of n)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 n=this.scopes.get(e);if(n)return n.kind==="owned"?n.bag.get(t):n.resolver.get(n.norm(t))}set(e,t,n,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,n,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,n)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,n]of this.scopes){let s=n.owner!==void 0?{owner:n.owner}:{};if(n.kind==="owned")for(let i of n.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of n.decls)e.push({scope:t,...s,...Ie(o,n.resolver.get(i),this.foreignWritable(n,i),i,`${t}.`)})}return e}toEvalContext(e,t){let n=this.view(t?.aliases),s={};for(let[o,a]of n)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(n);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=n.get(o);return c?i.get(o)?.get(Et(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[n,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${n}\' names \'${s}\', which is not registered`);t.set(n,i)}return t}qualityLadders(e){let t=new Map;for(let[n,s]of e)for(let[i,o]of De(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(n);a||(a=new Map,t.set(n,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[n,s]of this.view(e?.aliases)){let i=De(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(n,o)}return{properties:t}}save(){let e={};for(let[t,n]of this.scopes)n.kind==="owned"&&(e[t]=n.bag.save());for(let[t,n]of this.parked)e[t]=structuredClone(n);return e}load(e,t){t?.keepParked||(this.parked.clear(),this.parkedRefit.clear());let n=t?.refit===!0;for(let[s,i]of Object.entries(e)){let o=this.scopes.get(s);o?.kind==="owned"?this.note(s,o.bag.load(i,{refit:n})):o||(this.parked.set(s,structuredClone(i)),n?this.parkedRefit.add(s):this.parkedRefit.delete(s))}}saveFragment(){return{version:pe,scopes:this.save()}}loadFragment(e){if(e.version!==pe)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${pe})`);this.load(e.scopes)}assertFree(e,t){let n=this.scopes.get(e);if(!n)return;let s=n.owner!==void 0?` by ${n.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function De(r){return r.kind==="foreign"?[...r.decls.entries()]:r.bag.declarations().map(e=>[r.bag.normalise(e.name),e])}function Et(r){return r.kind==="foreign"?r.norm:e=>r.bag.normalise(e)}function A(r){if(r.default!==void 0)return r.default;switch(r.type){case"boolean":return!1;case"number":return 0;case"string":case"text":case"multiline":return"";case"enum":return r.values?.[0]??"";case"flags":return[];case"quality":return r.stages?.[0]??"";default:return!1}}var Oe=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function j(r){return r.ctx.host??{}}var ue={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(r,e){if(r.length!==2)throw new u("random(a, b) requires exactly 2 arguments");let t=j(e).nextRandom;if(!t)throw new u("random() called without a PRNG in context");let n=e.evaluate(r[0]),s=e.evaluate(r[1]);if(typeof n!="number"||typeof s!="number")throw new u("random(a, b) arguments must be numbers");if(!Number.isInteger(n)||!Number.isInteger(s))throw new u("random(a, b) arguments must be integers");let i=Math.min(n,s),o=Math.max(n,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:Ne("check_flags"),eval(r,e){let t=Pe(r[0],e,"check_flags");for(let n=1;n<r.length;n++){let s=r[n];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:Ne("set_flags"),eval(r,e){let t=[...Pe(r[0],e,"set_flags")];for(let n=1;n<r.length;n++){let s=r[n];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:X("visits"),eval:(r,e)=>j(e).visits?.(Y(r,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:X("seen"),eval:(r,e)=>(j(e).visits?.(Y(r,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:X("patter_visits"),eval:(r,e)=>j(e).patterVisits?.(Y(r,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:X("patter_seen"),eval:(r,e)=>(j(e).patterVisits?.(Y(r,e,"patter_seen"))??0)>0}}},Sn=Oe.map(r=>r.token).filter(r=>r!=="patter"&&r!=="scene");function Be(r,e){let t=r.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function Y(r,e,t){let n=e.evaluate(r[0]);if(typeof n!="string")throw new u(`${t}(id) requires a string node id`);return n}function X(r){return(e,t)=>{let n=e[0];n&&n.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${r}(id): the argument must be a string id literal (a scene / block / node id)`})}}function Pe(r,e,t){if(!r)throw new u(`${t}() requires at least one argument (the flags variable)`);let n=e.evaluate(r);if(Array.isArray(n))return n;if(n===!1||n===null||n===void 0)return[];throw new u(`${t}() first argument must be a flags property`)}function Ne(r){return(e,t)=>{if(e.length===0)return;let n=e[0];if(n.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${r}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(n.scope)?.get(n.name);if(s&&s.type!=="flags"){let i=n.scope===t.defaultScope?n.name:`${n.scope}.${n.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${r}(): \'@${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:`${r}(): 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:`${r}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var Rt=/^@[A-Za-z0-9_.]+$/;function*Ct(r){let e="",t=0;for(;t<r.length;){let n=r[t];if(n==="{"&&r[t+1]==="{"){e+="{",t+=2;continue}if(n==="}"&&r[t+1]==="}"){e+="}",t+=2;continue}if(n==="{"){let s=r.indexOf("}",t+1);if(s!==-1){let i=r.slice(t,s+1),o=r.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:Rt.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=n,t+=1}e&&(yield{kind:"text",value:e})}function Dt(r){return Array.isArray(r)?r.join(", "):typeof r=="boolean"?r?"true":"false":String(r)}function It(r){return r===" "||r===" "||r===`\n`||r==="\\r"||r==="\\f"||r==="\\v"}function Ot(r){let e="",t=!1;for(let n of r){if(It(n)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=n}return e}function Ae(r,e,t){if(e.length===0||r.indexOf(e)<0)return r;let n="",s=0,i=!1;for(;s<r.length;){if(r.startsWith(e,s)){let o=r.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}n+=r.slice(s);break}n+=r[s],s+=1}return i?Ot(n):r}function Fe(r,e){if(r.indexOf("{")<0)return r;let t="";for(let n of Ct(r)){if(n.kind==="text"){t+=n.value;continue}if(!n.ref){t+=n.raw;continue}let s=e(n.ref);t+=s===void 0?"":Dt(s)}return t}function D(r,e){for(let t of r){e(t);let n=t.children;n&&D(n,e)}}function Pt(r){return r.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function y(r){let e=r.gameId?.trim();return e||Pt(r.name)}var E=.6;function Z(r){return`cast:${r}`}function ee(r){return`qualifier:${r}`}function Te(r,e){return`speaker:${r}:${e}`}var Le="speaker:pattern",je="{name} ({qualifier})";function Me(r,e,t){return r.split("{name}").map(n=>n.split("{qualifier}").join(t)).join(e)}var ge={open:"[",close:"]"},Ve="SFX";var M=3;function V(r){let e=new Set,t=[];for(let n of r)e.has(n)||(e.add(n),t.push(n));return t}function te(r){let e=new Map,t=(n,s)=>{let i=V([...s,...n.tags??[]]);if(e.set(n.id,i),n.type==="group"&&n.prompt&&e.set(n.prompt.id,V([...i,...n.prompt.tags??[]])),n.type==="group")for(let o of n.children)t(o,i);else for(let o of n.beats??[])e.set(o.id,V([...i,...o.tags??[]]))};for(let n of Object.values(r.scenes)){let s=V(n.tags??[]);e.set(n.id,s);for(let i of n.blocks){let o=V([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var Ge=r=>r.kind==="line"||r.kind==="text",G=r=>typeof r=="number"&&Number.isFinite(r)?r:void 0;function qe(r){let e=new Map,t=(i,o,a)=>{let c=i.kind==="gameEvent"?void 0:G(i.padAfter),l=c??o;a&&l<0&&(l=0),e.set(i.id,c!==void 0?{own:c,resolved:l}:{resolved:l})},n=(i,o)=>{let a=G(i.padAfterDefault)??o;if(i.type==="group"){i.prompt&&t(i.prompt,a,!1);for(let d of i.children)n(d,a);return}let c=i.beats??[],l=-1;c.forEach((d,h)=>{Ge(d)&&(l=h)}),c.forEach((d,h)=>{Ge(d)&&t(d,a,h===l)})},s=G(r.padAfterDefault)??E;for(let i of Object.values(r.scenes)){let o=G(i.padAfterDefault)??s;for(let a of i.blocks){let c=G(a.padAfterDefault)??o;for(let l of a.children)n(l,c)}}return e}var $e=new WeakMap,_e=new WeakMap;function Nt(r){let e=_e.get(r);return e||(e=te(r),_e.set(r,e)),e}var He=new WeakMap;function Bt(r){let e=He.get(r);return e||(e=qe(r),He.set(r,e)),e}var Ue=new WeakMap;function At(r){let e=Ue.get(r);if(e)return e;e=new Map;for(let t of Object.values(r.scenes))for(let n of t.blocks)D(n.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 Ue.set(r,e),e}function I(r,e){return r.tagIndex.get(e)?.slice()}var _="Patter",q=r=>r.replace(/%/g,"%25").replace(/\\//g,"%2F"),Ft=r=>r.replace(/%2F/g,"/").replace(/%25/g,"%"),We=Symbol("ownsRegistry"),w={stage:r=>`patter/scene/${q(r)}`,flow:r=>`patter/flow/${q(r)}/`,flowGlobals:r=>`patter/flow/${q(r)}/patter`,flowScene:(r,e)=>`patter/flow/${q(r)}/scene/${q(e)}`},U=class{constructor(e){this.journal=e}journal},ne=class r{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 n=t.locale??e.locales.default,s=e.strings;this.allStrings=s,this.currentLocale=n;let i=s[n]??{},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 p of e.cast??[])p.displayName&&l.set(p.name,p.displayName);let d=new Map;for(let p of e.qualifiers??[])p?.gameId&&d.set(p.gameId,p.name);let h=new Map;for(let p of e.cast??[])for(let[g,S]of Object.entries(p.qualifierLabels??{}))typeof S=="string"&&h.set(`${p.name}\\0${g}`,S);let f=e.speakerPattern??je;this.defaultSeed=(t.seed??2654435769)>>>0;let Se=new Map,we=new Map,ve=new Map;for(let[p,g]of Object.entries(e.scenes)){this.sceneGameIdToId.set(y(g),p);let S=new Map;for(let m of g.blocks)we.set(m.id,{sceneId:p}),ve.set(m.id,m),S.set(y(m),m.id),D(m.children,K=>Se.set(K.id,K));this.blockGameIdToId.set(p,S)}let be=e.properties??[],re=be.filter(p=>p.shared??!0).map(ze),dt=be.filter(p=>!(p.shared??!0)).map(ze),pt=new Set(re.map(p=>p.name.toLowerCase())),x=t.registry??new z,xe=!t.registry||t[We]===!0,ke=new k(re,{pathPrefix:"@patter."}),se=[],W=[];try{x.mountOwned("patter",ke,{owner:_}),W.push("patter");let p=new Set;for(let[g,S]of Object.entries(Ye(t))){let m=e.scopeRegistry?.scopes.find(ut=>ut.token===g),K=(m?.declarations??[]).map(tt);x.defineForeign(g,S,K,{writable:m?.writable??!0,owner:_}),W.push(g),p.add(g)}for(let g of xe?e.scopeRegistry?.scopes??[]:[]){if(p.has(g.token)||x.has(g.token))continue;let S=(g.declarations??[]).map(m=>jt(m,g.writable));x.defineOwned(g.token,S,{owner:_}),W.push(g.token),se.push(g.token)}}catch(p){for(let g of W)x.remove(g,{keep:!0});throw p}let Ee=new Map;for(let[p,g]of Object.entries(e.scenes)){let S=new Set((g.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));Ee.set(p,S)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(p,g,S)=>this.emitEngine(p,g,S),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:d,speakerLabels:h,speakerPattern:f,nodeIndex:Se,blockIndex:we,blockById:ve,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:Nt(e),padIndex:Bt(e),registry:x,ownsRegistry:xe,patterBag:ke,hostScopes:se,patterSharedDecls:re,patterLocalDecls:dt,patterSharedNames:pt,sceneSharedNames:Ee,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(p=>console.warn(`Patterplay: ${p.kind} on ${p.node} in flow \'${p.flow}\' failed, played through: ${p.message}`)),reportRefits:()=>{let p=x.takeMisfits(g=>g==="patter"||g.startsWith("patter/")||se.includes(g));if(p.length!==0)if(t.onRefit)t.onRefit(p);else for(let g of p)console.warn(`Patterplay: the save\'s ${g.scope}.${g.name} was ${JSON.stringify(g.value)}, which no longer fits its declaration; it is back at its default`)},replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??ge).open,captionClose:(e.closedCaptions??ge).close,captionCharacter:e.closedCaptions?.character||Ve,refSplitCache:new Map,refSplitRevision:x.revision},this.host.reportRefits()}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(),n=c=>(c.setLocale(this.currentLocale),c.setClosedCaptions(this.host.captionsOn),c),s=()=>new r(e,{...this.creationOptions,registry:this.host.registry,[We]:this.host.ownsRegistry}),i=new Set(["patter",...this.host.hostScopes]),o=this.host.ownsRegistry?void 0:Object.fromEntries(Object.entries(this.host.registry.save()).filter(([c])=>i.has(c)||c.startsWith("patter/")));this.release(!0),o&&this.host.registry.load(o,{keepParked:!0,refit:!0});let a=s();try{return a.loadGame(t),n(a)}catch{a.release(!0);let c=this.host.ownsRegistry?t.registry:o;c&&this.host.registry.load(c,{keepParked:!0,refit:!0});let l=s();for(let[d,h]of Object.entries(t.flows)){let f=h.cursor.currentSceneId;try{l.openFlow(d,f!==null?{scene:f}:{})}catch{}}return n(l)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:n,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 O(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(n,s),a}runFlow(e,t,n){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:n}).advanceToStop().played;if(!s.goto(t,n))throw new Error(`runFlow: address not found: ${t}${n===void 0?"":` / ${n}`}`);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 n=fe(this.host,e);if(n===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:n};let s=he(this.host,n,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:n,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 fe(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:he(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?y(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?y(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 n=this.resolveSceneRef(e),s=this.resolveBlockRef(n,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 n=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(n!=null?this.host.blockById.get(n):void 0)?.gameData}}extensionsForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.extensions}}extensionsForBlock(e,t){let n=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(n!=null?this.host.blockById.get(n):void 0)?.extensions}}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),n=t!=null?this.host.bundle.scenes[t]:void 0;if(!n)return[];let s=new Set;for(let i of n.blocks)Ke(i.children,s);return[...s]}castForBlock(e,t){let n=this.resolveSceneRef(e),s=this.resolveBlockRef(n,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return Ke(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...y(e)?{gameId:y(e)}:{},name:e.name,...Je(e.gameData),...b(e.extensions),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...y(t)?{gameId:y(t)}:{},name:t.name,...Je(t.gameData),...b(t.extensions),...this.tagsField(t.id),children:t.children.map(n=>this.outlineNode(n))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let n of t.blocks)D(n.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:n.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),n={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){n.character=e.character;let i=this.host.defaultStrings[Z(e.character)]??this.host.castDisplay.get(e.character);i!==void 0&&(n.characterName=i)}if(e.direction!==void 0&&(n.direction=e.direction),e.qualifier!==void 0){n.qualifier=e.qualifier;let i=this.host.defaultStrings[ee(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);i!==void 0&&(n.qualifierName=i)}let s=$(this.host,e.character,e.qualifier,!0);s!==void 0&&(n.speakerLabel=s)}if(e.kind==="line"||e.kind==="text"){let s=this.host.padIndex.get(e.id);n.padAfter=s?.resolved??E,s?.own!==void 0&&(n.ownPadAfter=s.own);let i=this.host.defaultStrings[e.id];i!==void 0&&(n.text=i)}return e.gameData&&Object.keys(e.gameData).length&&(n.gameData=e.gameData),e.extensions&&Object.keys(e.extensions).length&&(n.extensions=e.extensions),t&&t.length&&(n.tags=t),n}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,n]of this.host.stageBags)e.push({bag:n,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 U(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let n=t.length-1;n>=0;n--)t[n]();r.forget(e.journal)}commit(e){this.endCheckpoint(e),r.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:M,...t.snapshot(),...Vt(e,this.host.registry.save())}}restoreFlow(e,t){this.refuseInCheckpoint("restoreFlow");let n=t.version;if(n!==2&&n!==M)throw new Error(`unsupported flow state version: ${String(n)}`);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=w.flow(e);this.host.registry.discardParked(i),this.host.registry.load(rt(e,t),{keepParked:!0,refit:!0});let o=new O(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),this.host.reportRefits(),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(w.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let n of this.flowsById.values())n.releaseBags(e),n.close();this.flowsById.clear();let t=this.host.registry;for(let n of this.host.stageBags.keys())t.remove(w.stage(n),{keep:e});this.host.stageBags.clear();for(let n of["patter",...this.host.hostScopes])t.has(n)&&t.remove(n,{keep:e});for(let n of Object.keys(Ye(this.creationOptions)))t.has(n)&&t.remove(n)}getProperty(e){let{scope:t,name:n}=this.splitShared(e);return this.host.registry.get(t,n)}setProperty(e,t){let{scope:n,name:s}=this.splitShared(e),i=this.host.journal?this.host.registry.get(n,s):void 0;this.host.registry.set(n,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(n,s,i,{host:!0}))}stageIndex(e,t){return P(this.ladderOf(e),t)}compareStages(e,t,n){return N(this.ladderOf(e),t,n)}nextStage(e,t){return B(this.ladderOf(e),t)}ladderOf(e){let{scope:t,name:n}=this.splitShared(e),s=Lt(this.host,t,n);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,n){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...n?{scene:n}:{}})}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:A(e),writable:e.writable??!0}))}splitShared(e){let t=nt(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,n]of this.flowsById)e[t]=n.snapshot();return{version:M,...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!==M)throw new Error(`unsupported save version: ${String(t)}`);let n=e.flows;if(typeof n!="object"||n===null||Array.isArray(n))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(w.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.version===2?Mt(e):e.registry;i&&(this.host.ownsRegistry?s.load(i,{refit:!0}):s.load(i,{keepParked:!0,refit:!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 et(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new O(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}this.host.reportRefits()}},O=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 n=this.currentSceneId;if(n===null)return;let s=this.sceneBagFor(n,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,n){this.id=e,this.host=t,this.rngState=L(n),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(),n=this.evalCtx.scopes;for(let s of Object.keys(n))delete n[s];Object.assign(n,t.scopes),n.patter=this.patterResolver,n.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let n=t.toLowerCase();if(e==="patter")return H(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(n);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:H(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(n)}return this.registryQualities?.(e,t)??H(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(n)}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(w.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 n=this.host.blockIndex.get(t);if(!n)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(n.sceneId),this.stack=[{sceneId:n.sceneId,containerId:t,index:0}],this.enter(t)}else{let n=e??Object.keys(this.host.bundle.scenes)[0],s=n?this.host.bundle.scenes[n]:void 0;if(!s)throw new Error(n?`unknown scene: ${n}`:"no scenes in bundle");this.enterSceneSetup(n);let i=s.blocks[0];i&&(this.stack=[{sceneId:n,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 n=fe(this.host,e);if(n===void 0)return!1;let s;return t!==void 0&&(s=he(this.host,n,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??n,"jump"),this.settle(),!0):(this.begin(n,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:n,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:d}=this,h=this.stack.map(f=>({...f}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:n,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:d,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 n=this.childrenOf(t.containerId);if(!n){this.stack.pop();continue}let s=t.index;for(;t.index<n.length&&!this.eligible(n[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:n.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:n[t.index]?.id??null}),t.index>=n.length){this.stack.pop();continue}this.enterChild(n[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,n]of this.sceneBags)e.push({bag:n,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=At(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 n=t.options.find(o=>o.id===e);if(!n)throw new Error(`unknown choice option: ${e}`);if(!n.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&&n.prompt?{beat:i,shown:{...n.prompt}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:n}=this.splitRef(e);return t==="patter"?this.patterResolver.get(n):t==="scene"?this.sceneResolver.get(n):this.host.registry.get(t,n)}setProperty(e,t){this.writeProperty(e,t,!0)}stageIndex(e,t){return P(this.ladderOf(e),t)}compareStages(e,t,n){return N(this.ladderOf(e),t,n)}nextStage(e,t){return B(this.ladderOf(e),t)}ladderOf(e){let{scope:t,name:n}=this.splitRef(e);this.context();let s=this.stagesFor(t,n);if(!s)throw new Error(`\'${e}\' is not a quality`);return s}writeProperty(e,t,n){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,n?{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(({extensions:e,...t})=>({...t}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:Tt(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(n=>n.sceneId)]))t!==null&&this.host.bundle.scenes[t]&&this.ensureSceneBags(t)}restoreCursor(e){this.rngState=L(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(n=>{let{nextId:s,...i}=n;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 n=this.host.nodeIndex.get(t.activeSnippetId);n&&n.type==="snippet"&&(this.activeSnippet=n)}if(this.selectors=et(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let n=new Map,s=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);if(!o)continue;n.set(i.id,o);let{prompt:a,extensions:c,...l}=i,d=a?.kind==="line"&&a.speakerLabel===void 0?$(this.host,a.character,a.qualifier):void 0;s.push({...l,...a?{prompt:{...a,padAfter:a.padAfter??this.promptPadOf(o),...d!==void 0?{speakerLabel:d}:{}}}:{},...b(o.extensions)})}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:n})}if(this.pendingPrompt=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let n=this.host.nodeIndex.get(this.pendingPromptOwnerId),s=n?this.authoredPromptOf(n):void 0,i=t.pendingPrompt,o=i?.kind==="line"&&i.speakerLabel===void 0?$(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 n=this.selectChild(e);n&&this.enterChild(n)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let n=this.host.nodeIndex.get(e);if(n&&n.type==="group")return n.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],n=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,...b(o.extensions)}),n.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:n};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 n,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}n=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),n=a.sceneId,s=e}this.enter(s);let o={sceneId:n,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)})),n=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(n.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(n[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(n,i,a):s==="specificity"?this.pickSpecificity(n,i,a):this.pickSequential(n,i,a));default:return null}}pickSequential(e,t,n){let s=e.length,i=n.seq??0;return n.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,n){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(f=>f.id),a=new Set(e.map(f=>f.id));if(n.bag===void 0&&(n.bag=o()),!n.bag.some(f=>a.has(f))){if(t==="once")return null;if(i){let f=e[s-1];return n.last=f.id,f}if(n.bag=o(),n.bag.length===0)return null}let c=n.bag.filter(f=>a.has(f)),l=n.last!==void 0&&c.length>1?c.indexOf(n.last):-1,d=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&d>=l&&d++;let h=c[d];return n.bag.splice(n.bag.indexOf(h),1),n.last=h,e.find(f=>f.id===h)}pickSpecificity(e,t,n){let s=e;if(t!=="repeat"){n.bag===void 0&&(n.bag=e.map(d=>d.id));let l=new Set(n.bag);if(s=e.filter(d=>l.has(d.id)),s.length===0)return t==="stick"&&n.last!==void 0?e.find(d=>d.id===n.last)??null:null}let i=-1,a=s.map(l=>{let d=this.specScore(l);return d>i&&(i=d),{c:l,s:d}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=n.last!==void 0?a.findIndex(h=>h.id===n.last):-1,d=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&d>=l&&d++,c=a[d]}return t!=="repeat"&&(n.bag=n.bag.filter(l=>l!==c.id)),n.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,n){return Ce(e,i=>{try{return Xe(J(i,this.context(),ue))}catch(o){return this.reportError("best-match",n.id,n.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,n=this.host.journal,s;if(n&&(s=e.shared?n.selectors:n.flowSelectors.get(this),s||(s=new Set,n.flowSelectors.set(this,s))),n&&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;n.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 n of e??[]){let s;try{s=this.evalExpr(n.value);let i=this.host.tracing?this.getProperty(n.target):void 0;this.writeProperty(n.target,s,!1),this.host.tracing&&this.emit({type:"write",target:n.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,n.value,i)}}}eligible(e){if(!e.condition)return!0;try{return Xe(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,n,s){let i=s instanceof Error?s.message:String(s),o=n?.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 J(this.conditionAst(e),this.context(),ue)}conditionAst(e){let t=$e.get(e);return t||(t=R(e.ast),$e.set(e,t)),t}enter(e){let t=this.visitCounts,n=t.get(e);t.set(e,(n??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(()=>{n===void 0?t.delete(e):t.set(e,n),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=ae(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=I(this.host,e.id),n=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:e.gameData,...b(e.extensions),...n};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),padAfter:this.padOf(e.id),gameData:e.gameData,...b(e.extensions),...n};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:$(this.host,e.character,e.qualifier),padAfter:this.padOf(e.id),gameData:e.gameData,...b(e.extensions),...n}}}}interpolate(e){return Fe(e,t=>this.getProperty(t))}stripCaptions(e){return Ae(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 n=this.interpolate(this.resolveString(t.id)),s=this.padOf(t.id);return t.kind==="line"?{kind:"line",text:this.captionLine(n),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction,qualifier:t.qualifier,qualifierName:this.resolveQualifierName(t.qualifier),speakerLabel:$(this.host,t.character,t.qualifier),padAfter:s}:{kind:"text",text:n,padAfter:s}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let n=I(this.host,e.id),s=n&&n.length?{tags:n}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,padAfter:this.padOf(e.id),gameData:e.gameData,...b(e.extensions),...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,...b(e.extensions),...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(n=>n.kind==="line"||n.kind==="text")}firstTextSnippetIn(e){let t;return D(e,n=>{!t&&n.type==="snippet"&&(n.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=n)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let n=this.host.defaultStrings[e];return n!==void 0?`<Untranslated: ${e}> ${n}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=Z(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):E}padOf(e){return this.host.padIndex.get(e)?.resolved??E}resolveQualifierName(e){if(e===void 0||this.host.emitIds)return;let t=ee(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return nt(this.host,e)}newLocal(){return new k(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=w.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:_}),this.registered.add(e),this.host.reportRefits()}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,n=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal,i=!this.sceneBags.has(e)||!this.host.stageBags.has(e);if(!this.sceneBags.has(e)){let o=new k(n.filter(l=>!t.has(l.name.toLowerCase())),{pathPrefix:"@scene."}),a=w.flowScene(this.id,e),c=Qe(this.host.registry,a,o,s!==null);this.registered.add(a),this.sceneBags.set(e,o),s&&s.undo.push(()=>{this.sceneBags.get(e)===o&&(this.host.registry.remove(a,c?{keep:!0}:void 0),this.registered.delete(a),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let o=new k(n.filter(c=>t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),a=Qe(this.host.registry,w.stage(e),o,s!==null);this.host.stageBags.set(e,o),s&&s.undo.push(()=>{this.host.stageBags.get(e)===o&&(this.host.registry.remove(w.stage(e),a?{keep:!0}:void 0),this.host.stageBags.delete(e))})}i&&this.host.reportRefits()}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let n of e.sceneProps??[]){if(!n.temporary)continue;let s=n.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,A(n))}}};function Ke(r,e){D(r,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let n of t.beats??[])n.kind==="line"&&n.character&&e.add(n.character)})}function $(r,e,t,n=!1){if(!e||r.emitIds)return;let s=c=>(n?void 0:r.strings[c])??r.defaultStrings[c],i=s(Z(e))??r.castDisplay.get(e)??e;if(t===void 0)return i;let o=r.speakerLabels.get(`${e}\\0${t}`);if(o!==void 0)return s(Te(e,t))??o;let a=s(ee(t))??r.qualifierDisplay.get(t)??t;return Me(s(Le)??r.speakerPattern,i,a)}function Je(r){return r&&Object.keys(r).length?{gameData:r}:{}}function b(r){return r&&Object.keys(r).length?{extensions:r}:{}}function Tt(r){let e={kind:r.kind,text:r.text};return r.character!==void 0&&(e.character=r.character),r.characterName!==void 0&&(e.characterName=r.characterName),r.direction!==void 0&&(e.direction=r.direction),r.qualifier!==void 0&&(e.qualifier=r.qualifier),r.qualifierName!==void 0&&(e.qualifierName=r.qualifierName),r.speakerLabel!==void 0&&(e.speakerLabel=r.speakerLabel),typeof r.padAfter=="number"&&(e.padAfter=r.padAfter),e}function Ze(r){let e={};for(let[t,n]of r){let s={};n.seq!==void 0&&(s.seq=n.seq),n.bag&&(s.bag=[...n.bag]),n.last!==void 0&&(s.last=n.last),e[t]=s}return e}function et(r){let e=new Map;for(let[t,n]of Object.entries(r??{})){let s={};n.seq!==void 0&&(s.seq=n.seq),n.bag&&(s.bag=[...n.bag]),n.last!==void 0&&(s.last=n.last),e.set(t,s)}return e}function Qe(r,e,t,n){let s=n?JSON.stringify(t.save()):"";return r.mountOwned(e,t,{owner:_}),n&&JSON.stringify(t.save())!==s}function ze(r){return{name:r.name,type:r.type,values:r.values,stages:r.stages,default:r.default}}function Ye(r){return{...r.world?{world:r.world}:{},...r.hostScopes}}function H(r,e,t){let n=r.qualityLadders.get(e);if(!n){n=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!n.has(i)&&n.set(i,s.stages)}r.qualityLadders.set(e,n)}return n}function Lt(r,e,t){let n=t.toLowerCase();return e==="patter"?H(r,"patter",()=>[...r.patterSharedDecls,...r.patterLocalDecls]).get(n):r.registry.toEvalContext().qualities?.(e,t)??H(r,`host/${e}`,()=>r.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(n)}function tt(r){return{name:r.name,type:r.type,values:r.values,stages:r.stages,default:r.default,writable:r.writable}}function jt(r,e){let t=tt(r),n=r.writable??e;return n===void 0?t:{...t,writable:n}}function fe(r,e){return r.sceneGameIdToId.get(e)??(r.bundle.scenes[e]?e:void 0)}function he(r,e,t){return r.blockGameIdToId.get(e)?.get(t)??(r.blockIndex.get(t)?.sceneId===e?t:void 0)}function nt(r,e){r.refSplitRevision!==r.registry.revision&&(r.refSplitCache.clear(),r.refSplitRevision=r.registry.revision);let t=r.refSplitCache.get(e);return t||(t=Be(e,n=>n==="scene"||r.registry.has(n)||(r.bundle.externalScopes?.includes(n)??!1)),r.refSplitCache.set(e,t)),t}function Mt(r){let e={};r.shared?.patter&&(e.patter=r.shared.patter);for(let[t,n]of Object.entries(r.stageBags??{}))e[w.stage(t)]=n;for(let[t,n]of Object.entries(r.flows??{}))Object.assign(e,rt(t,n));return e}function rt(r,e){let t={};e.scopes?.patter&&(t[w.flowGlobals(r)]=e.scopes.patter);for(let[n,s]of Object.entries(e.sceneBags??{}))t[w.flowScene(r,n)]=s;return t}function Vt(r,e){let t={scopes:{},sceneBags:{}},n=w.flow(r);for(let[s,i]of Object.entries(e)){if(!s.startsWith(n))continue;let o=s.slice(n.length);o==="patter"?t.scopes.patter=i:o.startsWith("scene/")&&(t.sceneBags[Ft(o.slice(6))]=i)}return t}function Xe(r){return typeof r=="boolean"?r:typeof r=="number"?r!==0:typeof r=="string"?r!=="":r.length>0}var Gt=(r,e)=>r.shared??e;function me(r,e){return{name:r.name,type:r.type,hasDefault:r.default!==void 0,...r.default!==void 0?{default:r.default}:{},shared:Gt(r,e)}}function qt(r){return{name:r.name,type:r.type,hasDefault:r.default!==void 0,...r.values?{values:[...r.values]}:{},...r.purpose?{purpose:r.purpose}:{}}}function $t(r,e){e.blocks++;let t=[...r.children];for(;t.length;){let n=t.pop();if(n.type==="group"){e.groups++,n.prompt&&e.prompts++,t.push(...n.children);continue}e.snippets++;for(let s of n.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function st(r){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:r.cast?.length??0},t=[],n=[];for(let o of Object.values(r.scenes)){e.scenes++;let a=y(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:y(c),name:c.name}))});for(let c of o.blocks)$t(c,e);o.sceneProps?.length&&n.push({gameId:a,properties:o.sceneProps.map(c=>me(c,!1))})}let s=(r.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>me(a,!0))})),i=Object.entries(r.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(qt)}));return{identity:{schema:r.schema,project:r.content.project,...r.content.version!==void 0?{version:r.content.version}:{},...r.content.hash!==void 0?{hash:r.content.hash}:{},...r.content.structureHash!==void 0?{structureHash:r.content.structureHash}:{},voiced:r.voiced,defaultLocale:r.locales.default,locales:[...r.locales.included],localisation:r.localisation?.mode??"embedded",sourceDebug:r.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(r.properties??[]).map(o=>me(o,!0)),scene:n},gameData:i,counts:e}}var F=(r,e)=>Object.prototype.hasOwnProperty.call(r,e);function it(r,e){return r.gameDataFields?.[e]??[]}function ye(r,e,t){return e&&F(e,t)?e[t]:r.find(n=>n.name===t)?.default}function ot(r,e){let t={};for(let n of r){let s=ye(r,e,n.name);s!==void 0&&(t[n.name]=s)}for(let[n,s]of Object.entries(e??{}))F(t,n)||(t[n]=s);return t}function at(r,e,t){let n=r.extensions!==void 0&&F(r.extensions,e)?r.extensions[e]:void 0;return n!==void 0&&F(n.fields,t)?n.fields[t]??[]:[]}function ct(r,e){return{...(r.extensions!==void 0&&F(r.extensions,e)?r.extensions[e]:void 0)?.settings??{}}}function lt(r,e,t=""){let n=o=>({...o?.extensions??{}}),s=Object.values(r.scenes??{}),i=o=>(F(r.scenes??{},o)?r.scenes[o]:void 0)??s.find(a=>y(a)===o);switch(e){case"project":{let o={};for(let[a,c]of Object.entries(r.extensions??{}))c.project!==void 0&&(o[a]=c.project);return o}case"scene":return n(i(t));case"block":{let o=t.indexOf("/");if(o<0)return{};let a=i(t.slice(0,o))?.blocks??[],c=t.slice(o+1);return n(a.find(l=>l.id===c)??a.find(l=>y(l)===c))}case"snippet":case"option":case"line":case"text":case"gameEvent":{let o,a=(c,l)=>{for(let d of c??[]){if(o!==void 0)return;if(d.type==="group"){if(l&&d.id===t&&e==="option"){o=d;return}if(l&&d.prompt?.id===t&&d.prompt.kind===e){o=d.prompt;return}a(d.children??[],d.selector==="choice");continue}if(d.id===t&&e==="snippet"){o=d;return}let h=(d.beats??[]).find(f=>f.id===t);if(h!==void 0){h.kind===e&&(o=h);return}}};for(let c of s)for(let l of c.blocks??[])o===void 0&&a(l.children??[],!1);return n(o)}default:return{}}}return St(_t);})();\n';
|
|
1552
1774
|
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';
|
|
1553
1775
|
|
|
1554
1776
|
// src/play-timeline.ts
|
|
@@ -2497,9 +2719,160 @@ function renderCoverageText(report, sceneName = (id) => id, opts = {}) {
|
|
|
2497
2719
|
}
|
|
2498
2720
|
|
|
2499
2721
|
// src/format.ts
|
|
2500
|
-
import { readFileSync as
|
|
2501
|
-
import { dirname as
|
|
2502
|
-
import { canonicalStringify as
|
|
2722
|
+
import { readFileSync as readFileSync8, statSync as statSync3 } from "fs";
|
|
2723
|
+
import { dirname as dirname6, resolve as resolve5 } from "path";
|
|
2724
|
+
import { canonicalStringify as canonicalStringify6, parseSource as parseSource6 } from "@patterkit/core";
|
|
2725
|
+
|
|
2726
|
+
// src/extensions.ts
|
|
2727
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
2728
|
+
import { join as join7, relative as relative5, sep as sep3 } from "path";
|
|
2729
|
+
import { canonicalStringify as canonicalStringify5, parseSource as parseSource5 } from "@patterkit/core";
|
|
2730
|
+
import { EXTENSIONS_DIR as EXTENSIONS_DIR3, MANIFEST_EXTENSION as MANIFEST_EXTENSION3 } from "@patterkit/model";
|
|
2731
|
+
import { manifestProblems, parseManifest, renameStages, targetOf } from "@wildwinter/toolkit/extensions";
|
|
2732
|
+
var CONFLICT_MARKERS = /^(<{7}|={7}|>{7})( |$)/m;
|
|
2733
|
+
var manifestPath = (projectDir, id) => join7(projectDir, EXTENSIONS_DIR3, `${id}${MANIFEST_EXTENSION3}`);
|
|
2734
|
+
function readProjectManifest(projectDir, id) {
|
|
2735
|
+
if (!/^[a-z][a-z0-9-]*$/.test(id)) return void 0;
|
|
2736
|
+
let text;
|
|
2737
|
+
try {
|
|
2738
|
+
text = readFileSync7(manifestPath(projectDir, id), "utf8");
|
|
2739
|
+
} catch {
|
|
2740
|
+
return void 0;
|
|
2741
|
+
}
|
|
2742
|
+
return soundManifest(text);
|
|
2743
|
+
}
|
|
2744
|
+
function soundManifest(text) {
|
|
2745
|
+
if (CONFLICT_MARKERS.test(text)) return void 0;
|
|
2746
|
+
try {
|
|
2747
|
+
const manifest = parseManifest(text);
|
|
2748
|
+
return manifestProblems(manifest).some((p) => p.severity === "error") ? void 0 : manifest;
|
|
2749
|
+
} catch {
|
|
2750
|
+
return void 0;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
var projectManifests = (projectDir) => (id) => readProjectManifest(projectDir, id);
|
|
2754
|
+
var soundOf = (manifest) => manifestProblems(manifest).some((p) => p.severity === "error") ? void 0 : manifest;
|
|
2755
|
+
function planExtensionUpgrades(loaded) {
|
|
2756
|
+
const writes = /* @__PURE__ */ new Map();
|
|
2757
|
+
const report = [];
|
|
2758
|
+
const notes = /* @__PURE__ */ new Map();
|
|
2759
|
+
const note = (path, line) => {
|
|
2760
|
+
notes.set(path, [...notes.get(path) ?? [], line]);
|
|
2761
|
+
};
|
|
2762
|
+
const taken = loaded.project.extensions ?? {};
|
|
2763
|
+
const moved = {};
|
|
2764
|
+
const rel = (path) => relative5(loaded.root, path).split(sep3).join("/");
|
|
2765
|
+
for (const [id, pin] of Object.entries(taken)) {
|
|
2766
|
+
const file = loaded.manifests.find((m) => m.id === id);
|
|
2767
|
+
const manifest = file !== void 0 ? soundOf(file.manifest) : void 0;
|
|
2768
|
+
if (manifest === void 0 || pin?.version === manifest.version) continue;
|
|
2769
|
+
moved[id] = { ...pin, version: manifest.version };
|
|
2770
|
+
const took = `took the Patter extension "${id}" at version ${manifest.version}${pin?.version ? ` (was ${pin.version})` : ""}`;
|
|
2771
|
+
report.push(took);
|
|
2772
|
+
note(loaded.projectFile, took);
|
|
2773
|
+
loaded.extensionShards.forEach((shard, i) => {
|
|
2774
|
+
if (shard.extension !== id) return;
|
|
2775
|
+
let renamed = 0;
|
|
2776
|
+
const entries = {};
|
|
2777
|
+
for (const [kind, byNode] of Object.entries(shard.entries ?? {})) {
|
|
2778
|
+
const target = targetOf(manifest, "patter", kind);
|
|
2779
|
+
const out = {};
|
|
2780
|
+
for (const [node, entry] of Object.entries(byNode ?? {})) {
|
|
2781
|
+
if (target === void 0 || typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
2782
|
+
out[node] = entry;
|
|
2783
|
+
continue;
|
|
2784
|
+
}
|
|
2785
|
+
const r = renameStages(target, entry);
|
|
2786
|
+
renamed += r.renames.length;
|
|
2787
|
+
out[node] = r.entry;
|
|
2788
|
+
}
|
|
2789
|
+
entries[kind] = out;
|
|
2790
|
+
}
|
|
2791
|
+
if (renamed > 0) {
|
|
2792
|
+
const path = loaded.extensionShardFiles[i];
|
|
2793
|
+
writes.set(path, canonicalStringify5({ ...shard, entries }));
|
|
2794
|
+
const what = `renamed ${renamed} stage value${renamed === 1 ? "" : "s"} for "${id}"`;
|
|
2795
|
+
report.push(` ${what} in ${rel(path)}`);
|
|
2796
|
+
note(path, what);
|
|
2797
|
+
}
|
|
2798
|
+
});
|
|
2799
|
+
}
|
|
2800
|
+
if (Object.keys(moved).length > 0) {
|
|
2801
|
+
const project = parseSource5(readFileSync7(loaded.projectFile, "utf8"));
|
|
2802
|
+
writes.set(loaded.projectFile, canonicalStringify5({ ...project, extensions: { ...taken, ...moved } }));
|
|
2803
|
+
}
|
|
2804
|
+
return { writes, report, notes };
|
|
2805
|
+
}
|
|
2806
|
+
function renameCastKeys(shards, manifests, renames) {
|
|
2807
|
+
const out = [];
|
|
2808
|
+
if (renames.size === 0) return out;
|
|
2809
|
+
const sound = /* @__PURE__ */ new Map();
|
|
2810
|
+
for (const m of manifests) {
|
|
2811
|
+
const s = soundOf(m.manifest);
|
|
2812
|
+
if (s !== void 0) sound.set(m.id, s);
|
|
2813
|
+
}
|
|
2814
|
+
for (const { path, shard } of shards) {
|
|
2815
|
+
const manifest = sound.get(shard.extension);
|
|
2816
|
+
if (manifest === void 0) continue;
|
|
2817
|
+
const count = { n: 0 };
|
|
2818
|
+
const entries = {};
|
|
2819
|
+
for (const [kind, byNode] of Object.entries(shard.entries ?? {})) {
|
|
2820
|
+
const fields = targetOf(manifest, "patter", kind)?.fields ?? [];
|
|
2821
|
+
const nodes = {};
|
|
2822
|
+
for (const [node, entry] of Object.entries(byNode ?? {})) {
|
|
2823
|
+
nodes[node] = typeof entry === "object" && entry !== null && !Array.isArray(entry) ? renameFields(fields, entry, renames, count) : entry;
|
|
2824
|
+
}
|
|
2825
|
+
entries[kind] = nodes;
|
|
2826
|
+
}
|
|
2827
|
+
if (count.n > 0) out.push({ path, shard: { ...shard, entries }, renamed: count.n });
|
|
2828
|
+
}
|
|
2829
|
+
return out;
|
|
2830
|
+
}
|
|
2831
|
+
function renameFields(fields, record, renames, count) {
|
|
2832
|
+
const decls = new Map(fields.map((f) => [f.name, f]));
|
|
2833
|
+
const out = {};
|
|
2834
|
+
for (const [k, v] of Object.entries(record)) {
|
|
2835
|
+
const decl = decls.get(k);
|
|
2836
|
+
out[k] = decl !== void 0 ? renameValue(decl, v, renames, count) : v;
|
|
2837
|
+
}
|
|
2838
|
+
return out;
|
|
2839
|
+
}
|
|
2840
|
+
function renameValue(decl, value, renames, count) {
|
|
2841
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2842
|
+
switch (decl.type) {
|
|
2843
|
+
case "reference": {
|
|
2844
|
+
const to = decl.to === "cast" && typeof value === "string" ? renames.get(value) : void 0;
|
|
2845
|
+
if (to === void 0) return value;
|
|
2846
|
+
count.n++;
|
|
2847
|
+
return to;
|
|
2848
|
+
}
|
|
2849
|
+
case "record":
|
|
2850
|
+
return isRecord(value) ? renameFields(decl.fields ?? [], value, renames, count) : value;
|
|
2851
|
+
case "list":
|
|
2852
|
+
return Array.isArray(value) && decl.of !== void 0 ? value.map((v) => renameValue(decl.of, v, renames, count)) : value;
|
|
2853
|
+
case "map": {
|
|
2854
|
+
if (!isRecord(value)) return value;
|
|
2855
|
+
const castKeys = decl.key?.type === "reference" && decl.key.to === "cast";
|
|
2856
|
+
const finals = Object.keys(value).map((k) => castKeys ? renames.get(k) ?? k : k);
|
|
2857
|
+
const taken = /* @__PURE__ */ new Map();
|
|
2858
|
+
for (const f of finals) taken.set(f, (taken.get(f) ?? 0) + 1);
|
|
2859
|
+
const out = {};
|
|
2860
|
+
Object.entries(value).forEach(([k, v], i) => {
|
|
2861
|
+
const renamed = decl.of !== void 0 ? renameValue(decl.of, v, renames, count) : v;
|
|
2862
|
+
const to = finals[i];
|
|
2863
|
+
if (to !== k && taken.get(to) === 1) {
|
|
2864
|
+
out[to] = renamed;
|
|
2865
|
+
count.n++;
|
|
2866
|
+
} else out[k] = renamed;
|
|
2867
|
+
});
|
|
2868
|
+
return out;
|
|
2869
|
+
}
|
|
2870
|
+
default:
|
|
2871
|
+
return value;
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
// src/format.ts
|
|
2503
2876
|
var isShard = (file) => SHARD_EXTENSIONS.some((ext) => file.endsWith(ext));
|
|
2504
2877
|
function expand(path) {
|
|
2505
2878
|
let dir = false;
|
|
@@ -2508,21 +2881,43 @@ function expand(path) {
|
|
|
2508
2881
|
} catch {
|
|
2509
2882
|
}
|
|
2510
2883
|
if (!dir) return [path];
|
|
2511
|
-
const root =
|
|
2884
|
+
const root = dirname6(findProjectFile(path));
|
|
2512
2885
|
return SHARD_EXTENSIONS.flatMap((ext) => walkFiles(root, ext)).sort();
|
|
2513
2886
|
}
|
|
2887
|
+
function upgradesFor(paths) {
|
|
2888
|
+
const content = /* @__PURE__ */ new Map(), notes = /* @__PURE__ */ new Map();
|
|
2889
|
+
for (const path of paths) {
|
|
2890
|
+
try {
|
|
2891
|
+
if (!statSync3(path).isDirectory()) continue;
|
|
2892
|
+
} catch {
|
|
2893
|
+
continue;
|
|
2894
|
+
}
|
|
2895
|
+
let loaded;
|
|
2896
|
+
try {
|
|
2897
|
+
loaded = loadProject(path);
|
|
2898
|
+
} catch {
|
|
2899
|
+
continue;
|
|
2900
|
+
}
|
|
2901
|
+
const plan = planExtensionUpgrades(loaded);
|
|
2902
|
+
for (const [file, text] of plan.writes) content.set(resolve5(file), text);
|
|
2903
|
+
for (const [file, lines] of plan.notes) notes.set(resolve5(file), [...notes.get(resolve5(file)) ?? [], ...lines]);
|
|
2904
|
+
}
|
|
2905
|
+
return { content, notes };
|
|
2906
|
+
}
|
|
2514
2907
|
function runFormat(paths) {
|
|
2908
|
+
const upgrades = upgradesFor(paths);
|
|
2515
2909
|
return paths.flatMap(expand).map((file) => {
|
|
2516
2910
|
if (!isShard(file)) return { file, changed: false, skipped: true };
|
|
2517
|
-
const original =
|
|
2911
|
+
const original = readFileSync8(file, "utf8");
|
|
2912
|
+
const notes = upgrades.notes.get(resolve5(file));
|
|
2518
2913
|
let canonical;
|
|
2519
2914
|
try {
|
|
2520
|
-
canonical =
|
|
2915
|
+
canonical = upgrades.content.get(resolve5(file)) ?? canonicalStringify6(parseSource6(original));
|
|
2521
2916
|
} catch (e) {
|
|
2522
2917
|
throw new Error(`${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
2523
2918
|
}
|
|
2524
2919
|
const changed = canonical !== original;
|
|
2525
|
-
return
|
|
2920
|
+
return { file, changed, ...changed ? { write: { path: file, content: canonical } } : {}, ...notes ? { notes } : {} };
|
|
2526
2921
|
});
|
|
2527
2922
|
}
|
|
2528
2923
|
|
|
@@ -2917,8 +3312,8 @@ async function runReportXlsx(data) {
|
|
|
2917
3312
|
}
|
|
2918
3313
|
|
|
2919
3314
|
// src/localisation.ts
|
|
2920
|
-
import { basename as basename5, join as
|
|
2921
|
-
import { canonicalStringify as
|
|
3315
|
+
import { basename as basename5, join as join8, sep as sep4 } from "path";
|
|
3316
|
+
import { canonicalStringify as canonicalStringify7 } from "@patterkit/core";
|
|
2922
3317
|
import {
|
|
2923
3318
|
walkNodes as walkNodes2,
|
|
2924
3319
|
castStringKey,
|
|
@@ -3084,23 +3479,23 @@ function targetLocPath(loaded, scene, locale, defaultLocale) {
|
|
|
3084
3479
|
if (existing) return existing;
|
|
3085
3480
|
const fromDefault = existingLocPath(loaded, scene, defaultLocale);
|
|
3086
3481
|
if (fromDefault) {
|
|
3087
|
-
const parts = fromDefault.split(
|
|
3482
|
+
const parts = fromDefault.split(sep4);
|
|
3088
3483
|
for (let i = parts.length - 2; i >= 0; i--) {
|
|
3089
3484
|
if (parts[i] === defaultLocale) {
|
|
3090
3485
|
parts[i] = locale;
|
|
3091
|
-
return parts.join(
|
|
3486
|
+
return parts.join(sep4);
|
|
3092
3487
|
}
|
|
3093
3488
|
}
|
|
3094
3489
|
}
|
|
3095
3490
|
const stem = scene === PROJECT_LOCALE_SCENE2 ? "_project" : scene;
|
|
3096
|
-
return
|
|
3491
|
+
return join8(loaded.root, layoutOf(loaded).strings, locale, `${stem}.patterloc`);
|
|
3097
3492
|
}
|
|
3098
3493
|
function authoringPath(loaded, scene) {
|
|
3099
|
-
const dir =
|
|
3100
|
-
if (scene === PROJECT_LOCALE_SCENE2) return
|
|
3494
|
+
const dir = join8(loaded.root, layoutOf(loaded).authoring);
|
|
3495
|
+
if (scene === PROJECT_LOCALE_SCENE2) return join8(dir, "_project.patterx");
|
|
3101
3496
|
const flow = loaded.sceneFiles[scene];
|
|
3102
3497
|
const stem = flow ? basename5(flow).replace(/\.patterflow$/, "") : scene;
|
|
3103
|
-
return
|
|
3498
|
+
return join8(dir, `${stem}.patterx`);
|
|
3104
3499
|
}
|
|
3105
3500
|
var findFile = (files, items, path) => {
|
|
3106
3501
|
const i = files.indexOf(path);
|
|
@@ -3138,7 +3533,7 @@ function applyLoc(loaded, catalog, opts = {}) {
|
|
|
3138
3533
|
merged[id] = text;
|
|
3139
3534
|
}
|
|
3140
3535
|
const file = { schema: existing?.schema ?? STRINGS_SCHEMA, scene, locale, strings: merged };
|
|
3141
|
-
writes.push({ path, content:
|
|
3536
|
+
writes.push({ path, content: canonicalStringify7(file) });
|
|
3142
3537
|
const fresh = bySceneFresh.get(scene);
|
|
3143
3538
|
if (!fresh?.size) continue;
|
|
3144
3539
|
const aPath = authoringPath(loaded, scene);
|
|
@@ -3147,7 +3542,7 @@ function applyLoc(loaded, catalog, opts = {}) {
|
|
|
3147
3542
|
const editsMap = { ...authoring.edits ?? {} };
|
|
3148
3543
|
for (const id of fresh) editsMap[id] = { ...editsMap[id], localisedAt: { ...editsMap[id]?.localisedAt, [locale]: now } };
|
|
3149
3544
|
authoring.edits = editsMap;
|
|
3150
|
-
writes.push({ path: aPath, content:
|
|
3545
|
+
writes.push({ path: aPath, content: canonicalStringify7(authoring) });
|
|
3151
3546
|
}
|
|
3152
3547
|
return { writes, stats: { updated, files: bySceneText.size } };
|
|
3153
3548
|
}
|
|
@@ -3833,9 +4228,9 @@ async function scriptToDocx(doc) {
|
|
|
3833
4228
|
import PDFDocument from "pdfkit";
|
|
3834
4229
|
|
|
3835
4230
|
// src/script-fonts.ts
|
|
3836
|
-
import { existsSync as existsSync5, readdirSync as
|
|
4231
|
+
import { existsSync as existsSync5, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
|
|
3837
4232
|
import { createRequire } from "module";
|
|
3838
|
-
import { join as
|
|
4233
|
+
import { join as join9 } from "path";
|
|
3839
4234
|
|
|
3840
4235
|
// src/script-fonts.json
|
|
3841
4236
|
var script_fonts_default = {
|
|
@@ -3922,18 +4317,18 @@ function useScriptFontFiles(paths, licencePaths = {}) {
|
|
|
3922
4317
|
cache.clear();
|
|
3923
4318
|
}
|
|
3924
4319
|
function scriptFontLicencePath(pkg) {
|
|
3925
|
-
const dir = (require2.resolve.paths(pkg) ?? []).map((p) =>
|
|
3926
|
-
const file = dir &&
|
|
4320
|
+
const dir = (require2.resolve.paths(pkg) ?? []).map((p) => join9(p, pkg)).find((d) => existsSync5(d));
|
|
4321
|
+
const file = dir && readdirSync5(dir).find((f) => /^(licen[cs]e|ofl)/i.test(f));
|
|
3927
4322
|
if (!dir || !file) throw new Error(`no licence file found for ${pkg}`);
|
|
3928
|
-
return
|
|
4323
|
+
return join9(dir, file);
|
|
3929
4324
|
}
|
|
3930
4325
|
function scriptFontLicences() {
|
|
3931
|
-
return SCRIPT_FONT_PACKAGES.map((pkg) => ({ package: pkg, text:
|
|
4326
|
+
return SCRIPT_FONT_PACKAGES.map((pkg) => ({ package: pkg, text: readFileSync9(licences[pkg] ?? scriptFontLicencePath(pkg), "utf8") }));
|
|
3932
4327
|
}
|
|
3933
4328
|
function scriptFontFiles(face) {
|
|
3934
4329
|
return script_fonts_default[face].map((spec) => {
|
|
3935
4330
|
let bytes = cache.get(spec);
|
|
3936
|
-
if (!bytes) cache.set(spec, bytes =
|
|
4331
|
+
if (!bytes) cache.set(spec, bytes = readFileSync9(located[spec] ?? require2.resolve(spec)));
|
|
3937
4332
|
return bytes;
|
|
3938
4333
|
});
|
|
3939
4334
|
}
|
|
@@ -3973,7 +4368,7 @@ var ROLES = {
|
|
|
3973
4368
|
"Mono": "mono"
|
|
3974
4369
|
};
|
|
3975
4370
|
async function scriptToPdf(doc) {
|
|
3976
|
-
return new Promise((
|
|
4371
|
+
return new Promise((resolve8, reject) => {
|
|
3977
4372
|
const pdf = new PDFDocument({ size: "A4", margins: { top: MARGIN, bottom: MARGIN, left: MARGIN, right: MARGIN }, info: { Title: doc.project, Author: "Patter" }, font: null });
|
|
3978
4373
|
const register = (prefix, face, firstIsPrefix) => scriptFontFiles(face).map((bytes, i) => {
|
|
3979
4374
|
const name = firstIsPrefix && i === 0 ? prefix : `${prefix}~${i}`;
|
|
@@ -4017,7 +4412,7 @@ async function scriptToPdf(doc) {
|
|
|
4017
4412
|
const bodyPieces = (runs, color, base = {}) => runs.flatMap((r) => r.code ? split(r.text, "Mono", ACCENT) : split(r.text, SERIF2(base.bold || r.bold, base.italic || r.italic), color));
|
|
4018
4413
|
const chunks = [];
|
|
4019
4414
|
pdf.on("data", (c) => chunks.push(c));
|
|
4020
|
-
pdf.on("end", () =>
|
|
4415
|
+
pdf.on("end", () => resolve8(Buffer.concat(chunks)));
|
|
4021
4416
|
pdf.on("error", reject);
|
|
4022
4417
|
const pageRight = pdf.page.width - MARGIN;
|
|
4023
4418
|
const pageBottom = pdf.page.height - MARGIN;
|
|
@@ -4169,17 +4564,17 @@ async function scriptToPdf(doc) {
|
|
|
4169
4564
|
}
|
|
4170
4565
|
|
|
4171
4566
|
// src/init.ts
|
|
4172
|
-
import { join as
|
|
4173
|
-
import { readdirSync as
|
|
4174
|
-
import { newId, slug, canonicalStringify as
|
|
4567
|
+
import { join as join10, basename as basename6, resolve as resolve6 } from "path";
|
|
4568
|
+
import { readdirSync as readdirSync6, existsSync as existsSync6, readFileSync as readFileSync10 } from "fs";
|
|
4569
|
+
import { newId, slug, canonicalStringify as canonicalStringify8 } from "@patterkit/core";
|
|
4175
4570
|
import { FLOW_SCHEMA, PROJECT_SCHEMA, STRINGS_SCHEMA as STRINGS_SCHEMA2 } from "@patterkit/model";
|
|
4176
4571
|
function runInit(opts) {
|
|
4177
|
-
const dir =
|
|
4572
|
+
const dir = resolve6(opts.dir);
|
|
4178
4573
|
const name = opts.name?.trim() || basename6(dir).replace(/\.patter$/, "");
|
|
4179
4574
|
const fileSlug = slug(name);
|
|
4180
4575
|
let existing = [];
|
|
4181
4576
|
try {
|
|
4182
|
-
existing =
|
|
4577
|
+
existing = readdirSync6(dir).filter((f) => f.endsWith(".patterproj"));
|
|
4183
4578
|
} catch {
|
|
4184
4579
|
}
|
|
4185
4580
|
if (existing.length > 0) throw new Error(`a project already exists here: ${existing[0]}`);
|
|
@@ -4223,13 +4618,13 @@ function runInit(opts) {
|
|
|
4223
4618
|
strings: { [beatId]: `Welcome to ${name}. This is the first line of your story - replace it with your own.` }
|
|
4224
4619
|
};
|
|
4225
4620
|
const bundle = opts.bundle ?? "commit";
|
|
4226
|
-
const projectFile =
|
|
4621
|
+
const projectFile = join10(dir, `${fileSlug}.patterproj`);
|
|
4227
4622
|
const writes = [
|
|
4228
|
-
{ path:
|
|
4229
|
-
{ path:
|
|
4230
|
-
{ path:
|
|
4623
|
+
{ path: join10(dir, "scenes", "start.patterflow"), content: canonicalStringify8(flow) },
|
|
4624
|
+
{ path: join10(dir, "loc", "en", "start.patterloc"), content: canonicalStringify8(locale) },
|
|
4625
|
+
{ path: join10(dir, ".editorconfig"), content: EDITORCONFIG },
|
|
4231
4626
|
...vcsConfigWrites(dir, opts.vcs, bundle),
|
|
4232
|
-
{ path: projectFile, content:
|
|
4627
|
+
{ path: projectFile, content: canonicalStringify8(project) }
|
|
4233
4628
|
];
|
|
4234
4629
|
const collisions = writes.map((w) => w.path).filter((p) => existsSync6(p));
|
|
4235
4630
|
if (collisions.length > 0) {
|
|
@@ -4238,16 +4633,16 @@ function runInit(opts) {
|
|
|
4238
4633
|
return { writes, projectFile, name };
|
|
4239
4634
|
}
|
|
4240
4635
|
function vcsConfigWrites(dir, vcs, bundle = "commit") {
|
|
4241
|
-
const writes = [{ path:
|
|
4242
|
-
if (vcs === "git") writes.push({ path:
|
|
4636
|
+
const writes = [{ path: join10(dir, "vcs-setup.md"), content: vcsSetup(vcs, bundle) }];
|
|
4637
|
+
if (vcs === "git") writes.push({ path: join10(dir, ".gitattributes"), content: GITATTRIBUTES });
|
|
4243
4638
|
const ignore = ignoreFileFor(vcs);
|
|
4244
|
-
if (ignore) writes.push({ path:
|
|
4639
|
+
if (ignore) writes.push({ path: join10(dir, ignore), content: ignoreContent(bundle) });
|
|
4245
4640
|
return writes;
|
|
4246
4641
|
}
|
|
4247
4642
|
function currentBundlePosture(dir, vcs) {
|
|
4248
4643
|
const read = (name) => {
|
|
4249
4644
|
try {
|
|
4250
|
-
return
|
|
4645
|
+
return readFileSync10(join10(dir, name), "utf8");
|
|
4251
4646
|
} catch {
|
|
4252
4647
|
return "";
|
|
4253
4648
|
}
|
|
@@ -4264,7 +4659,7 @@ charset = utf-8
|
|
|
4264
4659
|
end_of_line = lf
|
|
4265
4660
|
insert_final_newline = true
|
|
4266
4661
|
|
|
4267
|
-
[*.{patterflow,patterloc,patterx,patterproj}]
|
|
4662
|
+
[*.{patterflow,patterloc,patterx,patterproj,patterextension}]
|
|
4268
4663
|
indent_style = space
|
|
4269
4664
|
indent_size = 2
|
|
4270
4665
|
`;
|
|
@@ -4273,6 +4668,7 @@ var GITATTRIBUTES = `# Patter source is UTF-8 + LF text (pinned; never let autoc
|
|
|
4273
4668
|
*.patterloc text eol=lf
|
|
4274
4669
|
*.patterx text eol=lf
|
|
4275
4670
|
*.patterproj text eol=lf
|
|
4671
|
+
*.patterextension text eol=lf
|
|
4276
4672
|
|
|
4277
4673
|
# Id-keyed structured merge for Patter source (the 'patter merge' driver).
|
|
4278
4674
|
# Register it once per clone (see vcs-setup.md); until then git falls back to a
|
|
@@ -4281,6 +4677,11 @@ var GITATTRIBUTES = `# Patter source is UTF-8 + LF text (pinned; never let autoc
|
|
|
4281
4677
|
*.patterloc merge=patter
|
|
4282
4678
|
*.patterx merge=patter
|
|
4283
4679
|
*.patterproj merge=patter
|
|
4680
|
+
*.patterextension merge=patter
|
|
4681
|
+
|
|
4682
|
+
# An extension's manifest is the extension's own JSON, kept in the project so
|
|
4683
|
+
# every machine reads the same one, and never edited here: text, merged as text.
|
|
4684
|
+
extensions/*.extension.json text eol=lf
|
|
4284
4685
|
|
|
4285
4686
|
# Generated artifacts (spec \xA711). The compiled bundle is committed but
|
|
4286
4687
|
# REGENERATED, never hand-merged - keep ours on conflict and rebuild (the
|
|
@@ -4307,7 +4708,9 @@ function ignoreContent(bundle) {
|
|
|
4307
4708
|
"# Patter generated artifacts - not source (see vcs-setup.md).",
|
|
4308
4709
|
"# Packed document (the send-and-return envelope):",
|
|
4309
4710
|
"*.patterpack",
|
|
4310
|
-
"# Unresolved merge sidecar
|
|
4711
|
+
"# Unresolved merge sidecar (a lingering one is a validate error). The merge also names its clashes",
|
|
4712
|
+
'# in the shard itself, under "unresolved", which IS committed with the file, so CI refuses an',
|
|
4713
|
+
"# unresolved merge wherever it goes:",
|
|
4311
4714
|
"*.patterconflict"
|
|
4312
4715
|
];
|
|
4313
4716
|
if (bundle === "ignore") {
|
|
@@ -4351,6 +4754,8 @@ compiled bundle are TEXT with LF; the packed document is BINARY:
|
|
|
4351
4754
|
text //....patterloc
|
|
4352
4755
|
text //....patterx
|
|
4353
4756
|
text //....patterproj
|
|
4757
|
+
text //....patterextension
|
|
4758
|
+
text //....extension.json
|
|
4354
4759
|
text //....patterc
|
|
4355
4760
|
binary //....patterpack
|
|
4356
4761
|
|
|
@@ -4389,6 +4794,8 @@ Set auto-props so Patter source keeps LF (\`~/.subversion/config\` or repo confi
|
|
|
4389
4794
|
*.patterloc = svn:eol-style=LF
|
|
4390
4795
|
*.patterx = svn:eol-style=LF
|
|
4391
4796
|
*.patterproj = svn:eol-style=LF
|
|
4797
|
+
*.patterextension = svn:eol-style=LF
|
|
4798
|
+
*.extension.json = svn:eol-style=LF
|
|
4392
4799
|
*.patterc = svn:eol-style=LF
|
|
4393
4800
|
*.patterpack = svn:mime-type=application/octet-stream
|
|
4394
4801
|
|
|
@@ -4676,7 +5083,7 @@ function buildIndex(loaded) {
|
|
|
4676
5083
|
}
|
|
4677
5084
|
|
|
4678
5085
|
// src/replace.ts
|
|
4679
|
-
import { canonicalStringify as
|
|
5086
|
+
import { canonicalStringify as canonicalStringify9 } from "@patterkit/core";
|
|
4680
5087
|
import { walkNodes as walkNodes4, PROJECT_LOCALE_SCENE as PROJECT_LOCALE_SCENE3, AUTHORING_SCHEMA as AUTHORING_SCHEMA2 } from "@patterkit/model";
|
|
4681
5088
|
import { findMatcher } from "@wildwinter/toolkit";
|
|
4682
5089
|
function runReplace(loaded, opts) {
|
|
@@ -4720,7 +5127,7 @@ function runReplace(loaded, opts) {
|
|
|
4720
5127
|
}
|
|
4721
5128
|
if (changedIds.length) {
|
|
4722
5129
|
const next = { ...shard, strings: merged };
|
|
4723
|
-
plan.writes.push({ path, content:
|
|
5130
|
+
plan.writes.push({ path, content: canonicalStringify9(next) });
|
|
4724
5131
|
plan.shards.push(next);
|
|
4725
5132
|
plan.scenes++;
|
|
4726
5133
|
const aPath = authoringPath(loaded, shard.scene);
|
|
@@ -4737,7 +5144,7 @@ function runReplace(loaded, opts) {
|
|
|
4737
5144
|
}
|
|
4738
5145
|
for (const [path, file] of stamped) {
|
|
4739
5146
|
plan.authoring.push({ path, file });
|
|
4740
|
-
plan.writes.push({ path, content:
|
|
5147
|
+
plan.writes.push({ path, content: canonicalStringify9(file) });
|
|
4741
5148
|
}
|
|
4742
5149
|
return plan;
|
|
4743
5150
|
}
|
|
@@ -4831,7 +5238,12 @@ function renameGameDataStage(scenes, kind, field, from, to) {
|
|
|
4831
5238
|
for (const block of scene.blocks) {
|
|
4832
5239
|
if (kind === "block") visit(block);
|
|
4833
5240
|
walkNodes5(block.children, (node) => {
|
|
4834
|
-
if (node.type !== "snippet")
|
|
5241
|
+
if (node.type !== "snippet") {
|
|
5242
|
+
if (kind === "option" && node.selector === "choice") {
|
|
5243
|
+
for (const option of node.children) if (option.type === "group") visit(option);
|
|
5244
|
+
}
|
|
5245
|
+
return;
|
|
5246
|
+
}
|
|
4835
5247
|
if (kind === "snippet") visit(node);
|
|
4836
5248
|
for (const beat of node.beats ?? []) if (beat.kind === kind) visit(beat);
|
|
4837
5249
|
});
|
|
@@ -4842,8 +5254,8 @@ function renameGameDataStage(scenes, kind, field, from, to) {
|
|
|
4842
5254
|
}
|
|
4843
5255
|
|
|
4844
5256
|
// src/pin.ts
|
|
4845
|
-
import { readFileSync as
|
|
4846
|
-
import { canonicalStringify as
|
|
5257
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
5258
|
+
import { canonicalStringify as canonicalStringify10, parseSource as parseSource7 } from "@patterkit/core";
|
|
4847
5259
|
import { effectiveGameId as effectiveGameId3, gameIdify } from "@patterkit/model";
|
|
4848
5260
|
function toPin(node) {
|
|
4849
5261
|
if (node.gameId?.trim()) return void 0;
|
|
@@ -4870,8 +5282,8 @@ function planPins(loaded) {
|
|
|
4870
5282
|
});
|
|
4871
5283
|
if (!changed) continue;
|
|
4872
5284
|
const next = { ...scene, ...sceneGameId !== void 0 ? { gameId: sceneGameId } : {}, blocks };
|
|
4873
|
-
const file =
|
|
4874
|
-
plan.writes.push({ path, content:
|
|
5285
|
+
const file = parseSource7(readFileSync11(path, "utf8"));
|
|
5286
|
+
plan.writes.push({ path, content: canonicalStringify10({ ...file, scene: next }) });
|
|
4875
5287
|
plan.scenes.push(next);
|
|
4876
5288
|
}
|
|
4877
5289
|
return plan;
|
|
@@ -4879,9 +5291,9 @@ function planPins(loaded) {
|
|
|
4879
5291
|
|
|
4880
5292
|
// src/unpack.ts
|
|
4881
5293
|
import JSZip2 from "jszip";
|
|
4882
|
-
import { join as
|
|
4883
|
-
import { existsSync as existsSync7, readFileSync as
|
|
4884
|
-
import { parseSource as
|
|
5294
|
+
import { join as join11, resolve as resolve7, sep as sep5 } from "path";
|
|
5295
|
+
import { existsSync as existsSync7, readFileSync as readFileSync12, readdirSync as readdirSync7 } from "fs";
|
|
5296
|
+
import { parseSource as parseSource8, canonicalStringify as canonicalStringify11 } from "@patterkit/core";
|
|
4885
5297
|
import { isUnsafeEntry } from "@wildwinter/toolkit/archive";
|
|
4886
5298
|
import { SCOPES_FILE_SUFFIX as SCOPES_FILE_SUFFIX2 } from "@wildwinter/scoperegistry/scopes";
|
|
4887
5299
|
import { isUnsafeEntry as isUnsafeEntry2 } from "@wildwinter/toolkit/archive";
|
|
@@ -4898,7 +5310,7 @@ var UnsafeEntryError = class extends Error {
|
|
|
4898
5310
|
};
|
|
4899
5311
|
var NotAPackError = class extends Error {
|
|
4900
5312
|
};
|
|
4901
|
-
var isShardEntry = (name) => SHARD_EXTENSIONS.some((ext) => name.endsWith(ext));
|
|
5313
|
+
var isShardEntry = (name) => SHARD_EXTENSIONS.some((ext) => name.endsWith(ext)) || isManifestEntry(name);
|
|
4902
5314
|
function refuseUnsafe(name) {
|
|
4903
5315
|
if (isUnsafeEntry(name)) throw new UnsafeEntryError(`document entry escapes the target directory: ${name}`);
|
|
4904
5316
|
if (name.split("/").some((segment) => segment.startsWith("."))) {
|
|
@@ -4948,13 +5360,13 @@ async function runUnpack(bytes, targetDir) {
|
|
|
4948
5360
|
function projectIdIn(dir) {
|
|
4949
5361
|
let names;
|
|
4950
5362
|
try {
|
|
4951
|
-
names =
|
|
5363
|
+
names = readdirSync7(dir).filter((n) => n.endsWith(".patterproj"));
|
|
4952
5364
|
} catch {
|
|
4953
5365
|
return void 0;
|
|
4954
5366
|
}
|
|
4955
5367
|
for (const n of names) {
|
|
4956
5368
|
try {
|
|
4957
|
-
const id =
|
|
5369
|
+
const id = parseSource8(readFileSync12(join11(dir, n), "utf8")).project?.id;
|
|
4958
5370
|
if (id) return id;
|
|
4959
5371
|
} catch {
|
|
4960
5372
|
}
|
|
@@ -4965,7 +5377,7 @@ function projectIdOf(shards) {
|
|
|
4965
5377
|
const rel = projectEntry(shards);
|
|
4966
5378
|
if (rel === void 0) return void 0;
|
|
4967
5379
|
try {
|
|
4968
|
-
return
|
|
5380
|
+
return parseSource8(shards.get(rel)).project?.id;
|
|
4969
5381
|
} catch {
|
|
4970
5382
|
return void 0;
|
|
4971
5383
|
}
|
|
@@ -4981,7 +5393,7 @@ function checkProvenance(returned, base, target) {
|
|
|
4981
5393
|
}
|
|
4982
5394
|
function targetProjectId(projectDir) {
|
|
4983
5395
|
try {
|
|
4984
|
-
const pf =
|
|
5396
|
+
const pf = parseSource8(readFileSync12(findProjectFile(projectDir), "utf8"));
|
|
4985
5397
|
return pf.project?.id;
|
|
4986
5398
|
} catch {
|
|
4987
5399
|
return void 0;
|
|
@@ -4999,6 +5411,8 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
|
|
|
4999
5411
|
let conflicts = 0, warnings = 0;
|
|
5000
5412
|
const projectRel = projectEntry(theirs);
|
|
5001
5413
|
let projectSides;
|
|
5414
|
+
const manifests = projectManifests(projectDir);
|
|
5415
|
+
const manifestsKept = [];
|
|
5002
5416
|
for (const [rel, theirText] of [...theirs.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
5003
5417
|
const outPath = containedWrite(projectDir, rel);
|
|
5004
5418
|
const baseText = base.get(rel);
|
|
@@ -5008,10 +5422,15 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
|
|
|
5008
5422
|
shards.push({ path: rel, added: true, changed: true });
|
|
5009
5423
|
continue;
|
|
5010
5424
|
}
|
|
5011
|
-
const oursText =
|
|
5425
|
+
const oursText = readFileSync12(outPath, "utf8");
|
|
5426
|
+
if (isManifestEntry(rel)) {
|
|
5427
|
+
if (theirText !== oursText) manifestsKept.push(rel);
|
|
5428
|
+
shards.push({ path: rel, added: false, changed: false });
|
|
5429
|
+
continue;
|
|
5430
|
+
}
|
|
5012
5431
|
if (theirText === baseText || theirText === oursText) {
|
|
5013
5432
|
if (rel === projectRel && baseText !== void 0) {
|
|
5014
|
-
const read = (t) =>
|
|
5433
|
+
const read = (t) => parseSource8(t);
|
|
5015
5434
|
const ours = read(oursText);
|
|
5016
5435
|
projectSides = { ours, base: read(baseText), theirs: read(theirText), merged: ours };
|
|
5017
5436
|
}
|
|
@@ -5020,7 +5439,7 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
|
|
|
5020
5439
|
}
|
|
5021
5440
|
const readSide = (side, text) => {
|
|
5022
5441
|
try {
|
|
5023
|
-
return
|
|
5442
|
+
return parseSource8(text);
|
|
5024
5443
|
} catch (e) {
|
|
5025
5444
|
throw new Error(`${rel}: the ${side} copy is not readable Patter source - ${e instanceof Error ? e.message : String(e)}`);
|
|
5026
5445
|
}
|
|
@@ -5028,8 +5447,9 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
|
|
|
5028
5447
|
const oursObj = readSide("ours", oursText);
|
|
5029
5448
|
const theirsObj = readSide("theirs", theirText);
|
|
5030
5449
|
const baseObj = baseText !== void 0 ? readSide("base", baseText) : {};
|
|
5031
|
-
const
|
|
5032
|
-
const
|
|
5450
|
+
const manifest = detectMergeType(oursObj) === "extension" && typeof oursObj["extension"] === "string" ? manifests(oursObj["extension"]) : void 0;
|
|
5451
|
+
const result = runMerge(baseObj, oursObj, theirsObj, manifest !== void 0 ? { manifest } : void 0);
|
|
5452
|
+
const mergedText = canonicalStringify11(result.merged);
|
|
5033
5453
|
const changed = mergedText !== oursText;
|
|
5034
5454
|
if (changed) writes.push({ path: outPath, content: mergedText });
|
|
5035
5455
|
if (rel === projectRel && baseText !== void 0) {
|
|
@@ -5043,13 +5463,13 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
|
|
|
5043
5463
|
shards.push({ path: rel, result, added: false, changed });
|
|
5044
5464
|
}
|
|
5045
5465
|
let gameScopes;
|
|
5046
|
-
const local2 = projectSides && discoverGameScopes(
|
|
5466
|
+
const local2 = projectSides && discoverGameScopes(resolve7(projectDir), projectSides.ours).gameScopes;
|
|
5047
5467
|
if (projectSides && local2) {
|
|
5048
5468
|
const world = planReturnedWorld(projectSides.ours, projectSides.base, projectSides.theirs, projectSides.merged, local2);
|
|
5049
5469
|
if (world.write) {
|
|
5050
5470
|
writes.push(world.write);
|
|
5051
5471
|
gameScopes = { path: world.write.path };
|
|
5052
|
-
} else if (world.error) gameScopes = { path:
|
|
5472
|
+
} else if (world.error) gameScopes = { path: join11(local2.dir, GAME_SCOPES_FILE), error: world.error };
|
|
5053
5473
|
}
|
|
5054
5474
|
const handoffs = [];
|
|
5055
5475
|
for (const [rel, theirText] of [...returnedDoc.handoffs.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
@@ -5059,13 +5479,24 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
|
|
|
5059
5479
|
handoffs.push(rel);
|
|
5060
5480
|
continue;
|
|
5061
5481
|
}
|
|
5062
|
-
const merged = mergeHandoff(parseHandoff(
|
|
5482
|
+
const merged = mergeHandoff(parseHandoff(readFileSync12(outPath, "utf8")), parseHandoff(theirText));
|
|
5063
5483
|
if (merged) {
|
|
5064
5484
|
writes.push({ path: outPath, content: serialiseHandoff(merged) });
|
|
5065
5485
|
handoffs.push(rel);
|
|
5066
5486
|
}
|
|
5067
5487
|
}
|
|
5068
|
-
return {
|
|
5488
|
+
return {
|
|
5489
|
+
shards,
|
|
5490
|
+
writes,
|
|
5491
|
+
sidecars,
|
|
5492
|
+
other: returnedDoc.other,
|
|
5493
|
+
conflicts,
|
|
5494
|
+
warnings,
|
|
5495
|
+
provenance,
|
|
5496
|
+
...gameScopes ? { gameScopes } : {},
|
|
5497
|
+
...handoffs.length ? { handoffs } : {},
|
|
5498
|
+
...manifestsKept.length ? { manifestsKept } : {}
|
|
5499
|
+
};
|
|
5069
5500
|
}
|
|
5070
5501
|
function mergeHandoff(ours, theirs) {
|
|
5071
5502
|
const key = (i) => `${i.at}|${i.fileHash}`;
|
|
@@ -5081,18 +5512,76 @@ function projectEntry(shards) {
|
|
|
5081
5512
|
return at.length === 1 ? at[0] : void 0;
|
|
5082
5513
|
}
|
|
5083
5514
|
function containedWrite(dir, name) {
|
|
5084
|
-
const root =
|
|
5085
|
-
const full =
|
|
5086
|
-
if (full !== root && !full.startsWith(root +
|
|
5515
|
+
const root = resolve7(dir);
|
|
5516
|
+
const full = resolve7(join11(dir, name));
|
|
5517
|
+
if (full !== root && !full.startsWith(root + sep5)) {
|
|
5087
5518
|
throw new UnsafeEntryError(`document entry escapes the target directory: ${name}`);
|
|
5088
5519
|
}
|
|
5089
5520
|
return full;
|
|
5090
5521
|
}
|
|
5091
5522
|
|
|
5523
|
+
// src/cast-rename.ts
|
|
5524
|
+
import { castStringKey as castStringKey2, speakerLabelKey as speakerLabelKey2, walkNodes as walkNodes6 } from "@patterkit/model";
|
|
5525
|
+
function cleanCastRenames(renames) {
|
|
5526
|
+
const out = /* @__PURE__ */ new Map();
|
|
5527
|
+
for (const [from, to] of Object.entries(renames ?? {})) if (from && to && from !== to) out.set(from, to);
|
|
5528
|
+
return out.size ? out : null;
|
|
5529
|
+
}
|
|
5530
|
+
function renameCastInScene(scene, renames) {
|
|
5531
|
+
let n = 0;
|
|
5532
|
+
const visit = (beat) => {
|
|
5533
|
+
if (beat?.kind !== "line" || beat.character === void 0) return;
|
|
5534
|
+
const to = renames.get(beat.character);
|
|
5535
|
+
if (to !== void 0) {
|
|
5536
|
+
beat.character = to;
|
|
5537
|
+
n++;
|
|
5538
|
+
}
|
|
5539
|
+
};
|
|
5540
|
+
for (const block of scene.blocks ?? []) {
|
|
5541
|
+
walkNodes6(block.children, (node) => {
|
|
5542
|
+
if (node.type === "group") {
|
|
5543
|
+
visit(node.prompt);
|
|
5544
|
+
return;
|
|
5545
|
+
}
|
|
5546
|
+
for (const beat of node.beats ?? []) visit(beat);
|
|
5547
|
+
});
|
|
5548
|
+
}
|
|
5549
|
+
return n;
|
|
5550
|
+
}
|
|
5551
|
+
var CAST_PREFIX = castStringKey2("");
|
|
5552
|
+
var SPEAKER_PREFIX = speakerLabelKey2("", "").slice(0, -1);
|
|
5553
|
+
function renamedKey(key, renames) {
|
|
5554
|
+
if (key.startsWith(CAST_PREFIX)) {
|
|
5555
|
+
const to = renames.get(key.slice(CAST_PREFIX.length));
|
|
5556
|
+
return to !== void 0 ? castStringKey2(to) : void 0;
|
|
5557
|
+
}
|
|
5558
|
+
if (key.startsWith(SPEAKER_PREFIX)) {
|
|
5559
|
+
const at = key.lastIndexOf(":");
|
|
5560
|
+
if (at <= SPEAKER_PREFIX.length) return void 0;
|
|
5561
|
+
const to = renames.get(key.slice(SPEAKER_PREFIX.length, at));
|
|
5562
|
+
return to !== void 0 ? speakerLabelKey2(to, key.slice(at + 1)) : void 0;
|
|
5563
|
+
}
|
|
5564
|
+
return void 0;
|
|
5565
|
+
}
|
|
5566
|
+
function moveCastKeys(record, renames) {
|
|
5567
|
+
const keys = Object.keys(record);
|
|
5568
|
+
const finals = keys.map((k) => renamedKey(k, renames) ?? k);
|
|
5569
|
+
const count = /* @__PURE__ */ new Map();
|
|
5570
|
+
for (const f of finals) count.set(f, (count.get(f) ?? 0) + 1);
|
|
5571
|
+
if (finals.every((f, i) => f === keys[i] || count.get(f) > 1)) return false;
|
|
5572
|
+
const entries = keys.map((k, i) => [count.get(finals[i]) === 1 ? finals[i] : k, record[k]]);
|
|
5573
|
+
for (const k of keys) delete record[k];
|
|
5574
|
+
for (const [k, v] of entries) record[k] = v;
|
|
5575
|
+
return true;
|
|
5576
|
+
}
|
|
5577
|
+
function renameCastStrings(loc, renames) {
|
|
5578
|
+
return loc.strings ? moveCastKeys(loc.strings, renames) : false;
|
|
5579
|
+
}
|
|
5580
|
+
|
|
5092
5581
|
// src/suggestions.ts
|
|
5093
|
-
import { readFileSync as
|
|
5094
|
-
import { canonicalStringify as
|
|
5095
|
-
import { walkNodes as
|
|
5582
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
5583
|
+
import { canonicalStringify as canonicalStringify12, parseSource as parseSource9 } from "@patterkit/core";
|
|
5584
|
+
import { walkNodes as walkNodes7, AUTHORING_SCHEMA as AUTHORING_SCHEMA3 } from "@patterkit/model";
|
|
5096
5585
|
function staleParts(s, liveText, line) {
|
|
5097
5586
|
const stale = [];
|
|
5098
5587
|
if (liveText !== s.baseline) stale.push("the text");
|
|
@@ -5123,7 +5612,7 @@ function indexPlaces(loaded) {
|
|
|
5123
5612
|
places.set(scene.id, { sceneId: scene.id });
|
|
5124
5613
|
for (const block of scene.blocks) {
|
|
5125
5614
|
places.set(block.id, { sceneId: scene.id });
|
|
5126
|
-
|
|
5615
|
+
walkNodes7(block.children, (node) => {
|
|
5127
5616
|
places.set(node.id, { sceneId: scene.id });
|
|
5128
5617
|
if (node.type === "group") {
|
|
5129
5618
|
if (node.prompt) places.set(node.prompt.id, { sceneId: scene.id });
|
|
@@ -5177,7 +5666,7 @@ var Working = class {
|
|
|
5177
5666
|
if (!path) return void 0;
|
|
5178
5667
|
let file = this.flows.get(path);
|
|
5179
5668
|
if (!file) {
|
|
5180
|
-
const onDisk =
|
|
5669
|
+
const onDisk = parseSource9(readFileSync13(path, "utf8"));
|
|
5181
5670
|
const scene = this.loaded.scenes.find((s) => s.id === sceneId);
|
|
5182
5671
|
file = { ...onDisk, scene: structuredClone(scene ?? onDisk.scene) };
|
|
5183
5672
|
this.flows.set(path, file);
|
|
@@ -5185,13 +5674,13 @@ var Working = class {
|
|
|
5185
5674
|
return { path, file };
|
|
5186
5675
|
}
|
|
5187
5676
|
writes() {
|
|
5188
|
-
return [...this.locs, ...this.flows, ...this.authoring].filter(([path]) => this.touched.has(path)).map(([path, file]) => ({ path, content:
|
|
5677
|
+
return [...this.locs, ...this.flows, ...this.authoring].filter(([path]) => this.touched.has(path)).map(([path, file]) => ({ path, content: canonicalStringify12(file) })).sort((a, b) => a.path.localeCompare(b.path));
|
|
5189
5678
|
}
|
|
5190
5679
|
};
|
|
5191
5680
|
function beatIn(scene, id) {
|
|
5192
5681
|
let found;
|
|
5193
5682
|
for (const block of scene.blocks) {
|
|
5194
|
-
|
|
5683
|
+
walkNodes7(block.children, (node) => {
|
|
5195
5684
|
if (found || node.type !== "snippet") return;
|
|
5196
5685
|
found = (node.beats ?? []).find((b) => b.id === id);
|
|
5197
5686
|
});
|
|
@@ -5654,11 +6143,11 @@ function readParagraphs(ps, joiner) {
|
|
|
5654
6143
|
c.walk(p, "both");
|
|
5655
6144
|
return c;
|
|
5656
6145
|
});
|
|
5657
|
-
const
|
|
6146
|
+
const join12 = (view) => parts.map((c) => c.text(view)).filter((t) => t !== "").join(joiner);
|
|
5658
6147
|
return {
|
|
5659
6148
|
text: {
|
|
5660
|
-
proposed:
|
|
5661
|
-
original:
|
|
6149
|
+
proposed: join12("proposed"),
|
|
6150
|
+
original: join12("original"),
|
|
5662
6151
|
changes: parts.flatMap((c) => c.changes),
|
|
5663
6152
|
comments: [...new Set(parts.flatMap((c) => c.comments))],
|
|
5664
6153
|
droppedFormatting: parts.reduce((n, c) => n + c.dropped, 0)
|
|
@@ -5791,7 +6280,7 @@ async function readEditableDocx(bytes) {
|
|
|
5791
6280
|
}
|
|
5792
6281
|
|
|
5793
6282
|
// src/editable-import.ts
|
|
5794
|
-
import { canonicalStringify as
|
|
6283
|
+
import { canonicalStringify as canonicalStringify13 } from "@patterkit/core";
|
|
5795
6284
|
import { AUTHORING_SCHEMA as AUTHORING_SCHEMA4, projectQualifiers as projectQualifiers4 } from "@patterkit/model";
|
|
5796
6285
|
var REFUSE_MISSING_SHARE = 0.25;
|
|
5797
6286
|
var tidy = (s) => s.replace(/[\s ]+/g, " ").trim();
|
|
@@ -6059,7 +6548,7 @@ function planEditableImport(loaded, returned, opts) {
|
|
|
6059
6548
|
const af = shardFor(c.anchor);
|
|
6060
6549
|
af.comments = [...af.comments ?? [], c];
|
|
6061
6550
|
}
|
|
6062
|
-
let writes = [...shards].map(([path, af]) => ({ path, content:
|
|
6551
|
+
let writes = [...shards].map(([path, af]) => ({ path, content: canonicalStringify13(af) }));
|
|
6063
6552
|
if (opts.direct && suggestions.length) {
|
|
6064
6553
|
const warned = new Set(problems.filter((p) => p.severity === "warning" && p.anchor).map((p) => p.anchor));
|
|
6065
6554
|
const clean = suggestions.filter((s) => (live[s.anchor] ?? "") === s.baseline && !warned.has(s.anchor));
|
|
@@ -6227,6 +6716,7 @@ export {
|
|
|
6227
6716
|
NotAPackError,
|
|
6228
6717
|
PATTER_SCOPES_FILE,
|
|
6229
6718
|
PATTER_SCOPES_OWNER,
|
|
6719
|
+
PROJECT_HANDLED,
|
|
6230
6720
|
RARE_REACH_PCT,
|
|
6231
6721
|
REFUSE_MISSING_SHARE,
|
|
6232
6722
|
SCRIPT_FONT_FILES,
|
|
@@ -6249,6 +6739,7 @@ export {
|
|
|
6249
6739
|
catalogToPo,
|
|
6250
6740
|
catalogToXlsx,
|
|
6251
6741
|
classesForChannel,
|
|
6742
|
+
cleanCastRenames,
|
|
6252
6743
|
compileLoaded,
|
|
6253
6744
|
cueLabel,
|
|
6254
6745
|
currentBundlePosture,
|
|
@@ -6276,7 +6767,10 @@ export {
|
|
|
6276
6767
|
listProjectTags,
|
|
6277
6768
|
loadProject,
|
|
6278
6769
|
loadProjectLanding,
|
|
6770
|
+
loadedExtensions,
|
|
6771
|
+
manifestPath,
|
|
6279
6772
|
markerLength,
|
|
6773
|
+
moveCastKeys,
|
|
6280
6774
|
newHandoffId,
|
|
6281
6775
|
parseHandoff,
|
|
6282
6776
|
patterScopesFile,
|
|
@@ -6285,12 +6779,14 @@ export {
|
|
|
6285
6779
|
pickAudio,
|
|
6286
6780
|
planBuild,
|
|
6287
6781
|
planEditableImport,
|
|
6782
|
+
planExtensionUpgrades,
|
|
6288
6783
|
planPins,
|
|
6289
6784
|
planReturnedWorld,
|
|
6290
6785
|
planShareScopes,
|
|
6291
6786
|
planWorldSave,
|
|
6292
6787
|
poToCatalog,
|
|
6293
6788
|
previewRegistry,
|
|
6789
|
+
projectManifests,
|
|
6294
6790
|
projectStringSources,
|
|
6295
6791
|
proposeCoverageDrivers,
|
|
6296
6792
|
qualifierNamer,
|
|
@@ -6300,6 +6796,10 @@ export {
|
|
|
6300
6796
|
readHandoff,
|
|
6301
6797
|
readHandoffs,
|
|
6302
6798
|
readMarker,
|
|
6799
|
+
readProjectManifest,
|
|
6800
|
+
renameCastInScene,
|
|
6801
|
+
renameCastKeys,
|
|
6802
|
+
renameCastStrings,
|
|
6303
6803
|
renameGameDataStage,
|
|
6304
6804
|
renameStage,
|
|
6305
6805
|
renameStageInExpression,
|
|
@@ -6343,9 +6843,11 @@ export {
|
|
|
6343
6843
|
serialiseScopesFile,
|
|
6344
6844
|
setCut,
|
|
6345
6845
|
sidecarIssues,
|
|
6846
|
+
soundManifest,
|
|
6346
6847
|
stageDialect,
|
|
6347
6848
|
stageGateCount,
|
|
6348
6849
|
staleParts,
|
|
6850
|
+
unresolvedMarkIssues,
|
|
6349
6851
|
useScriptFontFiles,
|
|
6350
6852
|
vcsConfigWrites,
|
|
6351
6853
|
voiceScriptToXlsx,
|