@patterkit/runtime 0.19.0 → 0.21.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 +56 -0
- package/README.md +4 -3
- package/dist/index.cjs +107 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -20
- package/dist/index.d.ts +53 -20
- package/dist/index.js +107 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -43,6 +43,18 @@ function buildTagIndex(bundle) {
|
|
|
43
43
|
// src/engine.ts
|
|
44
44
|
import { SAVE_VERSION } from "@patterkit/model";
|
|
45
45
|
var astCache = /* @__PURE__ */ new WeakMap();
|
|
46
|
+
var tagIndexCache = /* @__PURE__ */ new WeakMap();
|
|
47
|
+
function tagIndexFor(bundle) {
|
|
48
|
+
let index = tagIndexCache.get(bundle);
|
|
49
|
+
if (!index) {
|
|
50
|
+
index = buildTagIndex(bundle);
|
|
51
|
+
tagIndexCache.set(bundle, index);
|
|
52
|
+
}
|
|
53
|
+
return index;
|
|
54
|
+
}
|
|
55
|
+
function tagsOf(host, id) {
|
|
56
|
+
return host.tagIndex.get(id)?.slice();
|
|
57
|
+
}
|
|
46
58
|
var OWNER = "Patter";
|
|
47
59
|
var esc = (id) => id.replace(/%/g, "%25").replace(/\//g, "%2F");
|
|
48
60
|
var OWNS_REGISTRY = /* @__PURE__ */ Symbol("ownsRegistry");
|
|
@@ -81,7 +93,7 @@ var Engine = class _Engine {
|
|
|
81
93
|
sceneGameIdToId = /* @__PURE__ */ new Map();
|
|
82
94
|
blockGameIdToId = /* @__PURE__ */ new Map();
|
|
83
95
|
/** The options this engine was built with - reused verbatim by `hotSwap` so the replacement
|
|
84
|
-
* engine keeps the same
|
|
96
|
+
* engine keeps the same host scopes, custom RNG, and diagnostic hooks. */
|
|
85
97
|
creationOptions;
|
|
86
98
|
/** The run's ordered stream: every flow's events, each naming its flow. Empty and
|
|
87
99
|
* unwritten unless `options.log` asked for it. */
|
|
@@ -133,12 +145,12 @@ var Engine = class _Engine {
|
|
|
133
145
|
registry.mountOwned("patter", patterBag, { owner: OWNER });
|
|
134
146
|
registered.push("patter");
|
|
135
147
|
const hostBound = /* @__PURE__ */ new Set();
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
const decls = (
|
|
139
|
-
registry.defineForeign(
|
|
140
|
-
registered.push(
|
|
141
|
-
hostBound.add(
|
|
148
|
+
for (const [token, resolver] of Object.entries(boundScopes(options))) {
|
|
149
|
+
const spec = bundle.scopeRegistry?.scopes.find((s) => s.token === token);
|
|
150
|
+
const decls = (spec?.declarations ?? []).map(toForeignDecl);
|
|
151
|
+
registry.defineForeign(token, resolver, decls, { writable: spec?.writable ?? true, owner: OWNER });
|
|
152
|
+
registered.push(token);
|
|
153
|
+
hostBound.add(token);
|
|
142
154
|
}
|
|
143
155
|
for (const spec of ownsRegistry ? bundle.scopeRegistry?.scopes ?? [] : []) {
|
|
144
156
|
if (hostBound.has(spec.token) || registry.has(spec.token)) continue;
|
|
@@ -158,6 +170,7 @@ var Engine = class _Engine {
|
|
|
158
170
|
}
|
|
159
171
|
this.host = {
|
|
160
172
|
logEnabled: options.log ?? false,
|
|
173
|
+
tracing: options.log ?? false,
|
|
161
174
|
emitEngine: (flow, event, scene) => this.emitEngine(flow, event, scene),
|
|
162
175
|
bundle,
|
|
163
176
|
emitIds,
|
|
@@ -170,7 +183,7 @@ var Engine = class _Engine {
|
|
|
170
183
|
sceneGameIdToId: this.sceneGameIdToId,
|
|
171
184
|
blockGameIdToId: this.blockGameIdToId,
|
|
172
185
|
// same instances the engine resolves with
|
|
173
|
-
tagIndex:
|
|
186
|
+
tagIndex: tagIndexFor(bundle),
|
|
174
187
|
registry,
|
|
175
188
|
ownsRegistry,
|
|
176
189
|
patterBag,
|
|
@@ -180,6 +193,7 @@ var Engine = class _Engine {
|
|
|
180
193
|
patterSharedNames,
|
|
181
194
|
sceneSharedNames,
|
|
182
195
|
sharedVisits: /* @__PURE__ */ new Map(),
|
|
196
|
+
qualityLadders: /* @__PURE__ */ new Map(),
|
|
183
197
|
sharedSelectors: /* @__PURE__ */ new Map(),
|
|
184
198
|
stageBags: /* @__PURE__ */ new Map(),
|
|
185
199
|
journal: null,
|
|
@@ -206,6 +220,11 @@ var Engine = class _Engine {
|
|
|
206
220
|
get isSourceDebug() {
|
|
207
221
|
return this.sourceDebug;
|
|
208
222
|
}
|
|
223
|
+
/** The bundle's build identity (its content hash), the one a debug link handshakes with, or an empty
|
|
224
|
+
* string for a bundle compiled without one. */
|
|
225
|
+
get buildId() {
|
|
226
|
+
return this.host.bundle.content?.hash ?? "";
|
|
227
|
+
}
|
|
209
228
|
/**
|
|
210
229
|
* Switch the active locale LIVE - a real game's "language" setting can change mid-session. Subsequent
|
|
211
230
|
* string lookups (new beats, re-resolved character names, `{@ref}` interpolation) render in the new
|
|
@@ -326,7 +345,7 @@ var Engine = class _Engine {
|
|
|
326
345
|
});
|
|
327
346
|
}
|
|
328
347
|
this.flowsById.set(id, flow);
|
|
329
|
-
flow.
|
|
348
|
+
flow.begin(sceneId, blockId);
|
|
330
349
|
return flow;
|
|
331
350
|
}
|
|
332
351
|
/**
|
|
@@ -423,18 +442,18 @@ var Engine = class _Engine {
|
|
|
423
442
|
* delivered step carries. Empty array for an unknown id or a beat with no tags anywhere up the chain.
|
|
424
443
|
*/
|
|
425
444
|
tagsForBeat(beatId) {
|
|
426
|
-
return this.host
|
|
445
|
+
return tagsOf(this.host, beatId) ?? [];
|
|
427
446
|
}
|
|
428
447
|
/** A scene's own tags (by internal id or gameId address). Empty when none / unknown. */
|
|
429
448
|
tagsForScene(sceneRef) {
|
|
430
449
|
const id = this.resolveSceneRef(sceneRef);
|
|
431
|
-
return (id != null ? this.host
|
|
450
|
+
return (id != null ? tagsOf(this.host, id) : void 0) ?? [];
|
|
432
451
|
}
|
|
433
452
|
/** A block's accumulated tags (scene + block), by scene + block ref (id or gameId). Empty when none / unknown. */
|
|
434
453
|
tagsForBlock(sceneRef, blockRef) {
|
|
435
454
|
const sceneId = this.resolveSceneRef(sceneRef);
|
|
436
455
|
const id = this.resolveBlockRef(sceneId, blockRef);
|
|
437
|
-
return (id != null ? this.host
|
|
456
|
+
return (id != null ? tagsOf(this.host, id) : void 0) ?? [];
|
|
438
457
|
}
|
|
439
458
|
/**
|
|
440
459
|
* A scene's own author gameData (by internal id or gameId address): the RAW sparse overrides, exactly
|
|
@@ -553,7 +572,7 @@ var Engine = class _Engine {
|
|
|
553
572
|
}
|
|
554
573
|
/** One beat's static data (source locale), the same shape a delivered step carries. */
|
|
555
574
|
beatInfo(beat) {
|
|
556
|
-
const tags = this.host
|
|
575
|
+
const tags = tagsOf(this.host, beat.id);
|
|
557
576
|
const info = { id: beat.id, kind: beat.kind };
|
|
558
577
|
if (beat.kind === "line") {
|
|
559
578
|
if (beat.character !== void 0) {
|
|
@@ -573,7 +592,7 @@ var Engine = class _Engine {
|
|
|
573
592
|
}
|
|
574
593
|
/** A `{ tags }` fragment for an id, present only when the id has accumulated tags (keeps output tidy). */
|
|
575
594
|
tagsField(id) {
|
|
576
|
-
const tags = this.host
|
|
595
|
+
const tags = tagsOf(this.host, id);
|
|
577
596
|
return tags && tags.length ? { tags } : {};
|
|
578
597
|
}
|
|
579
598
|
/** Retrieve an open flow by id (undefined if none / closed). */
|
|
@@ -690,7 +709,7 @@ var Engine = class _Engine {
|
|
|
690
709
|
for (const s of this.host.stageBags.keys()) reg.remove(keys.stage(s), { keep });
|
|
691
710
|
this.host.stageBags.clear();
|
|
692
711
|
for (const t of ["patter", ...this.host.hostScopes]) if (reg.has(t)) reg.remove(t, { keep });
|
|
693
|
-
|
|
712
|
+
for (const t of Object.keys(boundScopes(this.creationOptions))) if (reg.has(t)) reg.remove(t);
|
|
694
713
|
}
|
|
695
714
|
/** Read a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level). */
|
|
696
715
|
getProperty(ref) {
|
|
@@ -710,8 +729,6 @@ var Engine = class _Engine {
|
|
|
710
729
|
this.host.registry.set(scope, name, value, { host: true });
|
|
711
730
|
if (this.host.journal && prev !== void 0) this.host.journal.undo.push(() => this.host.registry.set(scope, name, prev, { host: true }));
|
|
712
731
|
}
|
|
713
|
-
/** The shared `@patter` properties, for a live state inspector: each with its ref, type, current
|
|
714
|
-
* value, declared default (for reset), and enum options. Mirrors the Unity / Godot ports. */
|
|
715
732
|
/** The run's decisions, in order, each naming the flow it happened in. Empty unless the
|
|
716
733
|
* run was opened with `log: true`. A flow's own log stays flow-local; this is the only
|
|
717
734
|
* place a story spanning several flows reads as one sequence. */
|
|
@@ -727,13 +744,24 @@ var Engine = class _Engine {
|
|
|
727
744
|
* retained. Returns its own unsubscribe. */
|
|
728
745
|
onTrace(handler) {
|
|
729
746
|
this.engineTraceHandlers.add(handler);
|
|
730
|
-
|
|
747
|
+
this.host.tracing = true;
|
|
748
|
+
return () => {
|
|
749
|
+
this.engineTraceHandlers.delete(handler);
|
|
750
|
+
this.host.tracing = this.host.logEnabled || this.engineTraceHandlers.size > 0;
|
|
751
|
+
};
|
|
731
752
|
}
|
|
732
753
|
emitEngine(flow, event, scene) {
|
|
733
754
|
for (const h of this.engineTraceHandlers) h(flow, event);
|
|
734
755
|
if (!this.host.logEnabled) return;
|
|
735
756
|
this.engineLog.push({ ...event, flow, seq: this.engineSeq++, ...scene ? { scene } : {} });
|
|
736
757
|
}
|
|
758
|
+
/** How many times each node has been entered across every flow, by node id: the shared count. With a
|
|
759
|
+
* flow's own {@link Flow.getVisitCounts}, every visit count the run keeps, read without a save. */
|
|
760
|
+
getVisitCounts() {
|
|
761
|
+
return Object.fromEntries(this.host.sharedVisits);
|
|
762
|
+
}
|
|
763
|
+
/** The shared `@patter` properties, for a live state inspector: each with its ref, type, current
|
|
764
|
+
* value, declared default (for reset), and enum options. Every runtime has the same call. */
|
|
737
765
|
listProperties() {
|
|
738
766
|
return this.host.patterSharedDecls.map((d) => ({
|
|
739
767
|
name: d.name,
|
|
@@ -938,19 +966,21 @@ var Flow = class {
|
|
|
938
966
|
* lowercase, as the compiler emits references (the selfBackedResolver lesson). */
|
|
939
967
|
stagesFor(scope, name) {
|
|
940
968
|
const key = name.toLowerCase();
|
|
941
|
-
const fromDecls = (decls) => decls?.find((d) => d.name.toLowerCase() === key && d.type === "quality")?.stages;
|
|
942
969
|
if (scope === "patter") {
|
|
943
|
-
return
|
|
970
|
+
return ladders(this.host, "patter", () => [...this.host.patterSharedDecls, ...this.host.patterLocalDecls]).get(key);
|
|
944
971
|
}
|
|
945
972
|
if (scope === "scene") {
|
|
946
|
-
const
|
|
947
|
-
|
|
973
|
+
const sceneId = this.currentSceneId;
|
|
974
|
+
if (sceneId == null) return void 0;
|
|
975
|
+
return ladders(this.host, `scene/${sceneId}`, () => this.host.bundle.scenes[sceneId]?.sceneProps).get(key);
|
|
948
976
|
}
|
|
949
|
-
return this.registryQualities?.(scope, name) ??
|
|
977
|
+
return this.registryQualities?.(scope, name) ?? ladders(this.host, `host/${scope}`, () => this.host.bundle.scopeRegistry?.scopes.find((s) => s.token === scope)?.declarations).get(key);
|
|
950
978
|
}
|
|
951
979
|
// -- Host API -------------------------------------------------------------
|
|
952
|
-
/** Begin this flow at a scene (and optionally a specific block within it).
|
|
953
|
-
|
|
980
|
+
/** Begin this flow at a scene (and optionally a specific block within it). The engine's own entry point:
|
|
981
|
+
* openFlow and a goto on an unstarted flow begin a flow here. A game calls {@link reset}.
|
|
982
|
+
* @internal */
|
|
983
|
+
begin(sceneId, blockId) {
|
|
954
984
|
if (this.host.journal && !this.host.journal.opened.has(this)) throw new Error("a flow can't be started or reset while a checkpoint is open");
|
|
955
985
|
this.releaseBags(false);
|
|
956
986
|
this.host.registry.discardParked(keys.flow(this.id));
|
|
@@ -962,7 +992,7 @@ var Flow = class {
|
|
|
962
992
|
this.flowEnded = false;
|
|
963
993
|
this.activeSnippet = null;
|
|
964
994
|
this.beatIndex = 0;
|
|
965
|
-
this.
|
|
995
|
+
this.clearPending();
|
|
966
996
|
this.started = true;
|
|
967
997
|
if (blockId) {
|
|
968
998
|
const loc = this.host.blockIndex.get(blockId);
|
|
@@ -987,10 +1017,15 @@ var Flow = class {
|
|
|
987
1017
|
* Forget everything in this flow and begin again - its per-flow state (not-shared
|
|
988
1018
|
* `@patter` globals + `@scene` props), cursor, callstack, selector cursors, and
|
|
989
1019
|
* visit counts. Shared state (shared `@patter` / `@scene`, world visit counts) is
|
|
990
|
-
* untouched.
|
|
1020
|
+
* untouched. The one public way to begin a flow again (openFlow begins a new one).
|
|
991
1021
|
*/
|
|
992
1022
|
reset(sceneId, blockId) {
|
|
993
|
-
this.
|
|
1023
|
+
this.begin(sceneId, blockId);
|
|
1024
|
+
}
|
|
1025
|
+
/** @deprecated Use {@link reset}, the one public name for beginning a flow again on every runtime. `start`
|
|
1026
|
+
* was the same call under a second name, and goes in a later release. */
|
|
1027
|
+
start(sceneId, blockId) {
|
|
1028
|
+
this.reset(sceneId, blockId);
|
|
994
1029
|
}
|
|
995
1030
|
/**
|
|
996
1031
|
* Send this flow's cursor to an ADDRESS, exactly as an authored `go` jump would: the target scene's
|
|
@@ -1014,9 +1049,7 @@ var Flow = class {
|
|
|
1014
1049
|
this.touch();
|
|
1015
1050
|
if (scene === "END") {
|
|
1016
1051
|
this.started = true;
|
|
1017
|
-
this.
|
|
1018
|
-
this.pendingPrompt = null;
|
|
1019
|
-
this.pendingPromptOwnerId = null;
|
|
1052
|
+
this.clearPending();
|
|
1020
1053
|
this.activeSnippet = null;
|
|
1021
1054
|
this.beatIndex = 0;
|
|
1022
1055
|
this.flowEnded = true;
|
|
@@ -1031,12 +1064,10 @@ var Flow = class {
|
|
|
1031
1064
|
if (blockId === void 0) return false;
|
|
1032
1065
|
}
|
|
1033
1066
|
if (!this.started) {
|
|
1034
|
-
this.
|
|
1067
|
+
this.begin(sceneId, blockId);
|
|
1035
1068
|
return true;
|
|
1036
1069
|
}
|
|
1037
|
-
this.
|
|
1038
|
-
this.pendingPrompt = null;
|
|
1039
|
-
this.pendingPromptOwnerId = null;
|
|
1070
|
+
this.clearPending();
|
|
1040
1071
|
this.activeSnippet = null;
|
|
1041
1072
|
this.beatIndex = 0;
|
|
1042
1073
|
this.flowEnded = false;
|
|
@@ -1061,6 +1092,15 @@ var Flow = class {
|
|
|
1061
1092
|
this.stack = [];
|
|
1062
1093
|
this.activeSnippet = null;
|
|
1063
1094
|
this.beatIndex = 0;
|
|
1095
|
+
this.clearPending();
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Drop everything waiting to be delivered: an open choice, and a chosen option's prompt still to be
|
|
1099
|
+
* replayed. Every move that abandons the flow's place (start and reset, goto, close) does this, so none
|
|
1100
|
+
* leaves a stale prompt behind. start() cleared only the choice, so a reset between choose() and the
|
|
1101
|
+
* next advance() replayed the abandoned run's prompt as the new run's first beat.
|
|
1102
|
+
*/
|
|
1103
|
+
clearPending() {
|
|
1064
1104
|
this.pendingChoice = null;
|
|
1065
1105
|
this.pendingPrompt = null;
|
|
1066
1106
|
this.pendingPromptOwnerId = null;
|
|
@@ -1157,7 +1197,7 @@ var Flow = class {
|
|
|
1157
1197
|
}
|
|
1158
1198
|
const from = frame.index;
|
|
1159
1199
|
while (frame.index < children.length && !this.eligible(children[frame.index])) frame.index++;
|
|
1160
|
-
if (this.host.
|
|
1200
|
+
if (this.host.tracing && frame.index !== from) {
|
|
1161
1201
|
this.emit({
|
|
1162
1202
|
type: "select",
|
|
1163
1203
|
group: frame.containerId,
|
|
@@ -1193,14 +1233,18 @@ var Flow = class {
|
|
|
1193
1233
|
clearLog() {
|
|
1194
1234
|
this.flowLog.length = 0;
|
|
1195
1235
|
}
|
|
1196
|
-
/** Record one decision, on this flow's log and the engine's
|
|
1197
|
-
*
|
|
1236
|
+
/** Record one decision, on this flow's log and the engine's, and hand it to any `onTrace`. Each call
|
|
1237
|
+
* site checks `host.tracing` first, so with nothing logging or tracing the entry is never built. */
|
|
1198
1238
|
emit(event) {
|
|
1199
1239
|
const scene = this.currentSceneId ?? void 0;
|
|
1200
1240
|
this.host.emitEngine(this.id, event, scene);
|
|
1201
1241
|
if (!this.host.logEnabled) return;
|
|
1202
1242
|
this.flowLog.push({ ...event, seq: this.flowSeq++, ...scene ? { scene } : {} });
|
|
1203
1243
|
}
|
|
1244
|
+
/** How many times this flow has entered each node, by node id. */
|
|
1245
|
+
getVisitCounts() {
|
|
1246
|
+
return Object.fromEntries(this.visitCounts);
|
|
1247
|
+
}
|
|
1204
1248
|
/** The options of a pending choice (empty when not at a choice point). */
|
|
1205
1249
|
getChoices() {
|
|
1206
1250
|
return this.pendingChoice?.options ?? [];
|
|
@@ -1214,7 +1258,7 @@ var Flow = class {
|
|
|
1214
1258
|
if (!option.eligible) throw new Error(`choice option is not eligible: ${id}`);
|
|
1215
1259
|
this.touch();
|
|
1216
1260
|
const node = choice.byId.get(id);
|
|
1217
|
-
this.emit({ type: "chose", group: choice.groupId, option: id });
|
|
1261
|
+
if (this.host.tracing) this.emit({ type: "chose", group: choice.groupId, option: id });
|
|
1218
1262
|
this.pendingChoice = null;
|
|
1219
1263
|
const authored = this.host.replayPromptOnChoose ? this.authoredPromptOf(node) : void 0;
|
|
1220
1264
|
this.pendingPrompt = authored && option.prompt ? { beat: authored, shown: { ...option.prompt } } : null;
|
|
@@ -1410,7 +1454,7 @@ var Flow = class {
|
|
|
1410
1454
|
byId.set(child.id, child);
|
|
1411
1455
|
}
|
|
1412
1456
|
if (options.some((o) => o.eligible)) {
|
|
1413
|
-
this.emit({ type: "choice", group: group.id, options: options.map((o) => ({ id: o.id, eligible: o.eligible })) });
|
|
1457
|
+
if (this.host.tracing) this.emit({ type: "choice", group: group.id, options: options.map((o) => ({ id: o.id, eligible: o.eligible })) });
|
|
1414
1458
|
this.pendingChoice = { groupId: group.id, options, byId };
|
|
1415
1459
|
return;
|
|
1416
1460
|
}
|
|
@@ -1419,7 +1463,7 @@ var Flow = class {
|
|
|
1419
1463
|
this.enterChild(fallback);
|
|
1420
1464
|
return;
|
|
1421
1465
|
}
|
|
1422
|
-
this.emit({ type: "dry", group: group.id });
|
|
1466
|
+
if (this.host.tracing) this.emit({ type: "dry", group: group.id });
|
|
1423
1467
|
this.host.onDryChoice?.(group.id);
|
|
1424
1468
|
}
|
|
1425
1469
|
// -- Jumps (jump / call-return) ----------------------------------------
|
|
@@ -1434,7 +1478,7 @@ var Flow = class {
|
|
|
1434
1478
|
* hard-ends the flow regardless of the callstack.
|
|
1435
1479
|
*/
|
|
1436
1480
|
enterTarget(to, mode) {
|
|
1437
|
-
this.emit({ type: "jump", to, mode });
|
|
1481
|
+
if (this.host.tracing) this.emit({ type: "jump", to, mode });
|
|
1438
1482
|
if (to === "END") {
|
|
1439
1483
|
this.flowEnded = true;
|
|
1440
1484
|
this.stack = [];
|
|
@@ -1471,7 +1515,7 @@ var Flow = class {
|
|
|
1471
1515
|
const order = group.options?.order ?? "sequential";
|
|
1472
1516
|
const exhaust = group.options?.exhaust ?? "once";
|
|
1473
1517
|
const trace = (picked) => {
|
|
1474
|
-
this.emit({
|
|
1518
|
+
if (this.host.tracing) this.emit({
|
|
1475
1519
|
type: "select",
|
|
1476
1520
|
group: group.id,
|
|
1477
1521
|
selector: group.selector ?? "default",
|
|
@@ -1636,9 +1680,9 @@ var Flow = class {
|
|
|
1636
1680
|
let value;
|
|
1637
1681
|
try {
|
|
1638
1682
|
value = this.evalExpr(e.value);
|
|
1639
|
-
const prev = this.host.
|
|
1683
|
+
const prev = this.host.tracing ? this.getProperty(e.target) : void 0;
|
|
1640
1684
|
this.writeProperty(e.target, value, false);
|
|
1641
|
-
this.emit({ type: "write", target: e.target, value, ...prev !== void 0 ? { prev } : {} });
|
|
1685
|
+
if (this.host.tracing) this.emit({ type: "write", target: e.target, value, ...prev !== void 0 ? { prev } : {} });
|
|
1642
1686
|
} catch (err) {
|
|
1643
1687
|
this.reportError("effect", owner, e.value, err);
|
|
1644
1688
|
}
|
|
@@ -1659,7 +1703,7 @@ var Flow = class {
|
|
|
1659
1703
|
const message = err instanceof Error ? err.message : String(err);
|
|
1660
1704
|
const source = expr?.src;
|
|
1661
1705
|
this.host.onError({ flow: this.id, kind, node, ...source ? { source } : {}, message });
|
|
1662
|
-
this.emit({ type: "diagnostic", kind, node, ...source ? { source } : {}, message });
|
|
1706
|
+
if (this.host.tracing) this.emit({ type: "diagnostic", kind, node, ...source ? { source } : {}, message });
|
|
1663
1707
|
}
|
|
1664
1708
|
evalExpr(expr) {
|
|
1665
1709
|
return evaluate(this.conditionAst(expr), this.context(), patterDialect);
|
|
@@ -1705,7 +1749,7 @@ var Flow = class {
|
|
|
1705
1749
|
};
|
|
1706
1750
|
// -- Strings / beats ------------------------------------------------------
|
|
1707
1751
|
beatResult(beat) {
|
|
1708
|
-
const tags = this.host
|
|
1752
|
+
const tags = tagsOf(this.host, beat.id);
|
|
1709
1753
|
const withTags = tags && tags.length ? { tags } : {};
|
|
1710
1754
|
switch (beat.kind) {
|
|
1711
1755
|
case "gameEvent":
|
|
@@ -1772,7 +1816,7 @@ var Flow = class {
|
|
|
1772
1816
|
/** A replayed prompt as a step: the beat's id, gameData, and tags, and the text and speaker fields the
|
|
1773
1817
|
* choice showed. */
|
|
1774
1818
|
promptResult(beat, shown) {
|
|
1775
|
-
const tags = this.host
|
|
1819
|
+
const tags = tagsOf(this.host, beat.id);
|
|
1776
1820
|
const withTags = tags && tags.length ? { tags } : {};
|
|
1777
1821
|
if (shown.kind === "text") return { type: "text", id: beat.id, text: shown.text, gameData: beat.gameData, ...withTags };
|
|
1778
1822
|
return {
|
|
@@ -1946,6 +1990,21 @@ function mountClaims(registry, key, bag, ask) {
|
|
|
1946
1990
|
function toDecl(decl) {
|
|
1947
1991
|
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default };
|
|
1948
1992
|
}
|
|
1993
|
+
function boundScopes(options) {
|
|
1994
|
+
return { ...options.world ? { world: options.world } : {}, ...options.hostScopes };
|
|
1995
|
+
}
|
|
1996
|
+
function ladders(host, cacheKey, decls) {
|
|
1997
|
+
let found = host.qualityLadders.get(cacheKey);
|
|
1998
|
+
if (!found) {
|
|
1999
|
+
found = /* @__PURE__ */ new Map();
|
|
2000
|
+
for (const d of decls() ?? []) {
|
|
2001
|
+
const k = d.name.toLowerCase();
|
|
2002
|
+
if (d.type === "quality" && d.stages && !found.has(k)) found.set(k, d.stages);
|
|
2003
|
+
}
|
|
2004
|
+
host.qualityLadders.set(cacheKey, found);
|
|
2005
|
+
}
|
|
2006
|
+
return found;
|
|
2007
|
+
}
|
|
1949
2008
|
function toForeignDecl(decl) {
|
|
1950
2009
|
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default, writable: decl.writable };
|
|
1951
2010
|
}
|