@patterkit/runtime 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/dist/index.cjs +122 -83
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +127 -14
- package/dist/index.d.ts +127 -14
- package/dist/index.js +122 -83
- package/dist/index.js.map +1 -1
- package/dist/patterplay.min.js +2 -2
- package/dist/patterplay.min.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -33,6 +33,55 @@ version number always means the same runtime behaviour. This package is versione
|
|
|
33
33
|
|
|
34
34
|
## [Unreleased]
|
|
35
35
|
|
|
36
|
+
## [0.9.0] - 2026-09-02
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **BREAKING: a property row's address is `path`, not `ref`, and the row carries `name` and
|
|
41
|
+
`writable`.** `listProperties()` returns the row type from `@wildwinter/scoperegistry` itself,
|
|
42
|
+
rather than a local interface redeclaring it. `path` holds exactly what `ref`
|
|
43
|
+
held: the reference `getProperty` / `setProperty` take. `name` is the bare declared name.
|
|
44
|
+
`writable` is always true here, because Patter has no read-only shared property; it is carried
|
|
45
|
+
because the row is shared with the Storylet Engine, which does declare them.
|
|
46
|
+
|
|
47
|
+
The fork it replaces was not free. A quality's `stages` was added to the local copy and so never
|
|
48
|
+
reached the shared type, and the Storylet Engine - reading the same property model through the
|
|
49
|
+
same registry - could not carry a ladder at all: it edited a quality as free text on every
|
|
50
|
+
platform it ships. One row, one place to add the next field.
|
|
51
|
+
- **BREAKING: a row's address is the qualified one, `@patter.gold`, not `@gold`.** Both forms
|
|
52
|
+
have always resolved on input and still do - an unqualified name defaults to the `patter`
|
|
53
|
+
scope - so `getProperty("@gold")` is unaffected. What changed is the address a row REPORTS,
|
|
54
|
+
which is what a state panel displays and what an inspector writes back through. It matches
|
|
55
|
+
what `@scene` and the other family's scopes have always looked like.
|
|
56
|
+
|
|
57
|
+
- **Scene and stage state is held in the shared property bag.** `@scene` properties lived in
|
|
58
|
+
hand-rolled maps that duplicated the bag's own seeding, so they missed its guards: two flows
|
|
59
|
+
entering one scene now never share a mutable flags list, and a `temporary` property's reset on
|
|
60
|
+
re-entry goes through the bag, which means a state logger sees it. **The save format is
|
|
61
|
+
unchanged** - a flat name/value map per scene, and a load that seeds from the bundle's
|
|
62
|
+
declarations before laying saved values over, so a property a save predates keeps its default.
|
|
63
|
+
- **BREAKING: `PropertyView` is gone; `listProperties()` returns `PropertyRow`.** It was the
|
|
64
|
+
shared row plus a `path`, and `path` is on the shared row now, so the name was a second name
|
|
65
|
+
for one type. `PropertyRow` is re-exported from `@patterkit/runtime`, so naming a row needs no
|
|
66
|
+
dependency on `@wildwinter/scoperegistry`.
|
|
67
|
+
- **Requires `@wildwinter/scoperegistry` ^0.4.0**, which is where the row's `path` lives.
|
|
68
|
+
|
|
69
|
+
### Added
|
|
70
|
+
|
|
71
|
+
- **A decision log, and `onDryChoice`.** Opening a run with `log: true` records what the engine
|
|
72
|
+
decided and why - each choice with the options it offered, the ones it greyed out and the
|
|
73
|
+
reason, each jump, each property write with the value it replaced. `Engine.log()` is the whole
|
|
74
|
+
run in order, a `Flow`'s own log is flow-local, and `onTrace` streams entries live rather than
|
|
75
|
+
retaining them. `onDryChoice` fires when a choice runs dry - no takeable option, no eligible
|
|
76
|
+
fallback - so the silent fall-through is observable; it survives alongside the log because it is
|
|
77
|
+
live feedback, not a record.
|
|
78
|
+
|
|
79
|
+
### Fixed
|
|
80
|
+
|
|
81
|
+
- **A quality row carries its ladder.** `stages` was on the row so an examiner could offer the
|
|
82
|
+
stages instead of a free-text box, and the code that builds rows never filled it in - on this
|
|
83
|
+
runtime and two others. Every quality row came out without one.
|
|
84
|
+
|
|
36
85
|
## [0.8.0] - 2026-09-01
|
|
37
86
|
|
|
38
87
|
### Changed
|
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
34
34
|
var import_expr = require("@wildwinter/expr");
|
|
35
35
|
var import_expr_specificity = require("@wildwinter/expr-specificity");
|
|
36
36
|
var import_scoperegistry = require("@wildwinter/scoperegistry");
|
|
37
|
+
var import_scoperegistry2 = require("@wildwinter/scoperegistry");
|
|
37
38
|
var import_dialect = require("@patterkit/dialect");
|
|
38
39
|
var import_model = require("@patterkit/model");
|
|
39
40
|
|
|
@@ -92,6 +93,10 @@ var Engine = class _Engine {
|
|
|
92
93
|
/** The options this engine was built with - reused verbatim by `hotSwap` so the replacement
|
|
93
94
|
* engine keeps the same world resolver, custom RNG, and diagnostic hooks. */
|
|
94
95
|
creationOptions;
|
|
96
|
+
/** The run's ordered stream: every flow's events, each naming its flow. Empty and
|
|
97
|
+
* unwritten unless `options.log` asked for it. */
|
|
98
|
+
engineLog = [];
|
|
99
|
+
engineTraceHandlers = /* @__PURE__ */ new Set();
|
|
95
100
|
constructor(bundle, options = {}) {
|
|
96
101
|
this.creationOptions = options;
|
|
97
102
|
const locale = options.locale ?? bundle.locales.default;
|
|
@@ -127,7 +132,7 @@ var Engine = class _Engine {
|
|
|
127
132
|
const patterSharedDecls = props.filter((p) => p.shared ?? true).map(toDecl);
|
|
128
133
|
const patterLocalDecls = props.filter((p) => !(p.shared ?? true)).map(toDecl);
|
|
129
134
|
const patterSharedNames = new Set(patterSharedDecls.map((d) => d.name.toLowerCase()));
|
|
130
|
-
const shared = new import_scoperegistry.ScopeRegistry().defineOwned("patter", patterSharedDecls);
|
|
135
|
+
const shared = new import_scoperegistry.ScopeRegistry().defineOwned("patter", patterSharedDecls, "@patter.");
|
|
131
136
|
const hostBound = /* @__PURE__ */ new Set();
|
|
132
137
|
if (options.world) {
|
|
133
138
|
const worldSpec = bundle.scopeRegistry?.scopes.find((s) => s.token === "world");
|
|
@@ -146,6 +151,8 @@ var Engine = class _Engine {
|
|
|
146
151
|
sceneSharedNames.set(sceneId, names);
|
|
147
152
|
}
|
|
148
153
|
this.host = {
|
|
154
|
+
logEnabled: options.log ?? false,
|
|
155
|
+
emitEngine: (flow, event, scene) => this.emitEngine(flow, event, scene),
|
|
149
156
|
bundle,
|
|
150
157
|
emitIds,
|
|
151
158
|
strings,
|
|
@@ -520,14 +527,44 @@ var Engine = class _Engine {
|
|
|
520
527
|
}
|
|
521
528
|
/** The shared `@patter` properties, for a live state inspector: each with its ref, type, current
|
|
522
529
|
* value, declared default (for reset), and enum options. Mirrors the Unity / Godot ports. */
|
|
530
|
+
/** The run's decisions, in order, each naming the flow it happened in. Empty unless the
|
|
531
|
+
* run was opened with `log: true`. A flow's own log stays flow-local; this is the only
|
|
532
|
+
* place a story spanning several flows reads as one sequence. */
|
|
533
|
+
log() {
|
|
534
|
+
return this.engineLog;
|
|
535
|
+
}
|
|
536
|
+
/** Drop the retained entries. `seq` does NOT restart: two reads of a log either side of a
|
|
537
|
+
* clear still agree about what came first. */
|
|
538
|
+
clearLog() {
|
|
539
|
+
this.engineLog.length = 0;
|
|
540
|
+
}
|
|
541
|
+
/** Live tap on the run's decisions, for tooling that wants them as they happen rather than
|
|
542
|
+
* retained. Returns its own unsubscribe. */
|
|
543
|
+
onTrace(handler) {
|
|
544
|
+
this.engineTraceHandlers.add(handler);
|
|
545
|
+
return () => this.engineTraceHandlers.delete(handler);
|
|
546
|
+
}
|
|
547
|
+
emitEngine(flow, event, scene) {
|
|
548
|
+
for (const h of this.engineTraceHandlers) h(flow, event);
|
|
549
|
+
if (!this.host.logEnabled) return;
|
|
550
|
+
this.engineLog.push({ ...event, flow, seq: this.engineLog.length, ...scene ? { scene } : {} });
|
|
551
|
+
}
|
|
523
552
|
listProperties() {
|
|
524
553
|
return this.host.patterSharedDecls.map((d) => ({
|
|
525
|
-
|
|
554
|
+
name: d.name,
|
|
555
|
+
// The qualified address, matching what the bag composes for every other scope.
|
|
556
|
+
// `@gold` still resolves on input; it is the shorthand, not the address.
|
|
557
|
+
path: `@patter.${d.name}`,
|
|
526
558
|
type: d.type,
|
|
527
559
|
values: d.values,
|
|
528
560
|
stages: d.stages,
|
|
529
561
|
value: this.getProperty(`@${d.name}`),
|
|
530
|
-
default:
|
|
562
|
+
default: (0, import_scoperegistry2.defaultFor)(d),
|
|
563
|
+
// Part of the shared row. Always true here today: `toDecl` never sets it, because
|
|
564
|
+
// Patter has no read-only shared property. The Storylet Engine does declare them
|
|
565
|
+
// and its panels disable the editor accordingly, so the field is carried rather
|
|
566
|
+
// than dropped - and the day a read-only @patter property exists, the row says so.
|
|
567
|
+
writable: d.writable ?? true
|
|
531
568
|
}));
|
|
532
569
|
}
|
|
533
570
|
// @scene is scene-namespaced and needs a flow's current scene - silently
|
|
@@ -560,7 +597,7 @@ var Engine = class _Engine {
|
|
|
560
597
|
shared: this.host.shared.save(),
|
|
561
598
|
sharedVisits: Object.fromEntries(this.host.sharedVisits),
|
|
562
599
|
sharedSelectors: serialiseSelectors(this.host.sharedSelectors),
|
|
563
|
-
stageBags: Object.fromEntries([...this.host.stageBags].map(([s, bag]) => [s,
|
|
600
|
+
stageBags: Object.fromEntries([...this.host.stageBags].map(([s, bag]) => [s, bag.save()])),
|
|
564
601
|
flows
|
|
565
602
|
};
|
|
566
603
|
}
|
|
@@ -573,7 +610,13 @@ var Engine = class _Engine {
|
|
|
573
610
|
this.host.sharedSelectors.clear();
|
|
574
611
|
for (const [id, st] of deserialiseSelectors(save.sharedSelectors)) this.host.sharedSelectors.set(id, st);
|
|
575
612
|
this.host.stageBags.clear();
|
|
576
|
-
for (const [s,
|
|
613
|
+
for (const [s, values] of Object.entries(save.stageBags ?? {})) {
|
|
614
|
+
const shared = this.host.sceneSharedNames.get(s) ?? /* @__PURE__ */ new Set();
|
|
615
|
+
const decls = (this.host.bundle.scenes[s]?.sceneProps ?? []).filter((d) => shared.has(d.name.toLowerCase()));
|
|
616
|
+
const bag = new import_scoperegistry2.PropertyBag(decls);
|
|
617
|
+
bag.load(values);
|
|
618
|
+
this.host.stageBags.set(s, bag);
|
|
619
|
+
}
|
|
577
620
|
this.flowsById.clear();
|
|
578
621
|
for (const [id, snap] of Object.entries(save.flows)) {
|
|
579
622
|
const flow = new Flow(id, this.host, this.defaultSeed);
|
|
@@ -616,6 +659,7 @@ var Flow = class {
|
|
|
616
659
|
// The SHARED halves live on the host (`host.shared` / `host.stageBags`). Each
|
|
617
660
|
// resolver presents one merged scope, routing each property to its half by the
|
|
618
661
|
// declared `shared` flag.
|
|
662
|
+
/** This flow's per-scene LOCAL scene props; see FlowHost.stageBags. */
|
|
619
663
|
sceneBags = /* @__PURE__ */ new Map();
|
|
620
664
|
patterResolver = {
|
|
621
665
|
get: (n) => this.host.patterSharedNames.has(n) ? this.host.shared.get("patter", n) : this.local.get("patter", n),
|
|
@@ -629,13 +673,13 @@ var Flow = class {
|
|
|
629
673
|
const s = this.currentSceneId;
|
|
630
674
|
if (s === null) return void 0;
|
|
631
675
|
const bag = this.host.sceneSharedNames.get(s)?.has(n) ? this.host.stageBags.get(s) : this.sceneBags.get(s);
|
|
632
|
-
return bag?.
|
|
676
|
+
return bag?.get(n);
|
|
633
677
|
},
|
|
634
678
|
set: (n, v) => {
|
|
635
679
|
const s = this.currentSceneId;
|
|
636
680
|
if (s === null) return;
|
|
637
681
|
const bag = this.host.sceneSharedNames.get(s)?.has(n) ? this.host.stageBags.get(s) : this.sceneBags.get(s);
|
|
638
|
-
if (bag) bag
|
|
682
|
+
if (bag) bag.set(n, v);
|
|
639
683
|
}
|
|
640
684
|
};
|
|
641
685
|
// The eval context is built ONCE: every constituent resolves live state at
|
|
@@ -644,6 +688,9 @@ var Flow = class {
|
|
|
644
688
|
// `local`/`sceneBags`/`currentSceneId`; the host callbacks read current flow
|
|
645
689
|
// fields). Rebuilding it per evaluation was the engine's hottest allocation.
|
|
646
690
|
evalCtx;
|
|
691
|
+
flowLog = [];
|
|
692
|
+
/** Monotonic across the flow's life; survives clearLog so two reads agree on order. */
|
|
693
|
+
flowSeq = 0;
|
|
647
694
|
constructor(id, host, seed) {
|
|
648
695
|
this.id = id;
|
|
649
696
|
this.host = host;
|
|
@@ -869,7 +916,17 @@ var Flow = class {
|
|
|
869
916
|
this.stack.pop();
|
|
870
917
|
continue;
|
|
871
918
|
}
|
|
919
|
+
const from = frame.index;
|
|
872
920
|
while (frame.index < children.length && !this.eligible(children[frame.index])) frame.index++;
|
|
921
|
+
if (this.host.logEnabled && frame.index !== from) {
|
|
922
|
+
this.emit({
|
|
923
|
+
type: "select",
|
|
924
|
+
group: frame.containerId,
|
|
925
|
+
selector: "run",
|
|
926
|
+
children: children.slice(from, frame.index + 1).map((c, i) => ({ id: c.id, eligible: from + i === frame.index })),
|
|
927
|
+
picked: children[frame.index]?.id ?? null
|
|
928
|
+
});
|
|
929
|
+
}
|
|
873
930
|
if (frame.index >= children.length) {
|
|
874
931
|
this.stack.pop();
|
|
875
932
|
continue;
|
|
@@ -878,6 +935,24 @@ var Flow = class {
|
|
|
878
935
|
}
|
|
879
936
|
}
|
|
880
937
|
/** The options of a pending choice (empty when not at a choice point). */
|
|
938
|
+
/** This flow's decisions, in order. Empty unless the run was opened with `log: true`.
|
|
939
|
+
* The engine's log carries the same events tagged with the flow; this one is what a
|
|
940
|
+
* single conversation reads as. */
|
|
941
|
+
log() {
|
|
942
|
+
return this.flowLog;
|
|
943
|
+
}
|
|
944
|
+
/** Drop the retained entries. `seq` keeps counting, so order survives a clear. */
|
|
945
|
+
clearLog() {
|
|
946
|
+
this.flowLog.length = 0;
|
|
947
|
+
}
|
|
948
|
+
/** Record one decision, on this flow's log and the engine's. Cheap to call with logging
|
|
949
|
+
* off: the entry is never built. */
|
|
950
|
+
emit(event) {
|
|
951
|
+
const scene = this.currentSceneId ?? void 0;
|
|
952
|
+
this.host.emitEngine(this.id, event, scene);
|
|
953
|
+
if (!this.host.logEnabled) return;
|
|
954
|
+
this.flowLog.push({ ...event, seq: this.flowSeq++, ...scene ? { scene } : {} });
|
|
955
|
+
}
|
|
881
956
|
getChoices() {
|
|
882
957
|
return this.pendingChoice?.options ?? [];
|
|
883
958
|
}
|
|
@@ -889,6 +964,7 @@ var Flow = class {
|
|
|
889
964
|
if (!option) throw new Error(`unknown choice option: ${id}`);
|
|
890
965
|
if (!option.eligible) throw new Error(`choice option is not eligible: ${id}`);
|
|
891
966
|
const node = choice.byId.get(id);
|
|
967
|
+
this.emit({ type: "chose", group: choice.groupId, option: id });
|
|
892
968
|
this.pendingChoice = null;
|
|
893
969
|
this.pendingPromptBeat = this.host.replayPromptOnChoose ? this.promptBeatOf(node) ?? null : null;
|
|
894
970
|
this.pendingPromptOwnerId = this.pendingPromptBeat ? node.id : null;
|
|
@@ -922,7 +998,7 @@ var Flow = class {
|
|
|
922
998
|
return {
|
|
923
999
|
scopes: this.local.save(),
|
|
924
1000
|
// owned scope "patter" = the NOT-shared globals (@scene saved separately)
|
|
925
|
-
sceneBags: Object.fromEntries([...this.sceneBags].map(([s, bag]) => [s,
|
|
1001
|
+
sceneBags: Object.fromEntries([...this.sceneBags].map(([s, bag]) => [s, bag.save()])),
|
|
926
1002
|
rngState: this.rngState,
|
|
927
1003
|
visits: Object.fromEntries(this.visitCounts),
|
|
928
1004
|
cursor: {
|
|
@@ -960,7 +1036,13 @@ var Flow = class {
|
|
|
960
1036
|
}
|
|
961
1037
|
return { ...frame };
|
|
962
1038
|
});
|
|
963
|
-
this.sceneBags = new Map(Object.entries(snap.sceneBags ?? {}).map(([s,
|
|
1039
|
+
this.sceneBags = new Map(Object.entries(snap.sceneBags ?? {}).map(([s, values]) => {
|
|
1040
|
+
const shared = this.host.sceneSharedNames.get(s) ?? /* @__PURE__ */ new Set();
|
|
1041
|
+
const decls = (this.host.bundle.scenes[s]?.sceneProps ?? []).filter((d) => !shared.has(d.name.toLowerCase()));
|
|
1042
|
+
const bag = new import_scoperegistry2.PropertyBag(decls);
|
|
1043
|
+
bag.load(values);
|
|
1044
|
+
return [s, bag];
|
|
1045
|
+
}));
|
|
964
1046
|
this.local = this.freshLocal();
|
|
965
1047
|
this.local.load(snap.scopes);
|
|
966
1048
|
this.activeSnippet = null;
|
|
@@ -1054,6 +1136,7 @@ var Flow = class {
|
|
|
1054
1136
|
byId.set(child.id, child);
|
|
1055
1137
|
}
|
|
1056
1138
|
if (options.length > 0) {
|
|
1139
|
+
this.emit({ type: "choice", group: group.id, options: options.map((o) => ({ id: o.id, eligible: o.eligible })) });
|
|
1057
1140
|
this.pendingChoice = { groupId: group.id, options, byId };
|
|
1058
1141
|
return;
|
|
1059
1142
|
}
|
|
@@ -1062,6 +1145,7 @@ var Flow = class {
|
|
|
1062
1145
|
this.enterChild(fallback);
|
|
1063
1146
|
return;
|
|
1064
1147
|
}
|
|
1148
|
+
this.emit({ type: "dry", group: group.id });
|
|
1065
1149
|
this.host.onDryChoice?.(group.id);
|
|
1066
1150
|
}
|
|
1067
1151
|
// -- Jumps (jump / call-return) ----------------------------------------
|
|
@@ -1076,6 +1160,7 @@ var Flow = class {
|
|
|
1076
1160
|
* hard-ends the flow regardless of the callstack.
|
|
1077
1161
|
*/
|
|
1078
1162
|
enterTarget(to, mode) {
|
|
1163
|
+
this.emit({ type: "jump", to, mode });
|
|
1079
1164
|
if (to === "END") {
|
|
1080
1165
|
this.flowEnded = true;
|
|
1081
1166
|
this.stack = [];
|
|
@@ -1107,17 +1192,28 @@ var Flow = class {
|
|
|
1107
1192
|
}
|
|
1108
1193
|
// -- Selectors ------------------------------------------------------------
|
|
1109
1194
|
selectChild(group) {
|
|
1195
|
+
const verdicts = group.children.map((c) => ({ id: c.id, eligible: this.eligible(c) }));
|
|
1110
1196
|
const eligible = group.children.filter((c) => this.eligible(c));
|
|
1111
|
-
|
|
1197
|
+
const order = group.options?.order ?? "sequential";
|
|
1198
|
+
const exhaust = group.options?.exhaust ?? "once";
|
|
1199
|
+
const trace = (picked) => {
|
|
1200
|
+
this.emit({
|
|
1201
|
+
type: "select",
|
|
1202
|
+
group: group.id,
|
|
1203
|
+
selector: group.selector ?? "default",
|
|
1204
|
+
...group.selector === "sequence" ? { order, exhaust } : {},
|
|
1205
|
+
children: verdicts,
|
|
1206
|
+
picked: picked?.id ?? null
|
|
1207
|
+
});
|
|
1208
|
+
return picked;
|
|
1209
|
+
};
|
|
1210
|
+
if (eligible.length === 0) return trace(null);
|
|
1112
1211
|
const st = this.selectorState(group);
|
|
1113
1212
|
switch (group.selector) {
|
|
1114
1213
|
case "branch":
|
|
1115
|
-
return eligible[0];
|
|
1116
|
-
case "sequence":
|
|
1117
|
-
|
|
1118
|
-
const exhaust = group.options?.exhaust ?? "once";
|
|
1119
|
-
return order === "shuffle" ? this.pickShuffle(eligible, exhaust, st) : order === "specificity" ? this.pickSpecificity(eligible, exhaust, st) : this.pickSequential(eligible, exhaust, st);
|
|
1120
|
-
}
|
|
1214
|
+
return trace(eligible[0]);
|
|
1215
|
+
case "sequence":
|
|
1216
|
+
return trace(order === "shuffle" ? this.pickShuffle(eligible, exhaust, st) : order === "specificity" ? this.pickSpecificity(eligible, exhaust, st) : this.pickSequential(eligible, exhaust, st));
|
|
1121
1217
|
case "run":
|
|
1122
1218
|
case "choice":
|
|
1123
1219
|
default:
|
|
@@ -1235,7 +1331,10 @@ var Flow = class {
|
|
|
1235
1331
|
// -- Effects + expressions ------------------------------------------------
|
|
1236
1332
|
runEffects(effects) {
|
|
1237
1333
|
for (const e of effects ?? []) {
|
|
1238
|
-
|
|
1334
|
+
const value = this.evalExpr(e.value);
|
|
1335
|
+
const prev = this.host.logEnabled ? this.getProperty(e.target) : void 0;
|
|
1336
|
+
this.setProperty(e.target, value);
|
|
1337
|
+
this.emit({ type: "write", target: e.target, value, ...prev !== void 0 ? { prev } : {} });
|
|
1239
1338
|
}
|
|
1240
1339
|
}
|
|
1241
1340
|
eligible(node) {
|
|
@@ -1379,7 +1478,7 @@ var Flow = class {
|
|
|
1379
1478
|
}
|
|
1380
1479
|
/** The per-flow registry: the NOT-shared `@patter` globals (the shared ones live on the host). */
|
|
1381
1480
|
freshLocal() {
|
|
1382
|
-
return new import_scoperegistry.ScopeRegistry().defineOwned("patter", this.host.patterLocalDecls);
|
|
1481
|
+
return new import_scoperegistry.ScopeRegistry().defineOwned("patter", this.host.patterLocalDecls, "@patter.");
|
|
1383
1482
|
}
|
|
1384
1483
|
/**
|
|
1385
1484
|
* Seed a scene's `@scene` props (spec §7). The not-shared props seed THIS flow's
|
|
@@ -1390,27 +1489,18 @@ var Flow = class {
|
|
|
1390
1489
|
*/
|
|
1391
1490
|
seedScene(scene) {
|
|
1392
1491
|
const shared = this.host.sceneSharedNames.get(scene.id) ?? /* @__PURE__ */ new Set();
|
|
1492
|
+
const props = scene.sceneProps ?? [];
|
|
1393
1493
|
if (!this.sceneBags.has(scene.id)) {
|
|
1394
|
-
|
|
1395
|
-
for (const decl of scene.sceneProps ?? []) {
|
|
1396
|
-
const name = decl.name.toLowerCase();
|
|
1397
|
-
if (!shared.has(name)) bag[name] = sceneDefault(decl);
|
|
1398
|
-
}
|
|
1399
|
-
this.sceneBags.set(scene.id, bag);
|
|
1494
|
+
this.sceneBags.set(scene.id, new import_scoperegistry2.PropertyBag(props.filter((d) => !shared.has(d.name.toLowerCase()))));
|
|
1400
1495
|
}
|
|
1401
1496
|
if (!this.host.stageBags.has(scene.id)) {
|
|
1402
|
-
|
|
1403
|
-
for (const decl of scene.sceneProps ?? []) {
|
|
1404
|
-
const name = decl.name.toLowerCase();
|
|
1405
|
-
if (shared.has(name)) bag[name] = sceneDefault(decl);
|
|
1406
|
-
}
|
|
1407
|
-
this.host.stageBags.set(scene.id, bag);
|
|
1497
|
+
this.host.stageBags.set(scene.id, new import_scoperegistry2.PropertyBag(props.filter((d) => shared.has(d.name.toLowerCase()))));
|
|
1408
1498
|
}
|
|
1409
1499
|
for (const decl of scene.sceneProps ?? []) {
|
|
1410
1500
|
if (!decl.temporary) continue;
|
|
1411
1501
|
const name = decl.name.toLowerCase();
|
|
1412
1502
|
const bag = shared.has(name) ? this.host.stageBags.get(scene.id) : this.sceneBags.get(scene.id);
|
|
1413
|
-
if (bag) bag
|
|
1503
|
+
if (bag) bag.set(name, (0, import_scoperegistry2.defaultFor)(decl));
|
|
1414
1504
|
}
|
|
1415
1505
|
}
|
|
1416
1506
|
};
|
|
@@ -1448,47 +1538,13 @@ function deserialiseSelectors(rec) {
|
|
|
1448
1538
|
function toDecl(decl) {
|
|
1449
1539
|
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default };
|
|
1450
1540
|
}
|
|
1451
|
-
function declDefault(d) {
|
|
1452
|
-
if (d.default !== void 0) return d.default;
|
|
1453
|
-
switch (d.type) {
|
|
1454
|
-
case "number":
|
|
1455
|
-
return 0;
|
|
1456
|
-
case "string":
|
|
1457
|
-
return "";
|
|
1458
|
-
case "flags":
|
|
1459
|
-
return [];
|
|
1460
|
-
case "enum":
|
|
1461
|
-
return d.values?.[0] ?? "";
|
|
1462
|
-
case "quality":
|
|
1463
|
-
return d.stages?.[0] ?? "";
|
|
1464
|
-
default:
|
|
1465
|
-
return false;
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
1541
|
function toForeignDecl(decl) {
|
|
1469
1542
|
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default, writable: decl.writable };
|
|
1470
1543
|
}
|
|
1471
|
-
function hostScopeDefault(decl) {
|
|
1472
|
-
if (decl.default !== void 0) return decl.default;
|
|
1473
|
-
switch (decl.type) {
|
|
1474
|
-
case "boolean":
|
|
1475
|
-
return false;
|
|
1476
|
-
case "number":
|
|
1477
|
-
return 0;
|
|
1478
|
-
case "string":
|
|
1479
|
-
return "";
|
|
1480
|
-
case "flags":
|
|
1481
|
-
return [];
|
|
1482
|
-
case "enum":
|
|
1483
|
-
return decl.values?.[0] ?? "";
|
|
1484
|
-
case "quality":
|
|
1485
|
-
return decl.stages?.[0] ?? "";
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
1544
|
function selfBackedResolver(decls) {
|
|
1489
1545
|
const key = (name) => name.toLowerCase();
|
|
1490
1546
|
const bag = /* @__PURE__ */ new Map();
|
|
1491
|
-
for (const d of decls) bag.set(key(d.name),
|
|
1547
|
+
for (const d of decls) bag.set(key(d.name), (0, import_scoperegistry2.defaultFor)(d));
|
|
1492
1548
|
return {
|
|
1493
1549
|
get: (name) => bag.get(key(name)),
|
|
1494
1550
|
set: (name, value) => {
|
|
@@ -1496,23 +1552,6 @@ function selfBackedResolver(decls) {
|
|
|
1496
1552
|
}
|
|
1497
1553
|
};
|
|
1498
1554
|
}
|
|
1499
|
-
function sceneDefault(decl) {
|
|
1500
|
-
if (decl.default !== void 0) return decl.default;
|
|
1501
|
-
switch (decl.type) {
|
|
1502
|
-
case "boolean":
|
|
1503
|
-
return false;
|
|
1504
|
-
case "number":
|
|
1505
|
-
return 0;
|
|
1506
|
-
case "string":
|
|
1507
|
-
return "";
|
|
1508
|
-
case "flags":
|
|
1509
|
-
return [];
|
|
1510
|
-
case "enum":
|
|
1511
|
-
return decl.values?.[0] ?? "";
|
|
1512
|
-
case "quality":
|
|
1513
|
-
return decl.stages?.[0] ?? "";
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
1555
|
function truthy(v) {
|
|
1517
1556
|
if (typeof v === "boolean") return v;
|
|
1518
1557
|
if (typeof v === "number") return v !== 0;
|