@patterkit/cli 0.2.5 → 0.2.7
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/cli.js +616 -349
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -192562,6 +192562,21 @@ function checkDecls(decls, where, issues) {
|
|
|
192562
192562
|
message: `${where}: '${decl.name}' is ${decl.type} but declares no values`
|
|
192563
192563
|
});
|
|
192564
192564
|
}
|
|
192565
|
+
if (decl.type === "quality") {
|
|
192566
|
+
const stages = decl.stages ?? [];
|
|
192567
|
+
if (stages.length < 2) {
|
|
192568
|
+
issues.push({
|
|
192569
|
+
code: "invalid-declaration",
|
|
192570
|
+
message: `${where}: '${decl.name}' is a quality but declares ${stages.length === 0 ? "no stages" : "only one stage"} (a ladder needs at least two)`
|
|
192571
|
+
});
|
|
192572
|
+
}
|
|
192573
|
+
const seenStages = /* @__PURE__ */ new Set();
|
|
192574
|
+
for (const s of stages) {
|
|
192575
|
+
if (!s.trim()) issues.push({ code: "invalid-declaration", message: `${where}: '${decl.name}' has an empty stage name` });
|
|
192576
|
+
else if (seenStages.has(s)) issues.push({ code: "invalid-declaration", message: `${where}: '${decl.name}' declares stage '${s}' more than once` });
|
|
192577
|
+
else seenStages.add(s);
|
|
192578
|
+
}
|
|
192579
|
+
}
|
|
192565
192580
|
if (decl.default !== void 0 && !defaultMatchesType(decl.default, decl)) {
|
|
192566
192581
|
issues.push({
|
|
192567
192582
|
code: "invalid-declaration",
|
|
@@ -192582,6 +192597,8 @@ function defaultMatchesType(value, decl) {
|
|
|
192582
192597
|
return typeof value === "string" && (decl.values?.includes(value) ?? true);
|
|
192583
192598
|
case "flags":
|
|
192584
192599
|
return Array.isArray(value) && value.every((v) => typeof v === "string" && (decl.values?.includes(v) ?? true));
|
|
192600
|
+
case "quality":
|
|
192601
|
+
return typeof value === "string" && (decl.stages?.includes(value) ?? true);
|
|
192585
192602
|
}
|
|
192586
192603
|
}
|
|
192587
192604
|
|
|
@@ -192598,6 +192615,7 @@ function readDirSafe(dir2) {
|
|
|
192598
192615
|
function walkFiles(dir2, ext) {
|
|
192599
192616
|
const out = [];
|
|
192600
192617
|
for (const e of readDirSafe(dir2)) {
|
|
192618
|
+
if (e.name.startsWith(".")) continue;
|
|
192601
192619
|
const p = join(dir2, e.name);
|
|
192602
192620
|
if (e.isDirectory()) out.push(...walkFiles(p, ext));
|
|
192603
192621
|
else if (e.isFile() && e.name.endsWith(ext)) out.push(p);
|
|
@@ -192731,6 +192749,347 @@ import { dirname as dirname2 } from "path";
|
|
|
192731
192749
|
// ../ops/src/validate.ts
|
|
192732
192750
|
import { readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
192733
192751
|
|
|
192752
|
+
// ../ops/src/merge.ts
|
|
192753
|
+
var UnsupportedMergeError = class extends Error {
|
|
192754
|
+
};
|
|
192755
|
+
var eq = (a, b) => canonicalStringify(a) === canonicalStringify(b);
|
|
192756
|
+
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
192757
|
+
var CONFLICT_SIDECAR = ".patterconflict";
|
|
192758
|
+
function sidecarIssues(sidecarPaths) {
|
|
192759
|
+
return sidecarPaths.map((file) => ({
|
|
192760
|
+
file,
|
|
192761
|
+
message: "unresolved merge conflict - resolve it and delete the .patterconflict sidecar before committing"
|
|
192762
|
+
}));
|
|
192763
|
+
}
|
|
192764
|
+
function detectMergeType(file) {
|
|
192765
|
+
const s = typeof file.schema === "string" ? file.schema : "";
|
|
192766
|
+
if (s.startsWith("patter/flow")) return "flow";
|
|
192767
|
+
if (s.startsWith("patter/strings")) return "loc";
|
|
192768
|
+
if (s.startsWith("patter/authoring")) return "authoring";
|
|
192769
|
+
if (s.startsWith("patter/project")) return "project";
|
|
192770
|
+
throw new UnsupportedMergeError(`cannot detect a Patter merge type from schema '${s}'`);
|
|
192771
|
+
}
|
|
192772
|
+
function runMerge(base, ours, theirs, opts) {
|
|
192773
|
+
const type = opts?.type ?? detectMergeType(ours);
|
|
192774
|
+
switch (type) {
|
|
192775
|
+
case "loc":
|
|
192776
|
+
return mergeLoc(base, ours, theirs);
|
|
192777
|
+
case "authoring":
|
|
192778
|
+
return mergeAuthoring(base, ours, theirs);
|
|
192779
|
+
case "flow":
|
|
192780
|
+
return mergeFlow(base, ours, theirs);
|
|
192781
|
+
case "project":
|
|
192782
|
+
return mergeProject(base, ours, theirs);
|
|
192783
|
+
}
|
|
192784
|
+
}
|
|
192785
|
+
function deletedKind(base, ours, theirs) {
|
|
192786
|
+
const deleted = base !== void 0 && (ours === void 0 || theirs === void 0);
|
|
192787
|
+
return deleted ? "delete-vs-edit" : "both-changed";
|
|
192788
|
+
}
|
|
192789
|
+
function merge3(base, ours, theirs, id, path, conflicts) {
|
|
192790
|
+
if (eq(ours, theirs)) return ours;
|
|
192791
|
+
if (eq(base, ours)) return theirs;
|
|
192792
|
+
if (eq(base, theirs)) return ours;
|
|
192793
|
+
conflicts.push({ id, path, base, ours, theirs, kind: deletedKind(base, ours, theirs) });
|
|
192794
|
+
return ours;
|
|
192795
|
+
}
|
|
192796
|
+
function mergeMap(base, ours, theirs, prefix, conflicts, resolve6) {
|
|
192797
|
+
const out = {};
|
|
192798
|
+
for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
|
|
192799
|
+
const b = base[k], o = ours[k], t = theirs[k];
|
|
192800
|
+
let v;
|
|
192801
|
+
if (eq(o, t)) v = o;
|
|
192802
|
+
else if (eq(b, o)) v = t;
|
|
192803
|
+
else if (eq(b, t)) v = o;
|
|
192804
|
+
else {
|
|
192805
|
+
const r = resolve6?.(k, b, o, t);
|
|
192806
|
+
if (r && r.resolved) v = r.value;
|
|
192807
|
+
else {
|
|
192808
|
+
conflicts.push({ id: k, path: `${prefix}.${k}`, base: b, ours: o, theirs: t, kind: r ? r.kind : deletedKind(b, o, t) });
|
|
192809
|
+
v = o;
|
|
192810
|
+
}
|
|
192811
|
+
}
|
|
192812
|
+
if (v !== void 0) out[k] = v;
|
|
192813
|
+
}
|
|
192814
|
+
return out;
|
|
192815
|
+
}
|
|
192816
|
+
var asMap = (v) => isObj(v) ? v : {};
|
|
192817
|
+
var asArr = (v) => Array.isArray(v) ? v : [];
|
|
192818
|
+
function setIf(obj2, key2, val) {
|
|
192819
|
+
if (Array.isArray(val) ? val.length > 0 : Object.keys(val).length > 0) obj2[key2] = val;
|
|
192820
|
+
}
|
|
192821
|
+
function mergeLoc(base, ours, theirs) {
|
|
192822
|
+
const conflicts = [];
|
|
192823
|
+
const merged = {};
|
|
192824
|
+
for (const f of ["schema", "scene", "locale", "default"]) {
|
|
192825
|
+
const v = merge3(base[f], ours[f], theirs[f], "", f, conflicts);
|
|
192826
|
+
if (v !== void 0) merged[f] = v;
|
|
192827
|
+
}
|
|
192828
|
+
merged.strings = mergeMap(asMap(base.strings), asMap(ours.strings), asMap(theirs.strings), "strings", conflicts);
|
|
192829
|
+
return { type: "loc", merged, conflicts, warnings: [] };
|
|
192830
|
+
}
|
|
192831
|
+
function mergeAuthoring(base, ours, theirs) {
|
|
192832
|
+
const conflicts = [];
|
|
192833
|
+
const merged = {};
|
|
192834
|
+
merged.schema = merge3(base.schema, ours.schema, theirs.schema, "", "schema", conflicts) ?? ours.schema;
|
|
192835
|
+
setIf(merged, "comments", mergeComments(asArr(base.comments), asArr(ours.comments), asArr(theirs.comments)));
|
|
192836
|
+
const edits = mergeEdits(asMap(base.edits), asMap(ours.edits), asMap(theirs.edits));
|
|
192837
|
+
setIf(merged, "edits", edits);
|
|
192838
|
+
const lww = lastWriterWins(asMap(ours.edits), asMap(theirs.edits));
|
|
192839
|
+
for (const f of ["writing", "recording", "audio"]) {
|
|
192840
|
+
setIf(merged, f, mergeMap(asMap(base[f]), asMap(ours[f]), asMap(theirs[f]), f, conflicts, lww));
|
|
192841
|
+
}
|
|
192842
|
+
setIf(merged, "documentation", mergeMap(asMap(base.documentation), asMap(ours.documentation), asMap(theirs.documentation), "documentation", conflicts));
|
|
192843
|
+
setIf(merged, "cut", mergeMap(asMap(base.cut), asMap(ours.cut), asMap(theirs.cut), "cut", conflicts, void 0));
|
|
192844
|
+
setIf(merged, "suggestions", mergeById(asArr(base.suggestions), asArr(ours.suggestions), asArr(theirs.suggestions), "suggestions", conflicts));
|
|
192845
|
+
setIf(merged, "rerecord", mergeMap(asMap(base.rerecord), asMap(ours.rerecord), asMap(theirs.rerecord), "rerecord", conflicts, void 0));
|
|
192846
|
+
for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
|
|
192847
|
+
if (k in merged || AUTHORING_HANDLED.has(k)) continue;
|
|
192848
|
+
const v = merge3(base[k], ours[k], theirs[k], "", k, conflicts);
|
|
192849
|
+
if (v !== void 0) merged[k] = v;
|
|
192850
|
+
}
|
|
192851
|
+
return { type: "authoring", merged, conflicts, warnings: [] };
|
|
192852
|
+
}
|
|
192853
|
+
var AUTHORING_HANDLED = /* @__PURE__ */ new Set(["schema", "comments", "edits", "writing", "recording", "audio", "documentation", "cut", "suggestions", "rerecord"]);
|
|
192854
|
+
function mergeById(base, ours, theirs, path, conflicts) {
|
|
192855
|
+
const index2 = (arr) => {
|
|
192856
|
+
const m = {};
|
|
192857
|
+
for (const r of arr) if (isObj(r) && typeof r.id === "string") m[r.id] = r;
|
|
192858
|
+
return m;
|
|
192859
|
+
};
|
|
192860
|
+
const merged = mergeMap(index2(base), index2(ours), index2(theirs), path, conflicts);
|
|
192861
|
+
return Object.values(merged).sort((a, b) => String(asMap(a).ts ?? "").localeCompare(String(asMap(b).ts ?? "")));
|
|
192862
|
+
}
|
|
192863
|
+
function mergeComments(base, ours, theirs) {
|
|
192864
|
+
const byId = /* @__PURE__ */ new Map();
|
|
192865
|
+
for (const c2 of [...base, ...ours, ...theirs]) {
|
|
192866
|
+
if (isObj(c2) && typeof c2.id === "string" && !byId.has(c2.id)) byId.set(c2.id, c2);
|
|
192867
|
+
}
|
|
192868
|
+
return [...byId.values()].sort((a, b) => String(a.ts ?? "").localeCompare(String(b.ts ?? "")));
|
|
192869
|
+
}
|
|
192870
|
+
function lastWriterWins(oursEdits, theirsEdits) {
|
|
192871
|
+
return (id, _b, o, t) => {
|
|
192872
|
+
const om = asMap(oursEdits[id]).modifiedAt, tm = asMap(theirsEdits[id]).modifiedAt;
|
|
192873
|
+
if (typeof om === "string" && typeof tm === "string") return { resolved: true, value: om >= tm ? o : t };
|
|
192874
|
+
return { resolved: false, kind: "no-timestamp" };
|
|
192875
|
+
};
|
|
192876
|
+
}
|
|
192877
|
+
function mergeEdits(base, ours, theirs) {
|
|
192878
|
+
const out = {};
|
|
192879
|
+
for (const id of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
|
|
192880
|
+
const o = ours[id], t = theirs[id];
|
|
192881
|
+
if (eq(o, t)) {
|
|
192882
|
+
if (o !== void 0) out[id] = o;
|
|
192883
|
+
continue;
|
|
192884
|
+
}
|
|
192885
|
+
if (o === void 0) {
|
|
192886
|
+
if (t !== void 0) out[id] = t;
|
|
192887
|
+
continue;
|
|
192888
|
+
}
|
|
192889
|
+
if (t === void 0) {
|
|
192890
|
+
out[id] = o;
|
|
192891
|
+
continue;
|
|
192892
|
+
}
|
|
192893
|
+
out[id] = mergeEditRecord(asMap(o), asMap(t));
|
|
192894
|
+
}
|
|
192895
|
+
return out;
|
|
192896
|
+
}
|
|
192897
|
+
function mergeEditRecord(o, t) {
|
|
192898
|
+
const om = typeof o.modifiedAt === "string" ? o.modifiedAt : "";
|
|
192899
|
+
const tm = typeof t.modifiedAt === "string" ? t.modifiedAt : "";
|
|
192900
|
+
const newer = om >= tm ? o : t;
|
|
192901
|
+
const ol = asMap(o.localisedAt), tl = asMap(t.localisedAt);
|
|
192902
|
+
const loc = {};
|
|
192903
|
+
for (const k of /* @__PURE__ */ new Set([...Object.keys(ol), ...Object.keys(tl)])) {
|
|
192904
|
+
const a = typeof ol[k] === "string" ? ol[k] : "";
|
|
192905
|
+
const b = typeof tl[k] === "string" ? tl[k] : "";
|
|
192906
|
+
loc[k] = a >= b ? ol[k] ?? tl[k] : tl[k] ?? ol[k];
|
|
192907
|
+
}
|
|
192908
|
+
const merged = { ...newer };
|
|
192909
|
+
if (Object.keys(loc).length > 0) merged.localisedAt = loc;
|
|
192910
|
+
return merged;
|
|
192911
|
+
}
|
|
192912
|
+
function mergeKeyedByName(base, ours, theirs, path, conflicts) {
|
|
192913
|
+
const byName = (arr) => {
|
|
192914
|
+
const m = {};
|
|
192915
|
+
for (const p of arr) if (isObj(p) && typeof p.name === "string") m[p.name] = p;
|
|
192916
|
+
return m;
|
|
192917
|
+
};
|
|
192918
|
+
const merged = mergeMap(byName(base), byName(ours), byName(theirs), path, conflicts);
|
|
192919
|
+
return Object.keys(merged).sort().map((name) => merged[name]);
|
|
192920
|
+
}
|
|
192921
|
+
function mergeProject(base, ours, theirs) {
|
|
192922
|
+
const conflicts = [];
|
|
192923
|
+
const merged = {};
|
|
192924
|
+
for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
|
|
192925
|
+
if (k === "properties" || k === "cast") {
|
|
192926
|
+
const v = mergeKeyedByName(asArr(base[k]), asArr(ours[k]), asArr(theirs[k]), k, conflicts);
|
|
192927
|
+
if (v.length > 0) merged[k] = v;
|
|
192928
|
+
} else if (k === "gameDataFields") {
|
|
192929
|
+
const v = mergeGameDataFields(asMap(base[k]), asMap(ours[k]), asMap(theirs[k]), conflicts);
|
|
192930
|
+
if (Object.keys(v).length > 0) merged[k] = v;
|
|
192931
|
+
} else if (k === "locales") {
|
|
192932
|
+
merged.locales = mergeLocales(asMap(base.locales), asMap(ours.locales), asMap(theirs.locales), conflicts);
|
|
192933
|
+
} else {
|
|
192934
|
+
const v = merge3(base[k], ours[k], theirs[k], "", k, conflicts);
|
|
192935
|
+
if (v !== void 0) merged[k] = v;
|
|
192936
|
+
}
|
|
192937
|
+
}
|
|
192938
|
+
return { type: "project", merged, conflicts, warnings: [] };
|
|
192939
|
+
}
|
|
192940
|
+
function mergeGameDataFields(b, o, t, conflicts) {
|
|
192941
|
+
const out = {};
|
|
192942
|
+
for (const kind of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
|
|
192943
|
+
const v = mergeKeyedByName(asArr(b[kind]), asArr(o[kind]), asArr(t[kind]), `gameDataFields.${kind}`, conflicts);
|
|
192944
|
+
if (v.length > 0) out[kind] = v;
|
|
192945
|
+
}
|
|
192946
|
+
return out;
|
|
192947
|
+
}
|
|
192948
|
+
function mergeLocales(b, o, t, conflicts) {
|
|
192949
|
+
const out = {};
|
|
192950
|
+
const def = merge3(b.default, o.default, t.default, "", "locales.default", conflicts);
|
|
192951
|
+
if (def !== void 0) out.default = def;
|
|
192952
|
+
const all = [];
|
|
192953
|
+
for (const arr of [asArr(b.all), asArr(o.all), asArr(t.all)]) {
|
|
192954
|
+
for (const x of arr) if (typeof x === "string" && !all.includes(x)) all.push(x);
|
|
192955
|
+
}
|
|
192956
|
+
out.all = all;
|
|
192957
|
+
return out;
|
|
192958
|
+
}
|
|
192959
|
+
var CHILD_KEYS = ["blocks", "children", "beats"];
|
|
192960
|
+
function toTree(raw) {
|
|
192961
|
+
const childKey = CHILD_KEYS.find((k) => Array.isArray(raw[k]));
|
|
192962
|
+
const fields = {};
|
|
192963
|
+
for (const [k, v] of Object.entries(raw)) if (k !== childKey) fields[k] = v;
|
|
192964
|
+
const children = childKey ? raw[childKey].filter(isObj).map(toTree) : [];
|
|
192965
|
+
return { id: typeof raw.id === "string" ? raw.id : "", fields, childKey, children };
|
|
192966
|
+
}
|
|
192967
|
+
function fromTree(n) {
|
|
192968
|
+
const out = { ...n.fields };
|
|
192969
|
+
if (n.childKey) out[n.childKey] = n.children.map(fromTree);
|
|
192970
|
+
return out;
|
|
192971
|
+
}
|
|
192972
|
+
function mergeFlow(base, ours, theirs) {
|
|
192973
|
+
const conflicts = [];
|
|
192974
|
+
const warnings = [];
|
|
192975
|
+
const merged = {};
|
|
192976
|
+
merged.schema = merge3(base.schema, ours.schema, theirs.schema, "", "schema", conflicts) ?? ours.schema;
|
|
192977
|
+
const bScene = isObj(base.scene) ? toTree(base.scene) : void 0;
|
|
192978
|
+
const oScene = toTree(asMap(ours.scene));
|
|
192979
|
+
const tScene = isObj(theirs.scene) ? toTree(theirs.scene) : void 0;
|
|
192980
|
+
const mergedScene = mergeNode(bScene, oScene, tScene, "scene", conflicts, warnings);
|
|
192981
|
+
merged.scene = fromTree(mergedScene);
|
|
192982
|
+
checkDuplicateIds(mergedScene, conflicts);
|
|
192983
|
+
return { type: "flow", merged, conflicts, warnings };
|
|
192984
|
+
}
|
|
192985
|
+
function mergeNode(b, o, t, path, conflicts, warnings) {
|
|
192986
|
+
const fields = mergeFields(b?.fields ?? {}, o.fields, t?.fields ?? {}, o.id, path, conflicts);
|
|
192987
|
+
const childKey = o.childKey ?? t?.childKey ?? b?.childKey;
|
|
192988
|
+
const children = childKey ? mergeChildren(b?.children ?? [], o.children, t?.children ?? [], `${path}.${childKey}`, conflicts, warnings) : [];
|
|
192989
|
+
return { id: o.id, fields, childKey, children };
|
|
192990
|
+
}
|
|
192991
|
+
function mergeFields(b, o, t, id, path, conflicts) {
|
|
192992
|
+
const out = {};
|
|
192993
|
+
for (const k of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
|
|
192994
|
+
const v = merge3(b[k], o[k], t[k], id, `${path}.${k}`, conflicts);
|
|
192995
|
+
if (v !== void 0) out[k] = v;
|
|
192996
|
+
}
|
|
192997
|
+
return out;
|
|
192998
|
+
}
|
|
192999
|
+
function mergeChildren(B, O, T, path, conflicts, warnings) {
|
|
193000
|
+
const Bm = idMap(B), Om = idMap(O), Tm = idMap(T);
|
|
193001
|
+
const Bset = new Set(Bm.keys()), Oset = new Set(Om.keys()), Tset = new Set(Tm.keys());
|
|
193002
|
+
const merged = /* @__PURE__ */ new Map();
|
|
193003
|
+
const here = (id) => `${path}[${id}]`;
|
|
193004
|
+
for (const id of /* @__PURE__ */ new Set([...Bm.keys(), ...Om.keys(), ...Tm.keys()])) {
|
|
193005
|
+
const b = Bm.get(id), o = Om.get(id), t = Tm.get(id);
|
|
193006
|
+
const inB = Bset.has(id), inO = Oset.has(id), inT = Tset.has(id);
|
|
193007
|
+
if (inO && inT) {
|
|
193008
|
+
if (!inB && !eq(fromTree(o), fromTree(t))) {
|
|
193009
|
+
conflicts.push({ id, path: here(id), base: void 0, ours: fromTree(o), theirs: fromTree(t), kind: "added-both" });
|
|
193010
|
+
merged.set(id, o);
|
|
193011
|
+
} else {
|
|
193012
|
+
merged.set(id, mergeNode(b, o, t, here(id), conflicts, warnings));
|
|
193013
|
+
}
|
|
193014
|
+
} else if (inO && !inT) {
|
|
193015
|
+
if (inB && !eq(fromTree(o), fromTree(b))) {
|
|
193016
|
+
conflicts.push({ id, path: here(id), base: fromTree(b), ours: fromTree(o), theirs: void 0, kind: "delete-vs-edit" });
|
|
193017
|
+
merged.set(id, o);
|
|
193018
|
+
} else if (!inB) {
|
|
193019
|
+
merged.set(id, o);
|
|
193020
|
+
}
|
|
193021
|
+
} else if (!inO && inT) {
|
|
193022
|
+
if (inB && !eq(fromTree(t), fromTree(b))) {
|
|
193023
|
+
conflicts.push({ id, path: here(id), base: fromTree(b), ours: void 0, theirs: fromTree(t), kind: "delete-vs-edit" });
|
|
193024
|
+
} else if (!inB) {
|
|
193025
|
+
merged.set(id, t);
|
|
193026
|
+
}
|
|
193027
|
+
}
|
|
193028
|
+
}
|
|
193029
|
+
return orderChildren(merged, B, O, T, path, conflicts, warnings);
|
|
193030
|
+
}
|
|
193031
|
+
function orderChildren(merged, B, O, T, path, conflicts, warnings) {
|
|
193032
|
+
const Bids = B.map((n) => n.id), Oids = O.map((n) => n.id), Tids = T.map((n) => n.id);
|
|
193033
|
+
const Oidset = new Set(Oids);
|
|
193034
|
+
const common = new Set(Bids.filter((id) => merged.has(id)));
|
|
193035
|
+
const restrict = (ids) => ids.filter((id) => common.has(id));
|
|
193036
|
+
const relB = restrict(Bids), relO = restrict(Oids), relT = restrict(Tids);
|
|
193037
|
+
const fullO = relO.length === common.size, fullT = relT.length === common.size;
|
|
193038
|
+
let commonOrder;
|
|
193039
|
+
if (!fullO && !fullT) commonOrder = relB;
|
|
193040
|
+
else if (!fullO) commonOrder = relT;
|
|
193041
|
+
else if (!fullT) commonOrder = relO;
|
|
193042
|
+
else if (arrEq(relO, relB)) commonOrder = relT;
|
|
193043
|
+
else if (arrEq(relT, relB)) commonOrder = relO;
|
|
193044
|
+
else if (arrEq(relO, relT)) commonOrder = relO;
|
|
193045
|
+
else {
|
|
193046
|
+
conflicts.push({ id: "", path, base: relB, ours: relO, theirs: relT, kind: "moved" });
|
|
193047
|
+
commonOrder = relO;
|
|
193048
|
+
}
|
|
193049
|
+
const anchorIn = (ids, id) => {
|
|
193050
|
+
for (let i = ids.indexOf(id) - 1; i >= 0; i--) if (common.has(ids[i])) return ids[i];
|
|
193051
|
+
return null;
|
|
193052
|
+
};
|
|
193053
|
+
const byAnchor = /* @__PURE__ */ new Map();
|
|
193054
|
+
const group = (a) => {
|
|
193055
|
+
let g = byAnchor.get(a);
|
|
193056
|
+
if (!g) {
|
|
193057
|
+
g = { ours: [], theirs: [] };
|
|
193058
|
+
byAnchor.set(a, g);
|
|
193059
|
+
}
|
|
193060
|
+
return g;
|
|
193061
|
+
};
|
|
193062
|
+
for (const n of O) if (merged.has(n.id) && !common.has(n.id)) group(anchorIn(Oids, n.id)).ours.push(n.id);
|
|
193063
|
+
for (const n of T) if (merged.has(n.id) && !common.has(n.id) && !Oidset.has(n.id)) group(anchorIn(Tids, n.id)).theirs.push(n.id);
|
|
193064
|
+
const out = [];
|
|
193065
|
+
const emit = (anchor) => {
|
|
193066
|
+
const g = byAnchor.get(anchor);
|
|
193067
|
+
if (!g) return;
|
|
193068
|
+
if (g.ours.length > 0 && g.theirs.length > 0) warnings.push({ id: anchor ?? "", path, message: "both sides inserted here; kept ours-then-theirs - review the order" });
|
|
193069
|
+
out.push(...g.ours, ...g.theirs);
|
|
193070
|
+
};
|
|
193071
|
+
emit(null);
|
|
193072
|
+
for (const id of commonOrder) {
|
|
193073
|
+
out.push(id);
|
|
193074
|
+
emit(id);
|
|
193075
|
+
}
|
|
193076
|
+
return out.map((id) => merged.get(id));
|
|
193077
|
+
}
|
|
193078
|
+
function checkDuplicateIds(root2, conflicts) {
|
|
193079
|
+
const seen = /* @__PURE__ */ new Set(), dup = /* @__PURE__ */ new Set();
|
|
193080
|
+
const walk2 = (n) => {
|
|
193081
|
+
if (n.id) {
|
|
193082
|
+
if (seen.has(n.id)) dup.add(n.id);
|
|
193083
|
+
else seen.add(n.id);
|
|
193084
|
+
}
|
|
193085
|
+
n.children.forEach(walk2);
|
|
193086
|
+
};
|
|
193087
|
+
walk2(root2);
|
|
193088
|
+
for (const id of dup) conflicts.push({ id, path: "scene", base: void 0, ours: void 0, theirs: void 0, kind: "structural" });
|
|
193089
|
+
}
|
|
193090
|
+
var idMap = (nodes) => new Map(nodes.filter((n) => n.id).map((n) => [n.id, n]));
|
|
193091
|
+
var arrEq = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
|
|
193092
|
+
|
|
192734
193093
|
// ../../../expr/packages/expr/src/parser.ts
|
|
192735
193094
|
var ParseError = class extends Error {
|
|
192736
193095
|
constructor(message, pos2, source2) {
|
|
@@ -193190,6 +193549,18 @@ function evaluate(node, ctx, dialect) {
|
|
|
193190
193549
|
return val;
|
|
193191
193550
|
}
|
|
193192
193551
|
case "call": {
|
|
193552
|
+
if (n.name === "advance" && !dialect.functions[n.name]) {
|
|
193553
|
+
const arg = n.args[0];
|
|
193554
|
+
if (n.args.length !== 1 || arg === void 0) {
|
|
193555
|
+
throw new EvalError2(`advance() takes exactly 1 argument, got ${n.args.length}`);
|
|
193556
|
+
}
|
|
193557
|
+
const ladder = ladderOf(arg, ctx);
|
|
193558
|
+
if (ladder === void 0) {
|
|
193559
|
+
throw new EvalError2("advance() needs a quality reference (@scope.name of a quality property)");
|
|
193560
|
+
}
|
|
193561
|
+
const current = stageIndex(rec(arg), ladder, "advance");
|
|
193562
|
+
return ladder[Math.min(current + 1, ladder.length - 1)];
|
|
193563
|
+
}
|
|
193193
193564
|
const def = dialect.functions[n.name];
|
|
193194
193565
|
if (!def) throw new EvalError2(`unknown function '${n.name}'`);
|
|
193195
193566
|
return def.eval(n.args, { evaluate: rec, ctx });
|
|
@@ -193225,6 +193596,33 @@ function evaluate(node, ctx, dialect) {
|
|
|
193225
193596
|
}
|
|
193226
193597
|
const left = rec(n.left);
|
|
193227
193598
|
const right = rec(n.right);
|
|
193599
|
+
const lLadder = ladderOf(n.left, ctx);
|
|
193600
|
+
const rLadder = ladderOf(n.right, ctx);
|
|
193601
|
+
const ladder = lLadder ?? rLadder;
|
|
193602
|
+
if (ladder !== void 0) {
|
|
193603
|
+
if (lLadder && rLadder && !sameLadder(lLadder, rLadder)) {
|
|
193604
|
+
if (n.op === ">" || n.op === ">=" || n.op === "<" || n.op === "<=") {
|
|
193605
|
+
throw new EvalError2(`'${n.op}' compares two different qualities, whose stage orders are unrelated`);
|
|
193606
|
+
}
|
|
193607
|
+
}
|
|
193608
|
+
switch (n.op) {
|
|
193609
|
+
case ">":
|
|
193610
|
+
return stageIndex(left, ladder, ">") > stageIndex(right, ladder, ">");
|
|
193611
|
+
case ">=":
|
|
193612
|
+
return stageIndex(left, ladder, ">=") >= stageIndex(right, ladder, ">=");
|
|
193613
|
+
case "<":
|
|
193614
|
+
return stageIndex(left, ladder, "<") < stageIndex(right, ladder, "<");
|
|
193615
|
+
case "<=":
|
|
193616
|
+
return stageIndex(left, ladder, "<=") <= stageIndex(right, ladder, "<=");
|
|
193617
|
+
case "+":
|
|
193618
|
+
case "-":
|
|
193619
|
+
case "*":
|
|
193620
|
+
case "/":
|
|
193621
|
+
throw new EvalError2(`'${n.op}' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);
|
|
193622
|
+
default:
|
|
193623
|
+
break;
|
|
193624
|
+
}
|
|
193625
|
+
}
|
|
193228
193626
|
switch (n.op) {
|
|
193229
193627
|
case "==":
|
|
193230
193628
|
return valueEquals(left, right);
|
|
@@ -193276,6 +193674,19 @@ function assertNumbers(l2, r, op) {
|
|
|
193276
193674
|
throw new EvalError2(`'${op}' requires numeric operands, got ${typeof l2} and ${typeof r}`);
|
|
193277
193675
|
}
|
|
193278
193676
|
}
|
|
193677
|
+
function ladderOf(node, ctx) {
|
|
193678
|
+
if (node.kind !== "scopedvar" || ctx.qualities === void 0) return void 0;
|
|
193679
|
+
return ctx.qualities(node.scope, node.name);
|
|
193680
|
+
}
|
|
193681
|
+
function stageIndex(value, ladder, op) {
|
|
193682
|
+
if (typeof value !== "string") {
|
|
193683
|
+
throw new EvalError2(`'${op}' on a quality compares stages, got ${typeof value}`);
|
|
193684
|
+
}
|
|
193685
|
+
const i = ladder.indexOf(value);
|
|
193686
|
+
if (i < 0) throw new EvalError2(`"${value}" is not a stage of this quality (stages: ${ladder.join(", ")})`);
|
|
193687
|
+
return i;
|
|
193688
|
+
}
|
|
193689
|
+
var sameLadder = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
|
|
193279
193690
|
|
|
193280
193691
|
// ../../../expr/packages/expr/src/validate.ts
|
|
193281
193692
|
function validateExpressionAst(node, schema, dialect) {
|
|
@@ -193313,6 +193724,24 @@ function walkValidate(node, schema, dialect, path, issues) {
|
|
|
193313
193724
|
return;
|
|
193314
193725
|
}
|
|
193315
193726
|
case "call": {
|
|
193727
|
+
if (node.name === "advance" && !dialect.functions[node.name]) {
|
|
193728
|
+
if (node.args.length !== 1) {
|
|
193729
|
+
issues.push({ path, kind: "wrong-arg-count", severity: "error", message: `advance() takes exactly 1 argument, got ${node.args.length}` });
|
|
193730
|
+
}
|
|
193731
|
+
const arg = node.args[0];
|
|
193732
|
+
if (arg) {
|
|
193733
|
+
walkValidate(arg, schema, dialect, [...path, "args", 0], issues);
|
|
193734
|
+
if (stagesOf(arg, schema) === void 0 && typeOf(arg, schema, dialect) !== "unknown") {
|
|
193735
|
+
issues.push({
|
|
193736
|
+
path: [...path, "args", 0],
|
|
193737
|
+
kind: "wrong-arg-type",
|
|
193738
|
+
severity: "error",
|
|
193739
|
+
message: "advance() needs a quality reference (@scope.name of a quality property)"
|
|
193740
|
+
});
|
|
193741
|
+
}
|
|
193742
|
+
}
|
|
193743
|
+
return;
|
|
193744
|
+
}
|
|
193316
193745
|
const def = dialect.functions[node.name];
|
|
193317
193746
|
if (!def) {
|
|
193318
193747
|
issues.push({ path, kind: "unknown-function", severity: "error", message: `unknown function '${node.name}'` });
|
|
@@ -193400,11 +193829,17 @@ function inferredFromPropertyType(t) {
|
|
|
193400
193829
|
if (t === "number") return "number";
|
|
193401
193830
|
if (t === "string" || t === "enum") return "string";
|
|
193402
193831
|
if (t === "flags") return "flags";
|
|
193832
|
+
if (t === "quality") return "quality";
|
|
193403
193833
|
return "unknown";
|
|
193404
193834
|
}
|
|
193405
193835
|
function inferredFromReturn(t) {
|
|
193406
193836
|
return t;
|
|
193407
193837
|
}
|
|
193838
|
+
function stagesOf(node, schema) {
|
|
193839
|
+
if (node.kind !== "scopedvar") return void 0;
|
|
193840
|
+
const meta = schema.properties.get(node.scope)?.get(node.name);
|
|
193841
|
+
return meta?.type === "quality" ? meta.stages : void 0;
|
|
193842
|
+
}
|
|
193408
193843
|
function typeOf(node, schema, dialect) {
|
|
193409
193844
|
switch (node.kind) {
|
|
193410
193845
|
case "bool":
|
|
@@ -193420,6 +193855,7 @@ function typeOf(node, schema, dialect) {
|
|
|
193420
193855
|
return meta ? inferredFromPropertyType(meta.type) : "unknown";
|
|
193421
193856
|
}
|
|
193422
193857
|
case "call": {
|
|
193858
|
+
if (node.name === "advance") return "quality";
|
|
193423
193859
|
const def = dialect.functions[node.name];
|
|
193424
193860
|
return def ? inferredFromReturn(def.returnType) : "unknown";
|
|
193425
193861
|
}
|
|
@@ -193450,7 +193886,54 @@ function describeType(t) {
|
|
|
193450
193886
|
function checkBinaryOperandTypes(op, left, right, schema, dialect, path, issues) {
|
|
193451
193887
|
const lt = typeOf(left, schema, dialect);
|
|
193452
193888
|
const rt = typeOf(right, schema, dialect);
|
|
193453
|
-
|
|
193889
|
+
const ordering = op === ">" || op === "<" || op === ">=" || op === "<=";
|
|
193890
|
+
if (lt === "quality" || rt === "quality") {
|
|
193891
|
+
if (ordering || op === "==" || op === "!=") {
|
|
193892
|
+
const ls = stagesOf(left, schema);
|
|
193893
|
+
const rs = stagesOf(right, schema);
|
|
193894
|
+
if (ls && rs) {
|
|
193895
|
+
if (ls.join("\0") !== rs.join("\0")) {
|
|
193896
|
+
issues.push({ path, kind: "operand-type-mismatch", severity: "error", message: `'${op}' compares two different qualities, whose stages are unrelated` });
|
|
193897
|
+
}
|
|
193898
|
+
return;
|
|
193899
|
+
}
|
|
193900
|
+
const ladder = ls ?? rs;
|
|
193901
|
+
const other = ls ? right : left;
|
|
193902
|
+
const otherSide = ls ? "right" : "left";
|
|
193903
|
+
if (other.kind === "string") {
|
|
193904
|
+
if (ladder && !ladder.includes(other.value)) {
|
|
193905
|
+
issues.push({
|
|
193906
|
+
path: [...path, otherSide],
|
|
193907
|
+
kind: "unknown-stage",
|
|
193908
|
+
severity: "error",
|
|
193909
|
+
message: `'${other.value}' is not a stage of this quality - expected one of: ${ladder.join(", ")}`,
|
|
193910
|
+
reference: other.value
|
|
193911
|
+
});
|
|
193912
|
+
}
|
|
193913
|
+
return;
|
|
193914
|
+
}
|
|
193915
|
+
const ot = ls ? rt : lt;
|
|
193916
|
+
if (ot !== "unknown" && ot !== "quality") {
|
|
193917
|
+
issues.push({
|
|
193918
|
+
path: [...path, otherSide],
|
|
193919
|
+
kind: "operand-type-mismatch",
|
|
193920
|
+
severity: "error",
|
|
193921
|
+
message: `'${op}' on a quality compares stages, got ${describeType(ot)} on the ${otherSide}`
|
|
193922
|
+
});
|
|
193923
|
+
}
|
|
193924
|
+
return;
|
|
193925
|
+
}
|
|
193926
|
+
if (op === "+" || op === "-" || op === "*" || op === "/") {
|
|
193927
|
+
issues.push({
|
|
193928
|
+
path,
|
|
193929
|
+
kind: "operand-type-mismatch",
|
|
193930
|
+
severity: "error",
|
|
193931
|
+
message: `'${op}' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`
|
|
193932
|
+
});
|
|
193933
|
+
return;
|
|
193934
|
+
}
|
|
193935
|
+
}
|
|
193936
|
+
if (op === "+" || op === "-" || op === "*" || op === "/" || ordering) {
|
|
193454
193937
|
if (lt !== "unknown" && lt !== "number")
|
|
193455
193938
|
issues.push({ path: [...path, "left"], kind: "operand-type-mismatch", severity: "error", message: `'${op}' requires a number on the left, got ${describeType(lt)}` });
|
|
193456
193939
|
if (rt !== "unknown" && rt !== "number")
|
|
@@ -193676,6 +194159,7 @@ function hostScopesToSpec(reg) {
|
|
|
193676
194159
|
name: d.name,
|
|
193677
194160
|
type: d.type,
|
|
193678
194161
|
...d.values ? { values: d.values } : {},
|
|
194162
|
+
...d.stages ? { stages: d.stages } : {},
|
|
193679
194163
|
...d.default !== void 0 ? { default: d.default } : {},
|
|
193680
194164
|
...d.writable === false ? { writable: false } : {}
|
|
193681
194165
|
}))
|
|
@@ -193691,7 +194175,7 @@ function buildSchema(project, sceneProps = [], foreign) {
|
|
|
193691
194175
|
m = /* @__PURE__ */ new Map();
|
|
193692
194176
|
properties.set(scope, m);
|
|
193693
194177
|
}
|
|
193694
|
-
m.set(decl.name.toLowerCase(), { type: decl.type, enumValues: decl.values });
|
|
194178
|
+
m.set(decl.name.toLowerCase(), { type: decl.type, enumValues: decl.values, stages: decl.stages });
|
|
193695
194179
|
};
|
|
193696
194180
|
for (const decl of project.properties ?? []) put("patter", decl);
|
|
193697
194181
|
for (const decl of sceneProps) put("scene", decl);
|
|
@@ -193700,7 +194184,7 @@ function buildSchema(project, sceneProps = [], foreign) {
|
|
|
193700
194184
|
if (known.has(scope.token)) continue;
|
|
193701
194185
|
if (!scope.declarations?.length) continue;
|
|
193702
194186
|
const m = /* @__PURE__ */ new Map();
|
|
193703
|
-
for (const d of scope.declarations) m.set(d.name.toLowerCase(), { type: d.type, enumValues: d.values });
|
|
194187
|
+
for (const d of scope.declarations) m.set(d.name.toLowerCase(), { type: d.type, enumValues: d.values, stages: d.stages });
|
|
193704
194188
|
properties.set(scope.token, m);
|
|
193705
194189
|
}
|
|
193706
194190
|
return { properties };
|
|
@@ -194153,10 +194637,8 @@ function runValidate(loaded) {
|
|
|
194153
194637
|
const interpolation = validateInterpolation({ project, scenes, locales }, { foreignScopes });
|
|
194154
194638
|
const hygiene = checkHygiene([loaded.projectFile, ...Object.values(loaded.sceneFiles), ...loaded.localeFiles, ...loaded.authoringFiles]);
|
|
194155
194639
|
const staleBundles = checkBundles(loaded);
|
|
194156
|
-
const unresolvedMerges = walkFiles(loaded.root,
|
|
194157
|
-
|
|
194158
|
-
message: "unresolved merge conflict - resolve it and delete the .patterconflict sidecar before committing"
|
|
194159
|
-
}));
|
|
194640
|
+
const unresolvedMerges = sidecarIssues(walkFiles(loaded.root, CONFLICT_SIDECAR));
|
|
194641
|
+
const orphans = orphanShards(loaded);
|
|
194160
194642
|
return {
|
|
194161
194643
|
structural,
|
|
194162
194644
|
conditions,
|
|
@@ -194164,9 +194646,32 @@ function runValidate(loaded) {
|
|
|
194164
194646
|
hygiene,
|
|
194165
194647
|
staleBundles,
|
|
194166
194648
|
unresolvedMerges,
|
|
194167
|
-
|
|
194649
|
+
orphans,
|
|
194650
|
+
ok: structural.length === 0 && conditions.length === 0 && interpolation.length === 0 && hygiene.length === 0 && staleBundles.length === 0 && unresolvedMerges.length === 0 && orphans.length === 0
|
|
194168
194651
|
};
|
|
194169
194652
|
}
|
|
194653
|
+
function orphanShards(loaded) {
|
|
194654
|
+
const collected = /* @__PURE__ */ new Set([
|
|
194655
|
+
loaded.projectFile,
|
|
194656
|
+
...Object.values(loaded.sceneFiles),
|
|
194657
|
+
...loaded.localeFiles,
|
|
194658
|
+
...loaded.authoringFiles
|
|
194659
|
+
]);
|
|
194660
|
+
const kind = {
|
|
194661
|
+
".patterflow": "scene",
|
|
194662
|
+
".patterloc": "locale",
|
|
194663
|
+
".patterx": "authoring",
|
|
194664
|
+
".patterproj": "project"
|
|
194665
|
+
};
|
|
194666
|
+
const out = [];
|
|
194667
|
+
for (const [ext, what] of Object.entries(kind)) {
|
|
194668
|
+
for (const file of walkFiles(loaded.root, ext)) {
|
|
194669
|
+
if (collected.has(file)) continue;
|
|
194670
|
+
out.push({ file, message: `a ${what} shard outside the project's layout - nothing loads it, so its content is not in the project (move it under the configured folder, or delete it)` });
|
|
194671
|
+
}
|
|
194672
|
+
}
|
|
194673
|
+
return out;
|
|
194674
|
+
}
|
|
194170
194675
|
function checkBundles(loaded) {
|
|
194171
194676
|
const issues = [];
|
|
194172
194677
|
const bundles = walkFiles(loaded.root, ".patterc");
|
|
@@ -194246,7 +194751,16 @@ function compileScenes(loaded) {
|
|
|
194246
194751
|
blocks: s.blocks.map((b) => ({ ...b, children: b.children.map(clean3) }))
|
|
194247
194752
|
}));
|
|
194248
194753
|
}
|
|
194754
|
+
function refuseUnresolvedMerge(loaded) {
|
|
194755
|
+
const issues = sidecarIssues(walkFiles(loaded.root, CONFLICT_SIDECAR));
|
|
194756
|
+
if (!issues.length) return;
|
|
194757
|
+
throw new Error(
|
|
194758
|
+
`${issues.length} unresolved merge conflict(s) - resolve them and delete the .patterconflict sidecar(s) first:
|
|
194759
|
+
` + issues.map((i) => ` ${i.file}`).join("\n")
|
|
194760
|
+
);
|
|
194761
|
+
}
|
|
194249
194762
|
function runExport(loaded) {
|
|
194763
|
+
refuseUnresolvedMerge(loaded);
|
|
194250
194764
|
const { project, locales } = loaded;
|
|
194251
194765
|
const full = exportBundle({ project, scenes: compileScenes(loaded), locales });
|
|
194252
194766
|
const loc = project.export?.localisation;
|
|
@@ -194255,6 +194769,7 @@ function runExport(loaded) {
|
|
|
194255
194769
|
return { ...full, strings, localisation: { mode: "ids", ...loc.sourceDebug ? { sourceDebug: true } : {} } };
|
|
194256
194770
|
}
|
|
194257
194771
|
function runExportFull(loaded) {
|
|
194772
|
+
refuseUnresolvedMerge(loaded);
|
|
194258
194773
|
const { project, locales } = loaded;
|
|
194259
194774
|
return exportBundle({ project, scenes: compileScenes(loaded), locales });
|
|
194260
194775
|
}
|
|
@@ -194266,7 +194781,7 @@ function bundleOutputPath(loaded) {
|
|
|
194266
194781
|
}
|
|
194267
194782
|
|
|
194268
194783
|
// ../ops/src/playable-runtime.ts
|
|
194269
|
-
var PLAYABLE_RUNTIME_JS = '"use strict";var Patterplay=(()=>{var A=Object.defineProperty;var he=Object.getOwnPropertyDescriptor;var me=Object.getOwnPropertyNames;var Se=Object.prototype.hasOwnProperty;var ye=(n,e)=>{for(var t in e)A(n,t,{get:e[t],enumerable:!0})},ve=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of me(e))!Se.call(n,s)&&s!==t&&A(n,s,{get:()=>e[s],enumerable:!(r=he(e,s))||r.enumerable});return n};var be=n=>ve(A({},"__esModule",{value:!0}),n);var Ge={};ye(Ge,{Engine:()=>O,Flow:()=>w,buildTagIndex:()=>B,describeBundle:()=>pe,effectiveGameData:()=>ue,gameDataFields:()=>de,gameDataValue:()=>_});function S(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:S(n[2])};case"bin":return{kind:"binary",op:n[1],left:S(n[2]),right:S(n[3])};case"call":{let e=n.slice(2).map(S);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var p=class extends Error{constructor(e){super(e),this.name="EvalError"}};function C(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=e.scopes[i.scope];if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):o[i.name];if(a===void 0){if(r.get(i.scope)==="throw")throw new p(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{let o=t.functions[i.name];if(!o)throw new p(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new p("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 p(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new p(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let c=s(i.left);if(typeof c!="boolean")throw new p(`\'and\' requires boolean operands, left is ${typeof c}`);if(!c)return!1;let l=s(i.right);if(typeof l!="boolean")throw new p(`\'and\' requires boolean operands, right is ${typeof l}`);return l}if(i.op==="or"){let c=s(i.left);if(typeof c!="boolean")throw new p(`\'or\' requires boolean operands, left is ${typeof c}`);if(c)return!0;let l=s(i.right);if(typeof l!="boolean")throw new p(`\'or\' requires boolean operands, right is ${typeof l}`);return l}let o=s(i.left),a=s(i.right);switch(i.op){case"==":return J(o,a);case"!=":return!J(o,a);case">":return y(o,a,">"),o>a;case">=":return y(o,a,">="),o>=a;case"<":return y(o,a,"<"),o<a;case"<=":return y(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new p(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return y(o,a,"-"),o-a;case"*":return y(o,a,"*"),o*a;case"/":if(y(o,a,"/"),a===0)throw new p("division by zero");return o/a}}}};return s(n)}function J(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;for(let t=0;t<n.length;t++)if(n[t]!==e[t])return!1;return!0}return n===e}function y(n,e,t){if(typeof n!="number"||typeof e!="number")throw new p(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}var we={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},xe=[we];function z(n,e,t){let r=t?.countingCalls??xe;return E(n,t?.want??!0,e,r)}function E(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=E(n.left,e,t,r),i=E(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 E(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 G=class n{values={};decls=new Map;subscribers=new Set;auditors=new Set;norm;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),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??Y(t))}}get(e){return this.values[this.norm(e)]}set(e,t,r){let s=this.norm(e);if(this.decls.get(s)?.writable===!1)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])=>X(t,this.get(e),void 0,e))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e){for(let[t,r]of Object.entries(e))this.values[this.norm(t)]=r}};function X(n,e,t,r){return{name:r??n.name.toLowerCase(),type:n.type,value:e,default:n.default??Y(n),...n.values!==void 0?{values:n.values}:{},writable:t??n.writable??!0}}var F=1,x=class{scopes=new Map;defineOwned(e,t){return this.mountOwned(e,new G(t))}mountOwned(e,t){return this.assertFree(e),this.scopes.set(e,{kind:"owned",bag:t}),this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){this.assertFree(e);let i=new Map;for(let o of r)i.set(o.name.toLowerCase(),o);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:i,scopeWritable:s}),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(t.toLowerCase())}set(e,t,r){let s=this.scopes.get(e);if(!s)throw new Error(`unknown scope \'@${e}\'`);if(s.kind==="owned"){try{s.bag.set(t,r)}catch{throw new Error(`\'@${e}.${t}\' is read-only`)}return}let i=t.toLowerCase();if(!this.foreignWritable(s,i))throw new Error(`\'@${e}.${t}\' is read-only`);s.resolver.set(i,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)if(r.kind==="owned")for(let s of r.bag.rows())e.push({scope:t,...s});else for(let s of r.decls.values())e.push({scope:t,...X(s,r.resolver.get(s.name.toLowerCase()),this.foreignWritable(r,s.name.toLowerCase()))});return e}toEvalContext(e){let t={};for(let[r,s]of this.scopes)t[r]=s.kind==="owned"?s.bag.values:s.resolver;return{scopes:t,host:e}}toSchema(){let e=new Map;for(let[t,r]of this.scopes){let s=r.kind==="owned"?r.bag.declarations():[...r.decls.values()];if(s.length===0)continue;let i=new Map;for(let o of s)i.set(o.name.toLowerCase(),{type:o.type,enumValues:o.values});e.set(t,i)}return{properties:e}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());return e}load(e){for(let[t,r]of Object.entries(e)){let s=this.scopes.get(t);s?.kind==="owned"&&s.bag.load(r)}}saveFragment(){return{version:F,scopes:this.save()}}loadFragment(e){if(e.version!==F)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${F})`);this.load(e.scopes)}assertFree(e){if(this.scopes.has(e))throw new Error(`scope \'@${e}\' is already registered`)}};function Y(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return n.values?.[0]??"";case"flags":return[]}}function k(n){return n.ctx.host??{}}var T={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new p("random(a, b) requires exactly 2 arguments");let t=k(e).nextRandom;if(!t)throw new p("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 p("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new p("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:Q("check_flags"),eval(n,e){let t=Z(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new p("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:Q("set_flags"),eval(n,e){let t=[...Z(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new p("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:I("visits"),eval:(n,e)=>k(e).visits?.(D(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:I("seen"),eval:(n,e)=>(k(e).visits?.(D(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:I("patter_visits"),eval:(n,e)=>k(e).patterVisits?.(D(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:I("patter_seen"),eval:(n,e)=>(k(e).patterVisits?.(D(n,e,"patter_seen"))??0)>0}}};function V(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 D(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new p(`${t}(id) requires a string node id`);return r}function I(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 Z(n,e,t){if(!n)throw new p(`${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 p(`${t}() first argument must be a flags property`)}function Q(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 ke=/^@[A-Za-z0-9_.]+$/;function*Ce(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:ke.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 Ee(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function De(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function Ie(n){let e="",t=!1;for(let r of n){if(De(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function ee(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?Ie(r):n}function te(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of Ce(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?"":Ee(s)}return t}function b(n,e){for(let t of n){e(t);let r=t.children;r&&b(r,e)}}function Re(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function g(n){let e=n.gameId?.trim();return e||Re(n.name)}function M(n){return`cast:${n}`}var j={open:"[",close:"]"},re="SFX";function R(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function B(n){let e=new Map,t=(r,s)=>{let i=R([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,R([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=R(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=R([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var ne=new WeakMap,O=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;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);this.defaultSeed=(t.seed??2654435769)>>>0;let u=new Map,f=new Map,H=new Map;for(let[d,m]of Object.entries(e.scenes)){this.sceneGameIdToId.set(g(m),d);let v=new Map;for(let h of m.blocks)f.set(h.id,{sceneId:d}),H.set(h.id,h),v.set(g(h),h.id),b(h.children,K=>u.set(K.id,K));this.blockGameIdToId.set(d,v)}let q=e.properties??[],P=q.filter(d=>d.shared??!0).map(ie),ge=q.filter(d=>!(d.shared??!0)).map(ie),fe=new Set(P.map(d=>d.name.toLowerCase())),N=new x().defineOwned("patter",P),U=new Set;if(t.world){let d=e.scopeRegistry?.scopes.find(v=>v.token==="world"),m=(d?.declarations??[]).map(oe);N.defineForeign("world",t.world,m,d?.writable??!0),U.add("world")}for(let d of e.scopeRegistry?.scopes??[]){if(U.has(d.token))continue;let m=(d.declarations??[]).map(oe);N.defineForeign(d.token,Pe(d.declarations??[]),m,d.writable??!0)}let W=new Map;for(let[d,m]of Object.entries(e.scenes)){let v=new Set((m.sceneProps??[]).filter(h=>h.shared??!1).map(h=>h.name.toLowerCase()));W.set(d,v)}this.host={bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,nodeIndex:u,blockIndex:f,blockById:H,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:B(e),shared:N,patterSharedDecls:P,patterLocalDecls:ge,patterSharedNames:fe,sceneSharedNames:W,sharedVisits:new Map,sharedSelectors:new Map,stageBags:new Map,customRng:t.rng,onDryChoice:t.onDryChoice,replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??j).open,captionClose:(e.closedCaptions??j).close,captionCharacter:e.closedCaptions?.character||re,refSplitCache:new Map}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}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){let t=this.saveGame(),r=i=>(i.setLocale(this.currentLocale),i.setClosedCaptions(this.host.captionsOn),i),s=new n(e,this.creationOptions);try{return s.loadGame(t),r(s)}catch{let i=new n(e,this.creationOptions);for(let[o,a]of Object.entries(t.flows)){let c=a.cursor.currentSceneId;try{i.openFlow(o,c!==null?{scene:c}:{})}catch{}}return r(i)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){let r=this.resolveSceneRef(t.scene),s=this.resolveBlockRef(r,t.block);this.flowsById.get(e)?.close();let i=new w(e,this.host,t.seed??this.defaultSeed);return this.flowsById.set(e,i),i.start(r,s),i}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}resolveSceneRef(e){if(e!=null)return this.host.bundle.scenes[e]?e:this.sceneGameIdToId.get(e)??e}resolveBlockRef(e,t){if(t!=null){if(this.host.blockById.has(t))return t;if(e!=null){let r=this.blockGameIdToId.get(e)?.get(t);if(r)return r}return t}}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?g(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?g(t):void 0}tagsForBeat(e){return this.host.tagIndex.get(e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?this.host.tagIndex.get(t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?this.host.tagIndex.get(s):void 0)??[]}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)se(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 se(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...g(e)?{gameId:g(e)}:{},name:e.name,...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...g(t)?{gameId:g(t)}:{},name:t.name,...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)b(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=this.host.tagIndex.get(e.id),r={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){r.character=e.character;let s=this.host.defaultStrings[M(e.character)]??this.host.castDisplay.get(e.character);s!==void 0&&(r.characterName=s)}e.direction!==void 0&&(r.direction=e.direction)}if(e.kind==="line"||e.kind==="text"){let s=this.host.defaultStrings[e.id];s!==void 0&&(r.text=s)}return e.gameData&&Object.keys(e.gameData).length&&(r.gameData=e.gameData),t&&t.length&&(r.tags=t),r}tagsField(e){let t=this.host.tagIndex.get(e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}closeFlow(e){this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.shared.reseedOwned("patter",this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear(),this.host.stageBags.clear()}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.shared.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e);this.host.shared.set(r,s,t)}listProperties(){return this.host.patterSharedDecls.map(e=>({ref:`@${e.name}`,type:e.type,values:e.values,value:this.getProperty(`@${e.name}`),default:Be(e)}))}splitShared(e){let t=this.host.refSplitCache.get(e);if(t||(t=V(e,r=>r==="scene"||this.host.shared.has(r)),this.host.refSplitCache.set(e,t)),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 this.host.shared.save()}load(e){this.host.shared.load(e)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:2,shared:this.host.shared.save(),sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:ce(this.host.sharedSelectors),stageBags:Object.fromEntries([...this.host.stageBags].map(([t,r])=>[t,{...r}])),flows:e}}loadGame(e){if(e.version!==2)throw new Error(`unsupported save version: ${e.version}`);this.host.shared.load(e.shared),this.host.sharedVisits.clear();for(let[t,r]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(t,r);this.host.sharedSelectors.clear();for(let[t,r]of le(e.sharedSelectors))this.host.sharedSelectors.set(t,r);this.host.stageBags.clear();for(let[t,r]of Object.entries(e.stageBags??{}))this.host.stageBags.set(t,{...r});this.flowsById.clear();for(let[t,r]of Object.entries(e.flows)){let s=new w(t,this.host,this.defaultSeed);s.restore(r),this.flowsById.set(t,s)}}},w=class{id;host;local;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPromptBeat=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.shared.get("patter",e):this.local.get("patter",e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.shared.set("patter",e,t):this.local.set("patter",e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;return t===null?void 0:(this.host.sceneSharedNames.get(t)?.has(e)?this.host.stageBags.get(t):this.sceneBags.get(t))?.[e]},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.host.sceneSharedNames.get(r)?.has(e)?this.host.stageBags.get(r):this.sceneBags.get(r);s&&(s[e]=t)}};evalCtx;constructor(e,t,r){this.id=e,this.host=t,this.rngState=r>>>0,this.local=this.freshLocal();let s={...t.shared.toEvalContext().scopes};s.patter=this.patterResolver,s.scene=this.sceneResolver,this.evalCtx={scopes:s,host:{nextRandom:this.rng,visits:i=>this.visitCounts.get(i)??0,patterVisits:i=>this.host.sharedVisits.get(i)??0}}}start(e,t){if(this.sceneBags.clear(),this.local=this.freshLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.pendingChoice=null,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.start(e,t)}goto(e,t){if(this.closed)return!1;if(e==="END")return this.started=!0,this.pendingChoice=null,this.pendingPromptBeat=null,this.pendingPromptOwnerId=null,this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=this.host.sceneGameIdToId.get(e)??(this.host.bundle.scenes[e]?e:void 0);if(r===void 0)return!1;let s;return t!==void 0&&(s=this.host.blockGameIdToId.get(r)?.get(t)??(this.host.blockIndex.get(t)?.sceneId===r?t:void 0),s===void 0)?!1:this.started?(this.pendingChoice=null,this.pendingPromptBeat=null,this.pendingPromptOwnerId=null,this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.start(r,s),!0)}close(){this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.pendingChoice=null,this.pendingPromptBeat=null,this.pendingPromptOwnerId=null}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.pendingPromptBeat){let e=this.pendingPromptBeat;return this.pendingPromptBeat=null,this.pendingPromptOwnerId=null,this.beatResult(e)}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);let s=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(s);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}for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}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(i=>i.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);let s=t.byId.get(e);this.pendingChoice=null,this.pendingPromptBeat=this.host.replayPromptOnChoose?this.promptBeatOf(s)??null:null,this.pendingPromptOwnerId=this.pendingPromptBeat?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.shared.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitRef(e);if(r==="patter")this.patterResolver.set(s,t);else if(r==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);this.sceneResolver.set(s,t)}else this.host.shared.set(r,s,t)}snapshot(){return{scopes:this.local.save(),sceneBags:Object.fromEntries([...this.sceneBags].map(([e,t])=>[e,{...t}])),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,selectors:ce(this.selectors)}}}restore(e){this.rngState=e.rngState>>>0,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.sceneBags=new Map(Object.entries(e.sceneBags??{}).map(([r,s])=>[r,{...s}])),this.local=this.freshLocal(),this.local.load(e.scopes),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=le(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);o&&(r.set(i.id,o),s.push({...i}))}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:r})}if(this.pendingPromptBeat=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId);this.pendingPromptBeat=r?this.promptBeatOf(r)??null:null,this.pendingPromptBeat||(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)}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),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.length>0){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.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(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.filter(s=>this.eligible(s));if(t.length===0)return null;let r=this.selectorState(e);switch(e.selector){case"branch":return t[0];case"sequence":{let s=e.options?.order??"sequential",i=e.options?.exhaust??"once";return s==="shuffle"?this.pickShuffle(t,i,r):s==="specificity"?this.pickSpecificity(t,i,r):this.pickSequential(t,i,r)}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);if(r.bag===void 0&&(r.bag=o()),r.bag.length===0){if(t==="once")return null;if(i){let f=e[s-1];return r.last=f.id,f}r.bag=o()}let a=r.bag,c=r.last!==void 0&&a.length>1?a.indexOf(r.last):-1,l=Math.floor(this.rng()*(c>=0?a.length-1:a.length));c>=0&&l>=c&&l++;let u=a[l];return a.splice(l,1),r.last=u,e.find(f=>f.id===u)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(u=>u.id));let l=new Set(r.bag);if(s=e.filter(u=>l.has(u.id)),s.length===0)return t==="stick"&&r.last!==void 0?e.find(u=>u.id===r.last)??null:null}let i=-1,a=s.map(l=>{let u=this.specScore(l);return u>i&&(i=u),{c:l,s:u}}).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(f=>f.id===r.last):-1,u=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&u>=l&&u++,c=a[u]}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):0}matchedSpec(e,t){return z(e,s=>ae(C(s,this.evalCtx,T)),{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=t.get(e.id);return r||(r={},t.set(e.id,r)),r}runEffects(e){for(let t of e??[])this.setProperty(t.target,this.evalExpr(t.value))}eligible(e){return e.condition?ae(this.evalExpr(e.condition)):!0}evalExpr(e){return C(this.conditionAst(e),this.evalCtx,T)}conditionAst(e){let t=ne.get(e);return t||(t=S(e.ast),ne.set(e,t)),t}enter(e){this.visitCounts.set(e,(this.visitCounts.get(e)??0)+1),this.host.sharedVisits.set(e,(this.host.sharedVisits.get(e)??0)+1)}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=this.rngState+1831565813|0;this.rngState=e;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};beatResult(e){let t=this.host.tagIndex.get(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)),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,gameData:e.gameData,...r}}}}interpolate(e){return te(e,t=>this.getProperty(t))}stripCaptions(e){return ee(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));return t.kind==="line"?{kind:"line",text:this.captionLine(r),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction}:{kind:"text",text:r}}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 b(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=M(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}splitRef(e){let t=this.host.refSplitCache.get(e);return t||(t=V(e,r=>r==="scene"||this.host.shared.has(r)),this.host.refSplitCache.set(e,t)),t}freshLocal(){return new x().defineOwned("patter",this.host.patterLocalDecls)}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;if(!this.sceneBags.has(e.id)){let r={};for(let s of e.sceneProps??[]){let i=s.name.toLowerCase();t.has(i)||(r[i]=L(s))}this.sceneBags.set(e.id,r)}if(!this.host.stageBags.has(e.id)){let r={};for(let s of e.sceneProps??[]){let i=s.name.toLowerCase();t.has(i)&&(r[i]=L(s))}this.host.stageBags.set(e.id,r)}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);i&&(i[s]=L(r))}}};function se(n,e){b(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 ce(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 le(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 ie(n){return{name:n.name,type:n.type,values:n.values,default:n.default}}function Be(n){if(n.default!==void 0)return n.default;switch(n.type){case"number":return 0;case"string":return"";case"flags":return[];case"enum":return n.values?.[0]??"";default:return!1}}function oe(n){return{name:n.name,type:n.type,values:n.values,default:n.default,writable:n.writable}}function Oe(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"flags":return[];case"enum":return n.values?.[0]??""}}function Pe(n){let e=r=>r.toLowerCase(),t=new Map;for(let r of n)t.set(e(r.name),Oe(r));return{get:r=>t.get(e(r)),set:(r,s)=>{t.set(e(r),s)}}}function L(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"flags":return[];case"enum":return n.values?.[0]??""}}function ae(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var Ne=(n,e)=>n.shared??e;function $(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:Ne(n,e)}}function Ae(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function Fe(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 pe(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=g(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:g(c),name:c.name}))});for(let c of o.blocks)Fe(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>$(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=>$(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(Ae)}));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=>$(o,!0)),scene:r},gameData:i,counts:e}}function de(n,e){return n.gameDataFields?.[e]??[]}function _(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function ue(n,e){let t={};for(let r of n){let s=_(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 be(Ge);})();\n//# sourceMappingURL=patterplay.min.js.map';
|
|
194784
|
+
var PLAYABLE_RUNTIME_JS = '"use strict";var Patterplay=(()=>{var G=Object.defineProperty;var Se=Object.getOwnPropertyDescriptor;var ye=Object.getOwnPropertyNames;var ve=Object.prototype.hasOwnProperty;var be=(n,e)=>{for(var t in e)G(n,t,{get:e[t],enumerable:!0})},we=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of ye(e))!ve.call(n,s)&&s!==t&&G(n,s,{get:()=>e[s],enumerable:!(r=Se(e,s))||r.enumerable});return n};var xe=n=>we(G({},"__esModule",{value:!0}),n);var Me={};be(Me,{Engine:()=>N,Flow:()=>k,buildTagIndex:()=>P,describeBundle:()=>ue,effectiveGameData:()=>fe,gameDataFields:()=>ge,gameDataValue:()=>U});function v(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:v(n[2])};case"bin":return{kind:"binary",op:n[1],left:v(n[2]),right:v(n[3])};case"call":{let e=n.slice(2).map(v);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var l=class extends Error{constructor(e){super(e),this.name="EvalError"}};function D(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=e.scopes[i.scope];if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):o[i.name];if(a===void 0){if(r.get(i.scope)==="throw")throw new l(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!t.functions[i.name]){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new l(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=T(a,e);if(c===void 0)throw new l("advance() needs a quality reference (@scope.name of a quality property)");let d=S(s(a),c,"advance");return c[Math.min(d+1,c.length-1)]}let o=t.functions[i.name];if(!o)throw new l(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new l("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 l(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new l(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let g=s(i.left);if(typeof g!="boolean")throw new l(`\'and\' requires boolean operands, left is ${typeof g}`);if(!g)return!1;let h=s(i.right);if(typeof h!="boolean")throw new l(`\'and\' requires boolean operands, right is ${typeof h}`);return h}if(i.op==="or"){let g=s(i.left);if(typeof g!="boolean")throw new l(`\'or\' requires boolean operands, left is ${typeof g}`);if(g)return!0;let h=s(i.right);if(typeof h!="boolean")throw new l(`\'or\' requires boolean operands, right is ${typeof h}`);return h}let o=s(i.left),a=s(i.right),c=T(i.left,e),d=T(i.right,e),p=c??d;if(p!==void 0){if(c&&d&&!ke(c,d)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new l(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return S(o,p,">")>S(a,p,">");case">=":return S(o,p,">=")>=S(a,p,">=");case"<":return S(o,p,"<")<S(a,p,"<");case"<=":return S(o,p,"<=")<=S(a,p,"<=");case"+":case"-":case"*":case"/":throw new l(`\'${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 X(o,a);case"!=":return!X(o,a);case">":return b(o,a,">"),o>a;case">=":return b(o,a,">="),o>=a;case"<":return b(o,a,"<"),o<a;case"<=":return b(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new l(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return b(o,a,"-"),o-a;case"*":return b(o,a,"*"),o*a;case"/":if(b(o,a,"/"),a===0)throw new l("division by zero");return o/a}}}};return s(n)}function X(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;for(let t=0;t<n.length;t++)if(n[t]!==e[t])return!1;return!0}return n===e}function b(n,e,t){if(typeof n!="number"||typeof e!="number")throw new l(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function T(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function S(n,e,t){if(typeof n!="string")throw new l(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new l(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var ke=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);var Ce={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},Ee=[Ce];function Y(n,e,t){let r=t?.countingCalls??Ee;return I(n,t?.want??!0,e,r)}function I(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=I(n.left,e,t,r),i=I(n.right,e,t,r);return n.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(n.kind==="unary"&&n.op==="not")return I(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 M=class n{values={};decls=new Map;subscribers=new Set;auditors=new Set;norm;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),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??Q(t))}}get(e){return this.values[this.norm(e)]}set(e,t,r){let s=this.norm(e);if(this.decls.get(s)?.writable===!1)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])=>Z(t,this.get(e),void 0,e))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e){for(let[t,r]of Object.entries(e))this.values[this.norm(t)]=r}};function Z(n,e,t,r){return{name:r??n.name.toLowerCase(),type:n.type,value:e,default:n.default??Q(n),...n.values!==void 0?{values:n.values}:{},writable:t??n.writable??!0}}var V=1,C=class{scopes=new Map;defineOwned(e,t){return this.mountOwned(e,new M(t))}mountOwned(e,t){return this.assertFree(e),this.scopes.set(e,{kind:"owned",bag:t}),this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){this.assertFree(e);let i=new Map;for(let o of r)i.set(o.name.toLowerCase(),o);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:i,scopeWritable:s}),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(t.toLowerCase())}set(e,t,r){let s=this.scopes.get(e);if(!s)throw new Error(`unknown scope \'@${e}\'`);if(s.kind==="owned"){try{s.bag.set(t,r)}catch{throw new Error(`\'@${e}.${t}\' is read-only`)}return}let i=t.toLowerCase();if(!this.foreignWritable(s,i))throw new Error(`\'@${e}.${t}\' is read-only`);s.resolver.set(i,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)if(r.kind==="owned")for(let s of r.bag.rows())e.push({scope:t,...s});else for(let s of r.decls.values())e.push({scope:t,...Z(s,r.resolver.get(s.name.toLowerCase()),this.foreignWritable(r,s.name.toLowerCase()))});return e}toEvalContext(e){let t={};for(let[s,i]of this.scopes)t[s]=i.kind==="owned"?i.bag.values:i.resolver;let r=this.qualityLadders();return r.size===0?{scopes:t,host:e}:{scopes:t,host:e,qualities:(s,i)=>r.get(s)?.get(i.toLowerCase())}}qualityLadders(){let e=new Map;for(let[t,r]of this.scopes){let s=r.kind==="owned"?r.bag.declarations():[...r.decls.values()];for(let i of s){if(i.type!=="quality"||i.stages===void 0)continue;let o=e.get(t);o||(o=new Map,e.set(t,o)),o.set(i.name.toLowerCase(),i.stages)}}return e}toSchema(){let e=new Map;for(let[t,r]of this.scopes){let s=r.kind==="owned"?r.bag.declarations():[...r.decls.values()];if(s.length===0)continue;let i=new Map;for(let o of s)i.set(o.name.toLowerCase(),{type:o.type,enumValues:o.values,...o.stages!==void 0?{stages:o.stages}:{}});e.set(t,i)}return{properties:e}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());return e}load(e){for(let[t,r]of Object.entries(e)){let s=this.scopes.get(t);s?.kind==="owned"&&s.bag.load(r)}}saveFragment(){return{version:V,scopes:this.save()}}loadFragment(e){if(e.version!==V)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${V})`);this.load(e.scopes)}assertFree(e){if(this.scopes.has(e))throw new Error(`scope \'@${e}\' is already registered`)}};function Q(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??""}}function E(n){return n.ctx.host??{}}var L={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new l("random(a, b) requires exactly 2 arguments");let t=E(e).nextRandom;if(!t)throw new l("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 l("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new l("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:te("check_flags"),eval(n,e){let t=ee(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new l("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:te("set_flags"),eval(n,e){let t=[...ee(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new l("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:B("visits"),eval:(n,e)=>E(e).visits?.(R(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:B("seen"),eval:(n,e)=>(E(e).visits?.(R(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:B("patter_visits"),eval:(n,e)=>E(e).patterVisits?.(R(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:B("patter_seen"),eval:(n,e)=>(E(e).patterVisits?.(R(n,e,"patter_seen"))??0)>0}}};function j(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 R(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new l(`${t}(id) requires a string node id`);return r}function B(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 ee(n,e,t){if(!n)throw new l(`${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 l(`${t}() first argument must be a flags property`)}function te(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 De=/^@[A-Za-z0-9_.]+$/;function*Ie(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:De.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 Re(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function Be(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function Oe(n){let e="",t=!1;for(let r of n){if(Be(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function re(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?Oe(r):n}function ne(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of Ie(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?"":Re(s)}return t}function x(n,e){for(let t of n){e(t);let r=t.children;r&&x(r,e)}}function Pe(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function f(n){let e=n.gameId?.trim();return e||Pe(n.name)}function $(n){return`cast:${n}`}var _={open:"[",close:"]"},se="SFX";function O(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function P(n){let e=new Map,t=(r,s)=>{let i=O([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,O([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=O(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=O([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var ie=new WeakMap,N=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;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 d=new Map;for(let u of e.cast??[])u.displayName&&d.set(u.name,u.displayName);this.defaultSeed=(t.seed??2654435769)>>>0;let p=new Map,g=new Map,h=new Map;for(let[u,y]of Object.entries(e.scenes)){this.sceneGameIdToId.set(f(y),u);let w=new Map;for(let m of y.blocks)g.set(m.id,{sceneId:u}),h.set(m.id,m),w.set(f(m),m.id),x(m.children,J=>p.set(J.id,J));this.blockGameIdToId.set(u,w)}let W=e.properties??[],A=W.filter(u=>u.shared??!0).map(ae),he=W.filter(u=>!(u.shared??!0)).map(ae),me=new Set(A.map(u=>u.name.toLowerCase())),F=new C().defineOwned("patter",A),K=new Set;if(t.world){let u=e.scopeRegistry?.scopes.find(w=>w.token==="world"),y=(u?.declarations??[]).map(ce);F.defineForeign("world",t.world,y,u?.writable??!0),K.add("world")}for(let u of e.scopeRegistry?.scopes??[]){if(K.has(u.token))continue;let y=(u.declarations??[]).map(ce);F.defineForeign(u.token,Fe(u.declarations??[]),y,u.writable??!0)}let z=new Map;for(let[u,y]of Object.entries(e.scenes)){let w=new Set((y.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));z.set(u,w)}this.host={bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:d,nodeIndex:p,blockIndex:g,blockById:h,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:P(e),shared:F,patterSharedDecls:A,patterLocalDecls:he,patterSharedNames:me,sceneSharedNames:z,sharedVisits:new Map,sharedSelectors:new Map,stageBags:new Map,customRng:t.rng,onDryChoice:t.onDryChoice,replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??_).open,captionClose:(e.closedCaptions??_).close,captionCharacter:e.closedCaptions?.character||se,refSplitCache:new Map}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}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){let t=this.saveGame(),r=i=>(i.setLocale(this.currentLocale),i.setClosedCaptions(this.host.captionsOn),i),s=new n(e,this.creationOptions);try{return s.loadGame(t),r(s)}catch{let i=new n(e,this.creationOptions);for(let[o,a]of Object.entries(t.flows)){let c=a.cursor.currentSceneId;try{i.openFlow(o,c!==null?{scene:c}:{})}catch{}}return r(i)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){let r=this.resolveSceneRef(t.scene),s=this.resolveBlockRef(r,t.block);this.flowsById.get(e)?.close();let i=new k(e,this.host,t.seed??this.defaultSeed);return this.flowsById.set(e,i),i.start(r,s),i}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}resolveSceneRef(e){if(e!=null)return this.host.bundle.scenes[e]?e:this.sceneGameIdToId.get(e)??e}resolveBlockRef(e,t){if(t!=null){if(this.host.blockById.has(t))return t;if(e!=null){let r=this.blockGameIdToId.get(e)?.get(t);if(r)return r}return t}}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?f(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?f(t):void 0}tagsForBeat(e){return this.host.tagIndex.get(e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?this.host.tagIndex.get(t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?this.host.tagIndex.get(s):void 0)??[]}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)oe(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 oe(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...f(e)?{gameId:f(e)}:{},name:e.name,...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...f(t)?{gameId:f(t)}:{},name:t.name,...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)x(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=this.host.tagIndex.get(e.id),r={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){r.character=e.character;let s=this.host.defaultStrings[$(e.character)]??this.host.castDisplay.get(e.character);s!==void 0&&(r.characterName=s)}e.direction!==void 0&&(r.direction=e.direction)}if(e.kind==="line"||e.kind==="text"){let s=this.host.defaultStrings[e.id];s!==void 0&&(r.text=s)}return e.gameData&&Object.keys(e.gameData).length&&(r.gameData=e.gameData),t&&t.length&&(r.tags=t),r}tagsField(e){let t=this.host.tagIndex.get(e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}closeFlow(e){this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.shared.reseedOwned("patter",this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear(),this.host.stageBags.clear()}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.shared.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e);this.host.shared.set(r,s,t)}listProperties(){return this.host.patterSharedDecls.map(e=>({ref:`@${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:Ne(e)}))}splitShared(e){let t=this.host.refSplitCache.get(e);if(t||(t=j(e,r=>r==="scene"||this.host.shared.has(r)),this.host.refSplitCache.set(e,t)),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 this.host.shared.save()}load(e){this.host.shared.load(e)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:2,shared:this.host.shared.save(),sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:pe(this.host.sharedSelectors),stageBags:Object.fromEntries([...this.host.stageBags].map(([t,r])=>[t,{...r}])),flows:e}}loadGame(e){if(e.version!==2)throw new Error(`unsupported save version: ${e.version}`);this.host.shared.load(e.shared),this.host.sharedVisits.clear();for(let[t,r]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(t,r);this.host.sharedSelectors.clear();for(let[t,r]of de(e.sharedSelectors))this.host.sharedSelectors.set(t,r);this.host.stageBags.clear();for(let[t,r]of Object.entries(e.stageBags??{}))this.host.stageBags.set(t,{...r});this.flowsById.clear();for(let[t,r]of Object.entries(e.flows)){let s=new k(t,this.host,this.defaultSeed);s.restore(r),this.flowsById.set(t,s)}}},k=class{id;host;local;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPromptBeat=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.shared.get("patter",e):this.local.get("patter",e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.shared.set("patter",e,t):this.local.set("patter",e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;return t===null?void 0:(this.host.sceneSharedNames.get(t)?.has(e)?this.host.stageBags.get(t):this.sceneBags.get(t))?.[e]},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.host.sceneSharedNames.get(r)?.has(e)?this.host.stageBags.get(r):this.sceneBags.get(r);s&&(s[e]=t)}};evalCtx;constructor(e,t,r){this.id=e,this.host=t,this.rngState=r>>>0,this.local=this.freshLocal();let s={...t.shared.toEvalContext().scopes};s.patter=this.patterResolver,s.scene=this.sceneResolver,this.evalCtx={scopes:s,host:{nextRandom:this.rng,visits:i=>this.visitCounts.get(i)??0,patterVisits:i=>this.host.sharedVisits.get(i)??0},qualities:(i,o)=>this.stagesFor(i,o)}}stagesFor(e,t){let r=t.toLowerCase(),s=i=>i?.find(o=>o.name.toLowerCase()===r&&o.type==="quality")?.stages;if(e==="patter")return s(this.host.patterSharedDecls)??s(this.host.patterLocalDecls);if(e==="scene"){let i=this.currentSceneId!=null?this.host.bundle.scenes[this.currentSceneId]:void 0;return s(i?.sceneProps)}return s(this.host.bundle.scopeRegistry?.scopes.find(i=>i.token===e)?.declarations)}start(e,t){if(this.sceneBags.clear(),this.local=this.freshLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.pendingChoice=null,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.start(e,t)}goto(e,t){if(this.closed)return!1;if(e==="END")return this.started=!0,this.pendingChoice=null,this.pendingPromptBeat=null,this.pendingPromptOwnerId=null,this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=this.host.sceneGameIdToId.get(e)??(this.host.bundle.scenes[e]?e:void 0);if(r===void 0)return!1;let s;return t!==void 0&&(s=this.host.blockGameIdToId.get(r)?.get(t)??(this.host.blockIndex.get(t)?.sceneId===r?t:void 0),s===void 0)?!1:this.started?(this.pendingChoice=null,this.pendingPromptBeat=null,this.pendingPromptOwnerId=null,this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.start(r,s),!0)}close(){this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.pendingChoice=null,this.pendingPromptBeat=null,this.pendingPromptOwnerId=null}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.pendingPromptBeat){let e=this.pendingPromptBeat;return this.pendingPromptBeat=null,this.pendingPromptOwnerId=null,this.beatResult(e)}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);let s=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(s);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}for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}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(i=>i.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);let s=t.byId.get(e);this.pendingChoice=null,this.pendingPromptBeat=this.host.replayPromptOnChoose?this.promptBeatOf(s)??null:null,this.pendingPromptOwnerId=this.pendingPromptBeat?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.shared.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitRef(e);if(r==="patter")this.patterResolver.set(s,t);else if(r==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);this.sceneResolver.set(s,t)}else this.host.shared.set(r,s,t)}snapshot(){return{scopes:this.local.save(),sceneBags:Object.fromEntries([...this.sceneBags].map(([e,t])=>[e,{...t}])),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,selectors:pe(this.selectors)}}}restore(e){this.rngState=e.rngState>>>0,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.sceneBags=new Map(Object.entries(e.sceneBags??{}).map(([r,s])=>[r,{...s}])),this.local=this.freshLocal(),this.local.load(e.scopes),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=de(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);o&&(r.set(i.id,o),s.push({...i}))}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:r})}if(this.pendingPromptBeat=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId);this.pendingPromptBeat=r?this.promptBeatOf(r)??null:null,this.pendingPromptBeat||(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)}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),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.length>0){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.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(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.filter(s=>this.eligible(s));if(t.length===0)return null;let r=this.selectorState(e);switch(e.selector){case"branch":return t[0];case"sequence":{let s=e.options?.order??"sequential",i=e.options?.exhaust??"once";return s==="shuffle"?this.pickShuffle(t,i,r):s==="specificity"?this.pickSpecificity(t,i,r):this.pickSequential(t,i,r)}default:return null}}pickSequential(e,t,r){let s=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,r){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(g=>g.id);if(r.bag===void 0&&(r.bag=o()),r.bag.length===0){if(t==="once")return null;if(i){let g=e[s-1];return r.last=g.id,g}r.bag=o()}let a=r.bag,c=r.last!==void 0&&a.length>1?a.indexOf(r.last):-1,d=Math.floor(this.rng()*(c>=0?a.length-1:a.length));c>=0&&d>=c&&d++;let p=a[d];return a.splice(d,1),r.last=p,e.find(g=>g.id===p)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(p=>p.id));let d=new Set(r.bag);if(s=e.filter(p=>d.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(d=>{let p=this.specScore(d);return p>i&&(i=p),{c:d,s:p}}).filter(d=>d.s===i).map(d=>d.c),c;if(a.length===1)c=a[0];else{let d=r.last!==void 0?a.findIndex(g=>g.id===r.last):-1,p=Math.floor(this.rng()*(d>=0?a.length-1:a.length));d>=0&&p>=d&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(d=>d!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0):0}matchedSpec(e,t){return Y(e,s=>le(D(s,this.evalCtx,L)),{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=t.get(e.id);return r||(r={},t.set(e.id,r)),r}runEffects(e){for(let t of e??[])this.setProperty(t.target,this.evalExpr(t.value))}eligible(e){return e.condition?le(this.evalExpr(e.condition)):!0}evalExpr(e){return D(this.conditionAst(e),this.evalCtx,L)}conditionAst(e){let t=ie.get(e);return t||(t=v(e.ast),ie.set(e,t)),t}enter(e){this.visitCounts.set(e,(this.visitCounts.get(e)??0)+1),this.host.sharedVisits.set(e,(this.host.sharedVisits.get(e)??0)+1)}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=this.rngState+1831565813|0;this.rngState=e;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};beatResult(e){let t=this.host.tagIndex.get(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)),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,gameData:e.gameData,...r}}}}interpolate(e){return ne(e,t=>this.getProperty(t))}stripCaptions(e){return re(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));return t.kind==="line"?{kind:"line",text:this.captionLine(r),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction}:{kind:"text",text:r}}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 x(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=$(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}splitRef(e){let t=this.host.refSplitCache.get(e);return t||(t=j(e,r=>r==="scene"||this.host.shared.has(r)),this.host.refSplitCache.set(e,t)),t}freshLocal(){return new C().defineOwned("patter",this.host.patterLocalDecls)}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;if(!this.sceneBags.has(e.id)){let r={};for(let s of e.sceneProps??[]){let i=s.name.toLowerCase();t.has(i)||(r[i]=q(s))}this.sceneBags.set(e.id,r)}if(!this.host.stageBags.has(e.id)){let r={};for(let s of e.sceneProps??[]){let i=s.name.toLowerCase();t.has(i)&&(r[i]=q(s))}this.host.stageBags.set(e.id,r)}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);i&&(i[s]=q(r))}}};function oe(n,e){x(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 pe(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 de(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 ae(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Ne(n){if(n.default!==void 0)return n.default;switch(n.type){case"number":return 0;case"string":return"";case"flags":return[];case"enum":return n.values?.[0]??"";case"quality":return n.stages?.[0]??"";default:return!1}}function ce(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function Ae(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"flags":return[];case"enum":return n.values?.[0]??"";case"quality":return n.stages?.[0]??""}}function Fe(n){let e=r=>r.toLowerCase(),t=new Map;for(let r of n)t.set(e(r.name),Ae(r));return{get:r=>t.get(e(r)),set:(r,s)=>{t.set(e(r),s)}}}function q(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"flags":return[];case"enum":return n.values?.[0]??"";case"quality":return n.stages?.[0]??""}}function le(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var Ge=(n,e)=>n.shared??e;function H(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:Ge(n,e)}}function Te(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function Ve(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 ue(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=f(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:f(c),name:c.name}))});for(let c of o.blocks)Ve(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>H(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=>H(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(Te)}));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=>H(o,!0)),scene:r},gameData:i,counts:e}}function ge(n,e){return n.gameDataFields?.[e]??[]}function U(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function fe(n,e){let t={};for(let r of n){let s=U(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 xe(Me);})();\n//# sourceMappingURL=patterplay.min.js.map';
|
|
194270
194785
|
|
|
194271
194786
|
// ../ops/src/export-html.ts
|
|
194272
194787
|
var esc = (s) => s.replace(/[&<>]/g, (c2) => ({ "&": "&", "<": "<", ">": ">" })[c2]);
|
|
@@ -194713,7 +195228,29 @@ var ScopeRegistry = class {
|
|
|
194713
195228
|
for (const [token2, e] of this.scopes) {
|
|
194714
195229
|
scopes[token2] = e.kind === "owned" ? e.bag.values : e.resolver;
|
|
194715
195230
|
}
|
|
194716
|
-
|
|
195231
|
+
const qualities = this.qualityLadders();
|
|
195232
|
+
return qualities.size === 0 ? { scopes, host: host2 } : {
|
|
195233
|
+
scopes,
|
|
195234
|
+
host: host2,
|
|
195235
|
+
qualities: (scope, name) => qualities.get(scope)?.get(name.toLowerCase())
|
|
195236
|
+
};
|
|
195237
|
+
}
|
|
195238
|
+
/** Every quality declaration's ladder, keyed scope token then name. */
|
|
195239
|
+
qualityLadders() {
|
|
195240
|
+
const out = /* @__PURE__ */ new Map();
|
|
195241
|
+
for (const [token2, e] of this.scopes) {
|
|
195242
|
+
const decls = e.kind === "owned" ? e.bag.declarations() : [...e.decls.values()];
|
|
195243
|
+
for (const d of decls) {
|
|
195244
|
+
if (d.type !== "quality" || d.stages === void 0) continue;
|
|
195245
|
+
let m = out.get(token2);
|
|
195246
|
+
if (!m) {
|
|
195247
|
+
m = /* @__PURE__ */ new Map();
|
|
195248
|
+
out.set(token2, m);
|
|
195249
|
+
}
|
|
195250
|
+
m.set(d.name.toLowerCase(), d.stages);
|
|
195251
|
+
}
|
|
195252
|
+
}
|
|
195253
|
+
return out;
|
|
194717
195254
|
}
|
|
194718
195255
|
/**
|
|
194719
195256
|
* Build the `ExpressionSchema` expr's validator consumes. Scopes with no
|
|
@@ -194726,7 +195263,11 @@ var ScopeRegistry = class {
|
|
|
194726
195263
|
const decls = e.kind === "owned" ? e.bag.declarations() : [...e.decls.values()];
|
|
194727
195264
|
if (decls.length === 0) continue;
|
|
194728
195265
|
const m = /* @__PURE__ */ new Map();
|
|
194729
|
-
for (const d of decls) m.set(d.name.toLowerCase(), {
|
|
195266
|
+
for (const d of decls) m.set(d.name.toLowerCase(), {
|
|
195267
|
+
type: d.type,
|
|
195268
|
+
enumValues: d.values,
|
|
195269
|
+
...d.stages !== void 0 ? { stages: d.stages } : {}
|
|
195270
|
+
});
|
|
194730
195271
|
properties.set(token2, m);
|
|
194731
195272
|
}
|
|
194732
195273
|
return { properties };
|
|
@@ -194778,6 +195319,9 @@ function defaultFor(d) {
|
|
|
194778
195319
|
return d.values?.[0] ?? "";
|
|
194779
195320
|
case "flags":
|
|
194780
195321
|
return [];
|
|
195322
|
+
// A quality starts at the first rung of its ladder.
|
|
195323
|
+
case "quality":
|
|
195324
|
+
return d.stages?.[0] ?? "";
|
|
194781
195325
|
}
|
|
194782
195326
|
}
|
|
194783
195327
|
|
|
@@ -195269,6 +195813,7 @@ var Engine = class _Engine {
|
|
|
195269
195813
|
ref: `@${d.name}`,
|
|
195270
195814
|
type: d.type,
|
|
195271
195815
|
values: d.values,
|
|
195816
|
+
stages: d.stages,
|
|
195272
195817
|
value: this.getProperty(`@${d.name}`),
|
|
195273
195818
|
default: declDefault(d)
|
|
195274
195819
|
}));
|
|
@@ -195401,8 +195946,27 @@ var Flow = class {
|
|
|
195401
195946
|
nextRandom: this.rng,
|
|
195402
195947
|
visits: (id2) => this.visitCounts.get(id2) ?? 0,
|
|
195403
195948
|
patterVisits: (id2) => this.host.sharedVisits.get(id2) ?? 0
|
|
195404
|
-
}
|
|
195405
|
-
|
|
195949
|
+
},
|
|
195950
|
+
// The quality channel (expr 0.4.0): hands the evaluator a property's stage ladder, which is what
|
|
195951
|
+
// makes ordering compare by position and advance() step. Wired by hand because this context takes
|
|
195952
|
+
// only the registry's SCOPES (the patter/scene resolvers here are the flow's own merged views),
|
|
195953
|
+
// and because @scene declarations belong to whichever scene the flow is in RIGHT NOW.
|
|
195954
|
+
qualities: (scope, name) => this.stagesFor(scope, name)
|
|
195955
|
+
};
|
|
195956
|
+
}
|
|
195957
|
+
/** The stage ladder of `@scope.name` when it is a declared quality, else undefined. Names compare
|
|
195958
|
+
* lowercase, as the compiler emits references (the selfBackedResolver lesson). */
|
|
195959
|
+
stagesFor(scope, name) {
|
|
195960
|
+
const key2 = name.toLowerCase();
|
|
195961
|
+
const fromDecls = (decls) => decls?.find((d) => d.name.toLowerCase() === key2 && d.type === "quality")?.stages;
|
|
195962
|
+
if (scope === "patter") {
|
|
195963
|
+
return fromDecls(this.host.patterSharedDecls) ?? fromDecls(this.host.patterLocalDecls);
|
|
195964
|
+
}
|
|
195965
|
+
if (scope === "scene") {
|
|
195966
|
+
const scene = this.currentSceneId != null ? this.host.bundle.scenes[this.currentSceneId] : void 0;
|
|
195967
|
+
return fromDecls(scene?.sceneProps);
|
|
195968
|
+
}
|
|
195969
|
+
return fromDecls(this.host.bundle.scopeRegistry?.scopes.find((s) => s.token === scope)?.declarations);
|
|
195406
195970
|
}
|
|
195407
195971
|
// -- Host API -------------------------------------------------------------
|
|
195408
195972
|
/** Begin this flow at a scene (and optionally a specific block within it). */
|
|
@@ -196165,7 +196729,7 @@ function deserialiseSelectors(rec) {
|
|
|
196165
196729
|
return map2;
|
|
196166
196730
|
}
|
|
196167
196731
|
function toDecl(decl) {
|
|
196168
|
-
return { name: decl.name, type: decl.type, values: decl.values, default: decl.default };
|
|
196732
|
+
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default };
|
|
196169
196733
|
}
|
|
196170
196734
|
function declDefault(d) {
|
|
196171
196735
|
if (d.default !== void 0) return d.default;
|
|
@@ -196178,12 +196742,14 @@ function declDefault(d) {
|
|
|
196178
196742
|
return [];
|
|
196179
196743
|
case "enum":
|
|
196180
196744
|
return d.values?.[0] ?? "";
|
|
196745
|
+
case "quality":
|
|
196746
|
+
return d.stages?.[0] ?? "";
|
|
196181
196747
|
default:
|
|
196182
196748
|
return false;
|
|
196183
196749
|
}
|
|
196184
196750
|
}
|
|
196185
196751
|
function toForeignDecl(decl) {
|
|
196186
|
-
return { name: decl.name, type: decl.type, values: decl.values, default: decl.default, writable: decl.writable };
|
|
196752
|
+
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default, writable: decl.writable };
|
|
196187
196753
|
}
|
|
196188
196754
|
function hostScopeDefault(decl) {
|
|
196189
196755
|
if (decl.default !== void 0) return decl.default;
|
|
@@ -196198,6 +196764,8 @@ function hostScopeDefault(decl) {
|
|
|
196198
196764
|
return [];
|
|
196199
196765
|
case "enum":
|
|
196200
196766
|
return decl.values?.[0] ?? "";
|
|
196767
|
+
case "quality":
|
|
196768
|
+
return decl.stages?.[0] ?? "";
|
|
196201
196769
|
}
|
|
196202
196770
|
}
|
|
196203
196771
|
function selfBackedResolver(decls) {
|
|
@@ -196224,6 +196792,8 @@ function sceneDefault(decl) {
|
|
|
196224
196792
|
return [];
|
|
196225
196793
|
case "enum":
|
|
196226
196794
|
return decl.values?.[0] ?? "";
|
|
196795
|
+
case "quality":
|
|
196796
|
+
return decl.stages?.[0] ?? "";
|
|
196227
196797
|
}
|
|
196228
196798
|
}
|
|
196229
196799
|
function truthy(v) {
|
|
@@ -196259,7 +196829,7 @@ function stringsByLocale(loaded) {
|
|
|
196259
196829
|
}
|
|
196260
196830
|
return byLocale;
|
|
196261
196831
|
}
|
|
196262
|
-
function
|
|
196832
|
+
function mergeAuthoring2(loaded) {
|
|
196263
196833
|
const writing = /* @__PURE__ */ new Map();
|
|
196264
196834
|
const recording = /* @__PURE__ */ new Map();
|
|
196265
196835
|
const cut = /* @__PURE__ */ new Set();
|
|
@@ -196431,7 +197001,7 @@ function proposeCoverageDrivers(loaded) {
|
|
|
196431
197001
|
const { written, proposals } = analyzeHostScopes(bundle, hostTokens);
|
|
196432
197002
|
const declByRef = /* @__PURE__ */ new Map();
|
|
196433
197003
|
for (const s of loaded.project.scopeRegistry?.scopes ?? []) {
|
|
196434
|
-
for (const d of s.declarations ?? []) declByRef.set(`@${s.token}.${d.name}`, { type: d.type, values: d.values });
|
|
197004
|
+
for (const d of s.declarations ?? []) declByRef.set(`@${s.token}.${d.name}`, { type: d.type, values: d.values, stages: d.stages });
|
|
196435
197005
|
}
|
|
196436
197006
|
const drivers = [];
|
|
196437
197007
|
for (const [ref, pool] of proposals) {
|
|
@@ -196441,6 +197011,7 @@ function proposeCoverageDrivers(loaded) {
|
|
|
196441
197011
|
if (values.length === 0 && decl) {
|
|
196442
197012
|
if (decl.type === "boolean") values = [true, false];
|
|
196443
197013
|
else if (decl.type === "enum" && decl.values) values = [...decl.values];
|
|
197014
|
+
else if (decl.type === "quality" && decl.stages) values = [...decl.stages];
|
|
196444
197015
|
}
|
|
196445
197016
|
if (values.length === 0) continue;
|
|
196446
197017
|
drivers.push({ ref, kind: "recurring", cadence: "sometimes", values: sortValues(values) });
|
|
@@ -196677,7 +197248,7 @@ function runReport(loaded, recordingOverride) {
|
|
|
196677
197248
|
const thresholdIdx = estimatingOn ? writingIndex.get(est?.thresholdStatus ?? "") ?? 0 : -1;
|
|
196678
197249
|
const defaultLines = est?.defaultLines ?? 0;
|
|
196679
197250
|
const tagMap = new Map((est?.tagEstimates ?? []).map((t) => [t.tag, t.lines]));
|
|
196680
|
-
const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, rerecord: rerecordSet, edits: editsOf } =
|
|
197251
|
+
const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, rerecord: rerecordSet, edits: editsOf } = mergeAuthoring2(loaded);
|
|
196681
197252
|
const recordingBase = recordingOverride ?? manualRecordingOf;
|
|
196682
197253
|
const recordingOf = (id) => effectiveRecording(id, recordingBase, rerecordSet, recordingLadder[0]);
|
|
196683
197254
|
const byLocale = stringsByLocale(loaded);
|
|
@@ -197034,7 +197605,7 @@ function classesForChannel(classes, channel) {
|
|
|
197034
197605
|
function resolveDocumentation(loaded, channel) {
|
|
197035
197606
|
const classes = loaded.project.documentationClasses ?? DEFAULT_DOCUMENTATION_CLASSES;
|
|
197036
197607
|
const allowed = classesForChannel(classes, channel);
|
|
197037
|
-
const docsOf =
|
|
197608
|
+
const docsOf = mergeAuthoring2(loaded).documentation;
|
|
197038
197609
|
const own = (id) => (docsOf.get(id) ?? []).filter((l2) => l2.type !== void 0 && allowed.has(l2.type));
|
|
197039
197610
|
const out = /* @__PURE__ */ new Map();
|
|
197040
197611
|
const visit = (id, inherited) => {
|
|
@@ -197070,7 +197641,7 @@ function extractLoc(loaded, opts = {}) {
|
|
|
197070
197641
|
const targetLocale = isTemplate ? void 0 : opts.locale;
|
|
197071
197642
|
const source2 = tableFor(loaded, defaultLocale);
|
|
197072
197643
|
const target = targetLocale ? tableFor(loaded, targetLocale) : {};
|
|
197073
|
-
const edits =
|
|
197644
|
+
const edits = mergeAuthoring2(loaded).edits;
|
|
197074
197645
|
const docs = resolveDocumentation(loaded, "loc");
|
|
197075
197646
|
const commentsOf = (id) => (docs.get(id) ?? []).map((d) => d.text);
|
|
197076
197647
|
const staleFor = (id, translation) => {
|
|
@@ -197410,7 +197981,7 @@ function runVoiceScript(loaded, opts = {}) {
|
|
|
197410
197981
|
const stub = writingLadder[0];
|
|
197411
197982
|
const recordThreshold = ladderDecls.findIndex((s) => s.readyToRecord);
|
|
197412
197983
|
const writingIndex = new Map(writingLadder.map((name, i) => [name, i]));
|
|
197413
|
-
const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, rerecord: rerecordSet, documentation: docsOf } =
|
|
197984
|
+
const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, rerecord: rerecordSet, documentation: docsOf } = mergeAuthoring2(loaded);
|
|
197414
197985
|
const recordingBase = opts.recordingOverride ?? manualRecordingOf;
|
|
197415
197986
|
const recordingOf = (id) => effectiveRecording(id, recordingBase, rerecordSet, recordingLadder[0]);
|
|
197416
197987
|
const source2 = sourceStrings(loaded);
|
|
@@ -197606,7 +198177,7 @@ function sequenceLabel(node) {
|
|
|
197606
198177
|
function runScriptDoc(loaded) {
|
|
197607
198178
|
const { project } = loaded;
|
|
197608
198179
|
const source2 = sourceStrings(loaded);
|
|
197609
|
-
const { cut } =
|
|
198180
|
+
const { cut } = mergeAuthoring2(loaded);
|
|
197610
198181
|
const sceneOf = /* @__PURE__ */ new Map();
|
|
197611
198182
|
const blockTrail = /* @__PURE__ */ new Map();
|
|
197612
198183
|
const blockName = /* @__PURE__ */ new Map();
|
|
@@ -224067,11 +224638,11 @@ var $94d7a73bd2edfc9a$export$2e2bcd8739ae039 = class {
|
|
|
224067
224638
|
* Ignores features that have already been applied.
|
|
224068
224639
|
*/
|
|
224069
224640
|
_addFeatures(features, global3) {
|
|
224070
|
-
let
|
|
224071
|
-
let stage = this.stages[
|
|
224641
|
+
let stageIndex2 = this.stages.length - 1;
|
|
224642
|
+
let stage = this.stages[stageIndex2];
|
|
224072
224643
|
for (let feature of features) if (this.allFeatures[feature] == null) {
|
|
224073
224644
|
stage.push(feature);
|
|
224074
|
-
this.allFeatures[feature] =
|
|
224645
|
+
this.allFeatures[feature] = stageIndex2;
|
|
224075
224646
|
if (global3) this.globalFeatures[feature] = true;
|
|
224076
224647
|
}
|
|
224077
224648
|
}
|
|
@@ -237967,6 +238538,13 @@ async function runPack(startPath) {
|
|
|
237967
238538
|
const projectFile = findProjectFile(startPath);
|
|
237968
238539
|
const root2 = dirname4(projectFile);
|
|
237969
238540
|
const project = parseSource(readFileSync4(projectFile, "utf8"));
|
|
238541
|
+
const unresolved = sidecarIssues(walkFiles(root2, CONFLICT_SIDECAR));
|
|
238542
|
+
if (unresolved.length) {
|
|
238543
|
+
throw new Error(
|
|
238544
|
+
`${unresolved.length} unresolved merge conflict(s) - resolve them before packing, or the recipient gets your side of a disagreement with no sign of it:
|
|
238545
|
+
` + unresolved.map((i) => ` ${i.file}`).join("\n")
|
|
238546
|
+
);
|
|
238547
|
+
}
|
|
237970
238548
|
const files = SHARD_EXTENSIONS.flatMap((ext) => walkFiles(root2, ext)).map((abs) => ({ abs, rel: relative(root2, abs).split(sep2).join("/") })).sort((a, b) => a.rel.localeCompare(b.rel));
|
|
237971
238549
|
const manifest = {
|
|
237972
238550
|
schema: "patter/document@0",
|
|
@@ -237983,325 +238561,6 @@ async function runPack(startPath) {
|
|
|
237983
238561
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
237984
238562
|
import { join as join5, normalize, isAbsolute as isAbsolute2, resolve as resolve4, sep as sep3 } from "path";
|
|
237985
238563
|
import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
|
|
237986
|
-
|
|
237987
|
-
// ../ops/src/merge.ts
|
|
237988
|
-
var UnsupportedMergeError = class extends Error {
|
|
237989
|
-
};
|
|
237990
|
-
var eq = (a, b) => canonicalStringify(a) === canonicalStringify(b);
|
|
237991
|
-
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
237992
|
-
function detectMergeType(file) {
|
|
237993
|
-
const s = typeof file.schema === "string" ? file.schema : "";
|
|
237994
|
-
if (s.startsWith("patter/flow")) return "flow";
|
|
237995
|
-
if (s.startsWith("patter/strings")) return "loc";
|
|
237996
|
-
if (s.startsWith("patter/authoring")) return "authoring";
|
|
237997
|
-
if (s.startsWith("patter/project")) return "project";
|
|
237998
|
-
throw new UnsupportedMergeError(`cannot detect a Patter merge type from schema '${s}'`);
|
|
237999
|
-
}
|
|
238000
|
-
function runMerge(base, ours, theirs, opts) {
|
|
238001
|
-
const type = opts?.type ?? detectMergeType(ours);
|
|
238002
|
-
switch (type) {
|
|
238003
|
-
case "loc":
|
|
238004
|
-
return mergeLoc(base, ours, theirs);
|
|
238005
|
-
case "authoring":
|
|
238006
|
-
return mergeAuthoring2(base, ours, theirs);
|
|
238007
|
-
case "flow":
|
|
238008
|
-
return mergeFlow(base, ours, theirs);
|
|
238009
|
-
case "project":
|
|
238010
|
-
return mergeProject(base, ours, theirs);
|
|
238011
|
-
}
|
|
238012
|
-
}
|
|
238013
|
-
function deletedKind(base, ours, theirs) {
|
|
238014
|
-
const deleted = base !== void 0 && (ours === void 0 || theirs === void 0);
|
|
238015
|
-
return deleted ? "delete-vs-edit" : "both-changed";
|
|
238016
|
-
}
|
|
238017
|
-
function merge3(base, ours, theirs, id, path, conflicts) {
|
|
238018
|
-
if (eq(ours, theirs)) return ours;
|
|
238019
|
-
if (eq(base, ours)) return theirs;
|
|
238020
|
-
if (eq(base, theirs)) return ours;
|
|
238021
|
-
conflicts.push({ id, path, base, ours, theirs, kind: deletedKind(base, ours, theirs) });
|
|
238022
|
-
return ours;
|
|
238023
|
-
}
|
|
238024
|
-
function mergeMap(base, ours, theirs, prefix, conflicts, resolve6) {
|
|
238025
|
-
const out = {};
|
|
238026
|
-
for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
|
|
238027
|
-
const b = base[k], o = ours[k], t = theirs[k];
|
|
238028
|
-
let v;
|
|
238029
|
-
if (eq(o, t)) v = o;
|
|
238030
|
-
else if (eq(b, o)) v = t;
|
|
238031
|
-
else if (eq(b, t)) v = o;
|
|
238032
|
-
else {
|
|
238033
|
-
const r = resolve6?.(k, b, o, t);
|
|
238034
|
-
if (r && r.resolved) v = r.value;
|
|
238035
|
-
else {
|
|
238036
|
-
conflicts.push({ id: k, path: `${prefix}.${k}`, base: b, ours: o, theirs: t, kind: r ? r.kind : deletedKind(b, o, t) });
|
|
238037
|
-
v = o;
|
|
238038
|
-
}
|
|
238039
|
-
}
|
|
238040
|
-
if (v !== void 0) out[k] = v;
|
|
238041
|
-
}
|
|
238042
|
-
return out;
|
|
238043
|
-
}
|
|
238044
|
-
var asMap = (v) => isObj(v) ? v : {};
|
|
238045
|
-
var asArr = (v) => Array.isArray(v) ? v : [];
|
|
238046
|
-
function setIf(obj2, key2, val) {
|
|
238047
|
-
if (Array.isArray(val) ? val.length > 0 : Object.keys(val).length > 0) obj2[key2] = val;
|
|
238048
|
-
}
|
|
238049
|
-
function mergeLoc(base, ours, theirs) {
|
|
238050
|
-
const conflicts = [];
|
|
238051
|
-
const merged = {};
|
|
238052
|
-
for (const f of ["schema", "scene", "locale", "default"]) {
|
|
238053
|
-
const v = merge3(base[f], ours[f], theirs[f], "", f, conflicts);
|
|
238054
|
-
if (v !== void 0) merged[f] = v;
|
|
238055
|
-
}
|
|
238056
|
-
merged.strings = mergeMap(asMap(base.strings), asMap(ours.strings), asMap(theirs.strings), "strings", conflicts);
|
|
238057
|
-
return { type: "loc", merged, conflicts, warnings: [] };
|
|
238058
|
-
}
|
|
238059
|
-
function mergeAuthoring2(base, ours, theirs) {
|
|
238060
|
-
const conflicts = [];
|
|
238061
|
-
const merged = {};
|
|
238062
|
-
merged.schema = merge3(base.schema, ours.schema, theirs.schema, "", "schema", conflicts) ?? ours.schema;
|
|
238063
|
-
setIf(merged, "comments", mergeComments(asArr(base.comments), asArr(ours.comments), asArr(theirs.comments)));
|
|
238064
|
-
const edits = mergeEdits(asMap(base.edits), asMap(ours.edits), asMap(theirs.edits));
|
|
238065
|
-
setIf(merged, "edits", edits);
|
|
238066
|
-
const lww = lastWriterWins(asMap(ours.edits), asMap(theirs.edits));
|
|
238067
|
-
for (const f of ["writing", "recording", "audio"]) {
|
|
238068
|
-
setIf(merged, f, mergeMap(asMap(base[f]), asMap(ours[f]), asMap(theirs[f]), f, conflicts, lww));
|
|
238069
|
-
}
|
|
238070
|
-
setIf(merged, "documentation", mergeMap(asMap(base.documentation), asMap(ours.documentation), asMap(theirs.documentation), "documentation", conflicts));
|
|
238071
|
-
setIf(merged, "cut", mergeMap(asMap(base.cut), asMap(ours.cut), asMap(theirs.cut), "cut", conflicts, void 0));
|
|
238072
|
-
return { type: "authoring", merged, conflicts, warnings: [] };
|
|
238073
|
-
}
|
|
238074
|
-
function mergeComments(base, ours, theirs) {
|
|
238075
|
-
const byId = /* @__PURE__ */ new Map();
|
|
238076
|
-
for (const c2 of [...base, ...ours, ...theirs]) {
|
|
238077
|
-
if (isObj(c2) && typeof c2.id === "string" && !byId.has(c2.id)) byId.set(c2.id, c2);
|
|
238078
|
-
}
|
|
238079
|
-
return [...byId.values()].sort((a, b) => String(a.ts ?? "").localeCompare(String(b.ts ?? "")));
|
|
238080
|
-
}
|
|
238081
|
-
function lastWriterWins(oursEdits, theirsEdits) {
|
|
238082
|
-
return (id, _b, o, t) => {
|
|
238083
|
-
const om = asMap(oursEdits[id]).modifiedAt, tm = asMap(theirsEdits[id]).modifiedAt;
|
|
238084
|
-
if (typeof om === "string" && typeof tm === "string") return { resolved: true, value: om >= tm ? o : t };
|
|
238085
|
-
return { resolved: false, kind: "no-timestamp" };
|
|
238086
|
-
};
|
|
238087
|
-
}
|
|
238088
|
-
function mergeEdits(base, ours, theirs) {
|
|
238089
|
-
const out = {};
|
|
238090
|
-
for (const id of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
|
|
238091
|
-
const o = ours[id], t = theirs[id];
|
|
238092
|
-
if (eq(o, t)) {
|
|
238093
|
-
if (o !== void 0) out[id] = o;
|
|
238094
|
-
continue;
|
|
238095
|
-
}
|
|
238096
|
-
if (o === void 0) {
|
|
238097
|
-
if (t !== void 0) out[id] = t;
|
|
238098
|
-
continue;
|
|
238099
|
-
}
|
|
238100
|
-
if (t === void 0) {
|
|
238101
|
-
out[id] = o;
|
|
238102
|
-
continue;
|
|
238103
|
-
}
|
|
238104
|
-
out[id] = mergeEditRecord(asMap(o), asMap(t));
|
|
238105
|
-
}
|
|
238106
|
-
return out;
|
|
238107
|
-
}
|
|
238108
|
-
function mergeEditRecord(o, t) {
|
|
238109
|
-
const om = typeof o.modifiedAt === "string" ? o.modifiedAt : "";
|
|
238110
|
-
const tm = typeof t.modifiedAt === "string" ? t.modifiedAt : "";
|
|
238111
|
-
const newer = om >= tm ? o : t;
|
|
238112
|
-
const ol = asMap(o.localisedAt), tl = asMap(t.localisedAt);
|
|
238113
|
-
const loc = {};
|
|
238114
|
-
for (const k of /* @__PURE__ */ new Set([...Object.keys(ol), ...Object.keys(tl)])) {
|
|
238115
|
-
const a = typeof ol[k] === "string" ? ol[k] : "";
|
|
238116
|
-
const b = typeof tl[k] === "string" ? tl[k] : "";
|
|
238117
|
-
loc[k] = a >= b ? ol[k] ?? tl[k] : tl[k] ?? ol[k];
|
|
238118
|
-
}
|
|
238119
|
-
const merged = { ...newer };
|
|
238120
|
-
if (Object.keys(loc).length > 0) merged.localisedAt = loc;
|
|
238121
|
-
return merged;
|
|
238122
|
-
}
|
|
238123
|
-
function mergeKeyedByName(base, ours, theirs, path, conflicts) {
|
|
238124
|
-
const byName = (arr) => {
|
|
238125
|
-
const m = {};
|
|
238126
|
-
for (const p of arr) if (isObj(p) && typeof p.name === "string") m[p.name] = p;
|
|
238127
|
-
return m;
|
|
238128
|
-
};
|
|
238129
|
-
const merged = mergeMap(byName(base), byName(ours), byName(theirs), path, conflicts);
|
|
238130
|
-
return Object.keys(merged).sort().map((name) => merged[name]);
|
|
238131
|
-
}
|
|
238132
|
-
function mergeProject(base, ours, theirs) {
|
|
238133
|
-
const conflicts = [];
|
|
238134
|
-
const merged = {};
|
|
238135
|
-
for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
|
|
238136
|
-
if (k === "properties" || k === "cast") {
|
|
238137
|
-
const v = mergeKeyedByName(asArr(base[k]), asArr(ours[k]), asArr(theirs[k]), k, conflicts);
|
|
238138
|
-
if (v.length > 0) merged[k] = v;
|
|
238139
|
-
} else if (k === "gameDataFields") {
|
|
238140
|
-
const v = mergeGameDataFields(asMap(base[k]), asMap(ours[k]), asMap(theirs[k]), conflicts);
|
|
238141
|
-
if (Object.keys(v).length > 0) merged[k] = v;
|
|
238142
|
-
} else if (k === "locales") {
|
|
238143
|
-
merged.locales = mergeLocales(asMap(base.locales), asMap(ours.locales), asMap(theirs.locales), conflicts);
|
|
238144
|
-
} else {
|
|
238145
|
-
const v = merge3(base[k], ours[k], theirs[k], "", k, conflicts);
|
|
238146
|
-
if (v !== void 0) merged[k] = v;
|
|
238147
|
-
}
|
|
238148
|
-
}
|
|
238149
|
-
return { type: "project", merged, conflicts, warnings: [] };
|
|
238150
|
-
}
|
|
238151
|
-
function mergeGameDataFields(b, o, t, conflicts) {
|
|
238152
|
-
const out = {};
|
|
238153
|
-
for (const kind of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
|
|
238154
|
-
const v = mergeKeyedByName(asArr(b[kind]), asArr(o[kind]), asArr(t[kind]), `gameDataFields.${kind}`, conflicts);
|
|
238155
|
-
if (v.length > 0) out[kind] = v;
|
|
238156
|
-
}
|
|
238157
|
-
return out;
|
|
238158
|
-
}
|
|
238159
|
-
function mergeLocales(b, o, t, conflicts) {
|
|
238160
|
-
const out = {};
|
|
238161
|
-
const def = merge3(b.default, o.default, t.default, "", "locales.default", conflicts);
|
|
238162
|
-
if (def !== void 0) out.default = def;
|
|
238163
|
-
const all = [];
|
|
238164
|
-
for (const arr of [asArr(b.all), asArr(o.all), asArr(t.all)]) {
|
|
238165
|
-
for (const x of arr) if (typeof x === "string" && !all.includes(x)) all.push(x);
|
|
238166
|
-
}
|
|
238167
|
-
out.all = all;
|
|
238168
|
-
return out;
|
|
238169
|
-
}
|
|
238170
|
-
var CHILD_KEYS = ["blocks", "children", "beats"];
|
|
238171
|
-
function toTree(raw) {
|
|
238172
|
-
const childKey = CHILD_KEYS.find((k) => Array.isArray(raw[k]));
|
|
238173
|
-
const fields = {};
|
|
238174
|
-
for (const [k, v] of Object.entries(raw)) if (k !== childKey) fields[k] = v;
|
|
238175
|
-
const children = childKey ? raw[childKey].filter(isObj).map(toTree) : [];
|
|
238176
|
-
return { id: typeof raw.id === "string" ? raw.id : "", fields, childKey, children };
|
|
238177
|
-
}
|
|
238178
|
-
function fromTree(n) {
|
|
238179
|
-
const out = { ...n.fields };
|
|
238180
|
-
if (n.childKey) out[n.childKey] = n.children.map(fromTree);
|
|
238181
|
-
return out;
|
|
238182
|
-
}
|
|
238183
|
-
function mergeFlow(base, ours, theirs) {
|
|
238184
|
-
const conflicts = [];
|
|
238185
|
-
const warnings = [];
|
|
238186
|
-
const merged = {};
|
|
238187
|
-
merged.schema = merge3(base.schema, ours.schema, theirs.schema, "", "schema", conflicts) ?? ours.schema;
|
|
238188
|
-
const bScene = isObj(base.scene) ? toTree(base.scene) : void 0;
|
|
238189
|
-
const oScene = toTree(asMap(ours.scene));
|
|
238190
|
-
const tScene = isObj(theirs.scene) ? toTree(theirs.scene) : void 0;
|
|
238191
|
-
const mergedScene = mergeNode(bScene, oScene, tScene, "scene", conflicts, warnings);
|
|
238192
|
-
merged.scene = fromTree(mergedScene);
|
|
238193
|
-
checkDuplicateIds(mergedScene, conflicts);
|
|
238194
|
-
return { type: "flow", merged, conflicts, warnings };
|
|
238195
|
-
}
|
|
238196
|
-
function mergeNode(b, o, t, path, conflicts, warnings) {
|
|
238197
|
-
const fields = mergeFields(b?.fields ?? {}, o.fields, t?.fields ?? {}, o.id, path, conflicts);
|
|
238198
|
-
const childKey = o.childKey ?? t?.childKey ?? b?.childKey;
|
|
238199
|
-
const children = childKey ? mergeChildren(b?.children ?? [], o.children, t?.children ?? [], `${path}.${childKey}`, conflicts, warnings) : [];
|
|
238200
|
-
return { id: o.id, fields, childKey, children };
|
|
238201
|
-
}
|
|
238202
|
-
function mergeFields(b, o, t, id, path, conflicts) {
|
|
238203
|
-
const out = {};
|
|
238204
|
-
for (const k of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
|
|
238205
|
-
const v = merge3(b[k], o[k], t[k], id, `${path}.${k}`, conflicts);
|
|
238206
|
-
if (v !== void 0) out[k] = v;
|
|
238207
|
-
}
|
|
238208
|
-
return out;
|
|
238209
|
-
}
|
|
238210
|
-
function mergeChildren(B, O, T, path, conflicts, warnings) {
|
|
238211
|
-
const Bm = idMap(B), Om = idMap(O), Tm = idMap(T);
|
|
238212
|
-
const Bset = new Set(Bm.keys()), Oset = new Set(Om.keys()), Tset = new Set(Tm.keys());
|
|
238213
|
-
const merged = /* @__PURE__ */ new Map();
|
|
238214
|
-
const here = (id) => `${path}[${id}]`;
|
|
238215
|
-
for (const id of /* @__PURE__ */ new Set([...Bm.keys(), ...Om.keys(), ...Tm.keys()])) {
|
|
238216
|
-
const b = Bm.get(id), o = Om.get(id), t = Tm.get(id);
|
|
238217
|
-
const inB = Bset.has(id), inO = Oset.has(id), inT = Tset.has(id);
|
|
238218
|
-
if (inO && inT) {
|
|
238219
|
-
if (!inB && !eq(fromTree(o), fromTree(t))) {
|
|
238220
|
-
conflicts.push({ id, path: here(id), base: void 0, ours: fromTree(o), theirs: fromTree(t), kind: "added-both" });
|
|
238221
|
-
merged.set(id, o);
|
|
238222
|
-
} else {
|
|
238223
|
-
merged.set(id, mergeNode(b, o, t, here(id), conflicts, warnings));
|
|
238224
|
-
}
|
|
238225
|
-
} else if (inO && !inT) {
|
|
238226
|
-
if (inB && !eq(fromTree(o), fromTree(b))) {
|
|
238227
|
-
conflicts.push({ id, path: here(id), base: fromTree(b), ours: fromTree(o), theirs: void 0, kind: "delete-vs-edit" });
|
|
238228
|
-
merged.set(id, o);
|
|
238229
|
-
} else if (!inB) {
|
|
238230
|
-
merged.set(id, o);
|
|
238231
|
-
}
|
|
238232
|
-
} else if (!inO && inT) {
|
|
238233
|
-
if (inB && !eq(fromTree(t), fromTree(b))) {
|
|
238234
|
-
conflicts.push({ id, path: here(id), base: fromTree(b), ours: void 0, theirs: fromTree(t), kind: "delete-vs-edit" });
|
|
238235
|
-
} else if (!inB) {
|
|
238236
|
-
merged.set(id, t);
|
|
238237
|
-
}
|
|
238238
|
-
}
|
|
238239
|
-
}
|
|
238240
|
-
return orderChildren(merged, B, O, T, path, conflicts, warnings);
|
|
238241
|
-
}
|
|
238242
|
-
function orderChildren(merged, B, O, T, path, conflicts, warnings) {
|
|
238243
|
-
const Bids = B.map((n) => n.id), Oids = O.map((n) => n.id), Tids = T.map((n) => n.id);
|
|
238244
|
-
const Oidset = new Set(Oids);
|
|
238245
|
-
const common = new Set(Bids.filter((id) => merged.has(id)));
|
|
238246
|
-
const restrict = (ids) => ids.filter((id) => common.has(id));
|
|
238247
|
-
const relB = restrict(Bids), relO = restrict(Oids), relT = restrict(Tids);
|
|
238248
|
-
const fullO = relO.length === common.size, fullT = relT.length === common.size;
|
|
238249
|
-
let commonOrder;
|
|
238250
|
-
if (!fullO && !fullT) commonOrder = relB;
|
|
238251
|
-
else if (!fullO) commonOrder = relT;
|
|
238252
|
-
else if (!fullT) commonOrder = relO;
|
|
238253
|
-
else if (arrEq(relO, relB)) commonOrder = relT;
|
|
238254
|
-
else if (arrEq(relT, relB)) commonOrder = relO;
|
|
238255
|
-
else if (arrEq(relO, relT)) commonOrder = relO;
|
|
238256
|
-
else {
|
|
238257
|
-
conflicts.push({ id: "", path, base: relB, ours: relO, theirs: relT, kind: "moved" });
|
|
238258
|
-
commonOrder = relO;
|
|
238259
|
-
}
|
|
238260
|
-
const anchorIn = (ids, id) => {
|
|
238261
|
-
for (let i = ids.indexOf(id) - 1; i >= 0; i--) if (common.has(ids[i])) return ids[i];
|
|
238262
|
-
return null;
|
|
238263
|
-
};
|
|
238264
|
-
const byAnchor = /* @__PURE__ */ new Map();
|
|
238265
|
-
const group = (a) => {
|
|
238266
|
-
let g = byAnchor.get(a);
|
|
238267
|
-
if (!g) {
|
|
238268
|
-
g = { ours: [], theirs: [] };
|
|
238269
|
-
byAnchor.set(a, g);
|
|
238270
|
-
}
|
|
238271
|
-
return g;
|
|
238272
|
-
};
|
|
238273
|
-
for (const n of O) if (merged.has(n.id) && !common.has(n.id)) group(anchorIn(Oids, n.id)).ours.push(n.id);
|
|
238274
|
-
for (const n of T) if (merged.has(n.id) && !common.has(n.id) && !Oidset.has(n.id)) group(anchorIn(Tids, n.id)).theirs.push(n.id);
|
|
238275
|
-
const out = [];
|
|
238276
|
-
const emit = (anchor) => {
|
|
238277
|
-
const g = byAnchor.get(anchor);
|
|
238278
|
-
if (!g) return;
|
|
238279
|
-
if (g.ours.length > 0 && g.theirs.length > 0) warnings.push({ id: anchor ?? "", path, message: "both sides inserted here; kept ours-then-theirs - review the order" });
|
|
238280
|
-
out.push(...g.ours, ...g.theirs);
|
|
238281
|
-
};
|
|
238282
|
-
emit(null);
|
|
238283
|
-
for (const id of commonOrder) {
|
|
238284
|
-
out.push(id);
|
|
238285
|
-
emit(id);
|
|
238286
|
-
}
|
|
238287
|
-
return out.map((id) => merged.get(id));
|
|
238288
|
-
}
|
|
238289
|
-
function checkDuplicateIds(root2, conflicts) {
|
|
238290
|
-
const seen = /* @__PURE__ */ new Set(), dup = /* @__PURE__ */ new Set();
|
|
238291
|
-
const walk2 = (n) => {
|
|
238292
|
-
if (n.id) {
|
|
238293
|
-
if (seen.has(n.id)) dup.add(n.id);
|
|
238294
|
-
else seen.add(n.id);
|
|
238295
|
-
}
|
|
238296
|
-
n.children.forEach(walk2);
|
|
238297
|
-
};
|
|
238298
|
-
walk2(root2);
|
|
238299
|
-
for (const id of dup) conflicts.push({ id, path: "scene", base: void 0, ours: void 0, theirs: void 0, kind: "structural" });
|
|
238300
|
-
}
|
|
238301
|
-
var idMap = (nodes) => new Map(nodes.filter((n) => n.id).map((n) => [n.id, n]));
|
|
238302
|
-
var arrEq = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
|
|
238303
|
-
|
|
238304
|
-
// ../ops/src/unpack.ts
|
|
238305
238564
|
var MANIFEST = "patter.manifest.json";
|
|
238306
238565
|
var UnsafeEntryError = class extends Error {
|
|
238307
238566
|
};
|
|
@@ -238364,10 +238623,17 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
|
|
|
238364
238623
|
shards.push({ path: rel, added: true });
|
|
238365
238624
|
continue;
|
|
238366
238625
|
}
|
|
238367
|
-
const
|
|
238368
|
-
|
|
238626
|
+
const readSide = (side, text) => {
|
|
238627
|
+
try {
|
|
238628
|
+
return parseSource(text);
|
|
238629
|
+
} catch (e) {
|
|
238630
|
+
throw new Error(`${rel}: the ${side} copy is not readable Patter source - ${e instanceof Error ? e.message : String(e)}`);
|
|
238631
|
+
}
|
|
238632
|
+
};
|
|
238633
|
+
const oursObj = readSide("ours", readFileSync5(outPath, "utf8"));
|
|
238634
|
+
const theirsObj = readSide("theirs", theirText);
|
|
238369
238635
|
const baseText = base.get(rel);
|
|
238370
|
-
const baseObj = baseText !== void 0 ?
|
|
238636
|
+
const baseObj = baseText !== void 0 ? readSide("base", baseText) : {};
|
|
238371
238637
|
const result = runMerge(baseObj, oursObj, theirsObj);
|
|
238372
238638
|
writes.push({ path: outPath, content: canonicalStringify(result.merged) });
|
|
238373
238639
|
if (result.conflicts.length > 0) {
|
|
@@ -240491,14 +240757,15 @@ async function run(cmd, positionals, flags) {
|
|
|
240491
240757
|
}
|
|
240492
240758
|
case "validate": {
|
|
240493
240759
|
const loaded = loadProject(positionals[0] ?? ".");
|
|
240494
|
-
const { structural, conditions, interpolation, hygiene, staleBundles, unresolvedMerges, ok } = runValidate(loaded);
|
|
240760
|
+
const { structural, conditions, interpolation, hygiene, staleBundles, unresolvedMerges, orphans, ok } = runValidate(loaded);
|
|
240495
240761
|
for (const i of structural) console.error(` [${i.code}] ${i.message}`);
|
|
240496
240762
|
for (const i of conditions) console.error(` [${i.field}] ${i.nodeId}: ${i.message} (${i.src})`);
|
|
240497
240763
|
for (const i of interpolation) console.error(` [${i.field}] ${i.nodeId}: ${i.message} (${i.src})`);
|
|
240498
240764
|
for (const i of hygiene) console.error(` [hygiene] ${i.file}: ${i.message}`);
|
|
240499
240765
|
for (const i of staleBundles) console.error(` [stale-bundle] ${i.file}: ${i.message}`);
|
|
240500
240766
|
for (const i of unresolvedMerges) console.error(` [unresolved-merge] ${i.file}: ${i.message}`);
|
|
240501
|
-
const
|
|
240767
|
+
for (const i of orphans) console.error(` [not-in-project] ${i.file}: ${i.message}`);
|
|
240768
|
+
const count = structural.length + conditions.length + interpolation.length + hygiene.length + staleBundles.length + unresolvedMerges.length + orphans.length;
|
|
240502
240769
|
if (ok) console.log(`ok - ${loaded.scenes.length} scene(s), no issues`);
|
|
240503
240770
|
else console.error(`
|
|
240504
240771
|
${count} issue(s)`);
|