@patterkit/runtime 0.18.0 → 0.20.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 +86 -0
- package/README.md +4 -3
- package/dist/index.cjs +162 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +93 -23
- package/dist/index.d.ts +93 -23
- package/dist/index.js +162 -72
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -127,12 +127,14 @@ interface ChoiceOption {
|
|
|
127
127
|
gameData?: GameData;
|
|
128
128
|
}
|
|
129
129
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* comes from the compiled bundle's
|
|
130
|
+
* A host scope's resolver: a `{ get, set? }` the game provides so the story can read (and, if you allow
|
|
131
|
+
* it, write) values the game owns, `@world.*` among them, at runtime. Property metadata (types,
|
|
132
|
+
* read-only) comes from the compiled bundle's declarations; the values themselves live in the host and
|
|
133
133
|
* are never stored or saved by this engine.
|
|
134
134
|
*/
|
|
135
|
-
type
|
|
135
|
+
type HostScope = ScopeResolver;
|
|
136
|
+
/** @deprecated Use {@link HostScope}, the name every Patterplay runtime uses. */
|
|
137
|
+
type WorldResolver = HostScope;
|
|
136
138
|
/** What the engine DECIDED, as opposed to what it produced. A step tells you the line that
|
|
137
139
|
* played; a trace event tells you why that line and not its siblings - which children were
|
|
138
140
|
* eligible, where the selector's cursor stood, which choice options were live.
|
|
@@ -194,12 +196,36 @@ type TraceEvent = {
|
|
|
194
196
|
value: ScalarValue;
|
|
195
197
|
prev?: ScalarValue;
|
|
196
198
|
}
|
|
197
|
-
/**
|
|
199
|
+
/** Content the engine could not evaluate and played through (see {@link PlayError}): a condition
|
|
200
|
+
* counted as false, an effect skipped, or a Best-match part scored as false. */
|
|
198
201
|
| {
|
|
199
202
|
type: "diagnostic";
|
|
200
|
-
|
|
203
|
+
kind: PlayError["kind"];
|
|
204
|
+
node: string;
|
|
205
|
+
source?: string;
|
|
201
206
|
message: string;
|
|
202
207
|
};
|
|
208
|
+
/**
|
|
209
|
+
* A content error the engine played through. Content can fail at run time in ways the compiler cannot
|
|
210
|
+
* see: a division by zero, a host value of the wrong type, a story write to a read-only `@world` value.
|
|
211
|
+
* The story never stops for one. A condition that fails counts as false; an effect that fails is skipped
|
|
212
|
+
* and the rest of its list still runs; a part of a Best-match condition that fails scores as false. Each
|
|
213
|
+
* is reported, through `EngineOptions.onError` and as a `diagnostic` entry in the decision log. The same
|
|
214
|
+
* rule holds on all four runtimes, including the two whose languages cannot throw.
|
|
215
|
+
*/
|
|
216
|
+
interface PlayError {
|
|
217
|
+
/** The flow it happened in. */
|
|
218
|
+
flow: string;
|
|
219
|
+
/** What failed: a condition, an effect, or a part of a condition being scored for Best match. */
|
|
220
|
+
kind: "condition" | "effect" | "best-match";
|
|
221
|
+
/** The snippet, group, or option whose condition failed, or that owns the effect (a scene, for its onEntry). */
|
|
222
|
+
node: string;
|
|
223
|
+
/** The expression's source text, when the bundle carries it. */
|
|
224
|
+
source?: string;
|
|
225
|
+
message: string;
|
|
226
|
+
}
|
|
227
|
+
/** @deprecated Nothing takes one: a flow has no tap of its own, and `onTrace` takes an
|
|
228
|
+
* {@link EngineTraceHandler}. Goes in a later release. */
|
|
203
229
|
type TraceHandler = (event: TraceEvent) => void;
|
|
204
230
|
/** The engine-level tap: every flow's events, tagged with the flow id - one stream for tools. */
|
|
205
231
|
type EngineTraceHandler = (flow: string, event: TraceEvent) => void;
|
|
@@ -227,14 +253,18 @@ interface EngineOptions {
|
|
|
227
253
|
/** Active locale for string lookups (embedded localisation). Defaults to the bundle's default locale.
|
|
228
254
|
* Ignored by an "ids" bundle, which emits beat IDs for the game to localise itself. */
|
|
229
255
|
locale?: string;
|
|
230
|
-
/** The host
|
|
231
|
-
* reads
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
|
|
256
|
+
/** The host scopes the game binds, by token: for each, a resolver over values the game owns and the
|
|
257
|
+
* story reads, such as `{ world: { get, set } }` for **World Properties** (`@world.*`). A declared
|
|
258
|
+
* scope left unbound is self-backed from its defaults, as a property the registry stores and saves.
|
|
259
|
+
* Shared by all flows. A game running several engines registers its scopes in its registry itself
|
|
260
|
+
* instead, and binds none here. The same option on every runtime. */
|
|
261
|
+
hostScopes?: Record<string, HostScope>;
|
|
262
|
+
/** @deprecated Use `hostScopes: { world }`, the option every Patterplay runtime takes. Goes in a later
|
|
263
|
+
* release. */
|
|
264
|
+
world?: HostScope;
|
|
235
265
|
/** The game's registry: ONE per game, holding every engine's properties except those the game keeps
|
|
236
266
|
* itself, saved once. Given one, the engine registers its own scopes in it (`@patter` under `patter`,
|
|
237
|
-
* its per-flow and per-scene bags under keys starting `patter/`, and
|
|
267
|
+
* its per-flow and per-scene bags under keys starting `patter/`, and each scope `hostScopes` binds),
|
|
238
268
|
* reads every other scope from it, and `saveGame()` leaves the property values to the game. Host
|
|
239
269
|
* scopes the bundle declares (`@world`) are then the game's to register: owned if the registry should
|
|
240
270
|
* store them, foreign if the game keeps them. Omit it and the engine makes its own registry and acts as
|
|
@@ -256,6 +286,10 @@ interface EngineOptions {
|
|
|
256
286
|
* unchanged; this only makes the fall-through observable. The coverage harness uses it to flag choices
|
|
257
287
|
* that ran dry. Leave it unset in shipped games (zero cost). */
|
|
258
288
|
onDryChoice?: (groupId: string) => void;
|
|
289
|
+
/** Called with each content error the engine played through (see {@link PlayError}): a condition
|
|
290
|
+
* that failed and counted as false, an effect that failed and was skipped. Unset, each is written
|
|
291
|
+
* to `console.warn`, so a content bug is never silent. */
|
|
292
|
+
onError?: (error: PlayError) => void;
|
|
259
293
|
/** Retain a trace of the engine's DECISIONS (what it chose and why), readable through
|
|
260
294
|
* `engine.log()` and `flow.log()`. Off by default: a shipped game pays nothing for a
|
|
261
295
|
* debugging surface it never reads. `onDryChoice` above is unaffected and stays useful
|
|
@@ -298,8 +332,13 @@ declare class Checkpoint {
|
|
|
298
332
|
}
|
|
299
333
|
/** Shared, read-mostly context the engine hands to every flow it owns. */
|
|
300
334
|
interface FlowHost {
|
|
301
|
-
/** True when the run asked for a log
|
|
335
|
+
/** True when the run asked for a log. */
|
|
302
336
|
logEnabled: boolean;
|
|
337
|
+
/** Each declaration set's quality ladders, built on first use (see `ladders`). */
|
|
338
|
+
qualityLadders: Map<string, Map<string, readonly string[]>>;
|
|
339
|
+
/** True when anything takes the decisions: the log, or an {@link Engine.onTrace} handler. Flows skip
|
|
340
|
+
* the work only a decision needs (a `run` walk's entry, a write's previous value) otherwise. */
|
|
341
|
+
tracing: boolean;
|
|
303
342
|
/** A flow's events reach the ENGINE's stream through here, tagged with the flow id. */
|
|
304
343
|
emitEngine: (flow: string, event: TraceEvent, scene?: string) => void;
|
|
305
344
|
bundle: Bundle;
|
|
@@ -365,6 +404,8 @@ interface FlowHost {
|
|
|
365
404
|
* fallback - so it falls through and the flow continues past it. Zero cost when unset; the coverage
|
|
366
405
|
* harness passes it to surface silent fall-throughs. Not a gameplay signal (the behaviour is unchanged). */
|
|
367
406
|
onDryChoice?: (groupId: string) => void;
|
|
407
|
+
/** Where a content error the engine played through is reported (see PlayError). */
|
|
408
|
+
onError: (error: PlayError) => void;
|
|
368
409
|
/** Memoised `splitRef` results (ref string -> {scope,name}). The split depends only on the registry's
|
|
369
410
|
* scope set, so the cache is dropped whenever that moves (`refSplitRevision`). */
|
|
370
411
|
refSplitCache: Map<string, {
|
|
@@ -391,11 +432,13 @@ declare class Engine {
|
|
|
391
432
|
private readonly sceneGameIdToId;
|
|
392
433
|
private readonly blockGameIdToId;
|
|
393
434
|
/** The options this engine was built with - reused verbatim by `hotSwap` so the replacement
|
|
394
|
-
* engine keeps the same
|
|
435
|
+
* engine keeps the same host scopes, custom RNG, and diagnostic hooks. */
|
|
395
436
|
private readonly creationOptions;
|
|
396
437
|
/** The run's ordered stream: every flow's events, each naming its flow. Empty and
|
|
397
438
|
* unwritten unless `options.log` asked for it. */
|
|
398
439
|
private readonly engineLog;
|
|
440
|
+
/** The next engine-log `seq`: its own counter, since the log's length restarts after a clear. */
|
|
441
|
+
private engineSeq;
|
|
399
442
|
private readonly engineTraceHandlers;
|
|
400
443
|
constructor(bundle: Bundle, options?: EngineOptions);
|
|
401
444
|
/** The active locale (string + character-name lookups resolve in it). */
|
|
@@ -403,6 +446,9 @@ declare class Engine {
|
|
|
403
446
|
/** True for a source-only DEBUG build: the embedded strings are the source language (for debugging),
|
|
404
447
|
* not a shippable localised build. An IDs-only ship build is `false`. */
|
|
405
448
|
get isSourceDebug(): boolean;
|
|
449
|
+
/** The bundle's build identity (its content hash), the one a debug link handshakes with, or an empty
|
|
450
|
+
* string for a bundle compiled without one. */
|
|
451
|
+
get buildId(): string;
|
|
406
452
|
/**
|
|
407
453
|
* Switch the active locale LIVE - a real game's "language" setting can change mid-session. Subsequent
|
|
408
454
|
* string lookups (new beats, re-resolved character names, `{@ref}` interpolation) render in the new
|
|
@@ -501,9 +547,12 @@ declare class Engine {
|
|
|
501
547
|
* is an internal id from any scene. Neither: the bundle's first scene. The same rule on every runtime.
|
|
502
548
|
*/
|
|
503
549
|
private resolveOpenAddress;
|
|
504
|
-
/** Resolve a scene reference
|
|
550
|
+
/** Resolve a scene reference to its internal id by the one address rule (see resolveScene); an
|
|
551
|
+
* unknown one passes through, for the caller to find nothing. */
|
|
505
552
|
private resolveSceneRef;
|
|
506
|
-
/** Resolve a block reference
|
|
553
|
+
/** Resolve a block reference to its internal id by the one address rule (see resolveBlock): within its
|
|
554
|
+
* scene, so a block of another scene resolves to nothing. With no scene, only an internal id resolves,
|
|
555
|
+
* since a block's gameId is only unique within its scene. */
|
|
507
556
|
private resolveBlockRef;
|
|
508
557
|
/** The host-facing address (gameId) of a scene / block by internal id, or undefined if unknown.
|
|
509
558
|
* The inverse of the resolve helpers - for a host that wants to display / log the address. */
|
|
@@ -644,6 +693,9 @@ declare class Engine {
|
|
|
644
693
|
* retained. Returns its own unsubscribe. */
|
|
645
694
|
onTrace(handler: EngineTraceHandler): () => void;
|
|
646
695
|
private emitEngine;
|
|
696
|
+
/** How many times each node has been entered across every flow, by node id: the shared count. With a
|
|
697
|
+
* flow's own {@link Flow.getVisitCounts}, every visit count the run keeps, read without a save. */
|
|
698
|
+
getVisitCounts(): Record<string, number>;
|
|
647
699
|
listProperties(): PropertyRow[];
|
|
648
700
|
private splitShared;
|
|
649
701
|
/** The shared `@patter` values alone, as `{ patter: {...} }`.
|
|
@@ -711,15 +763,20 @@ declare class Flow {
|
|
|
711
763
|
/** The stage ladder of `@scope.name` when it is a declared quality, else undefined. Names compare
|
|
712
764
|
* lowercase, as the compiler emits references (the selfBackedResolver lesson). */
|
|
713
765
|
private stagesFor;
|
|
714
|
-
/** Begin this flow at a scene (and optionally a specific block within it).
|
|
715
|
-
|
|
766
|
+
/** Begin this flow at a scene (and optionally a specific block within it). The engine's own entry point:
|
|
767
|
+
* openFlow and a goto on an unstarted flow begin a flow here. A game calls {@link reset}.
|
|
768
|
+
* @internal */
|
|
769
|
+
begin(sceneId?: string, blockId?: string): void;
|
|
716
770
|
/**
|
|
717
771
|
* Forget everything in this flow and begin again - its per-flow state (not-shared
|
|
718
772
|
* `@patter` globals + `@scene` props), cursor, callstack, selector cursors, and
|
|
719
773
|
* visit counts. Shared state (shared `@patter` / `@scene`, world visit counts) is
|
|
720
|
-
* untouched.
|
|
774
|
+
* untouched. The one public way to begin a flow again (openFlow begins a new one).
|
|
721
775
|
*/
|
|
722
776
|
reset(sceneId?: string, blockId?: string): void;
|
|
777
|
+
/** @deprecated Use {@link reset}, the one public name for beginning a flow again on every runtime. `start`
|
|
778
|
+
* was the same call under a second name, and goes in a later release. */
|
|
779
|
+
start(sceneId?: string, blockId?: string): void;
|
|
723
780
|
/**
|
|
724
781
|
* Send this flow's cursor to an ADDRESS, exactly as an authored `go` jump would: the target scene's
|
|
725
782
|
* `onEntry` effects run, entering counts as a visit, and the callstack is REPLACED - any pending
|
|
@@ -749,6 +806,13 @@ declare class Flow {
|
|
|
749
806
|
* cannot quietly mutate the world. Terminal: unlike ending, a close is never revived.
|
|
750
807
|
*/
|
|
751
808
|
close(): void;
|
|
809
|
+
/**
|
|
810
|
+
* Drop everything waiting to be delivered: an open choice, and a chosen option's prompt still to be
|
|
811
|
+
* replayed. Every move that abandons the flow's place (start and reset, goto, close) does this, so none
|
|
812
|
+
* leaves a stale prompt behind. start() cleared only the choice, so a reset between choose() and the
|
|
813
|
+
* next advance() replayed the abandoned run's prompt as the new run's first beat.
|
|
814
|
+
*/
|
|
815
|
+
private clearPending;
|
|
752
816
|
/** Inside a checkpoint, the first change to this flow records its cursor and PRNG, so a rollback can
|
|
753
817
|
* put them back: a handful of fields and a copy of the call stack (a few frames), never its history.
|
|
754
818
|
* Its visits, selector cursors, and property values are recorded change by change as they happen. */
|
|
@@ -787,9 +851,11 @@ declare class Flow {
|
|
|
787
851
|
log(): readonly LogEntry[];
|
|
788
852
|
/** Drop the retained entries. `seq` keeps counting, so order survives a clear. */
|
|
789
853
|
clearLog(): void;
|
|
790
|
-
/** Record one decision, on this flow's log and the engine's
|
|
791
|
-
*
|
|
854
|
+
/** Record one decision, on this flow's log and the engine's, and hand it to any `onTrace`. Each call
|
|
855
|
+
* site checks `host.tracing` first, so with nothing logging or tracing the entry is never built. */
|
|
792
856
|
private emit;
|
|
857
|
+
/** How many times this flow has entered each node, by node id. */
|
|
858
|
+
getVisitCounts(): Record<string, number>;
|
|
793
859
|
/** The options of a pending choice (empty when not at a choice point). */
|
|
794
860
|
getChoices(): ChoiceOption[];
|
|
795
861
|
/** Pick an eligible option by id; the next `advance()` runs it. */
|
|
@@ -870,8 +936,12 @@ declare class Flow {
|
|
|
870
936
|
private matchedSpec;
|
|
871
937
|
/** A selector's cursor state - shared across flows (`group.shared`) or this flow's own. */
|
|
872
938
|
private selectorState;
|
|
939
|
+
/** Run an effect list. `owner` is the snippet or scene it belongs to, for an error report. */
|
|
873
940
|
private runEffects;
|
|
941
|
+
/** Whether a node's condition holds. A condition that fails to evaluate counts as false (see PlayError). */
|
|
874
942
|
private eligible;
|
|
943
|
+
/** Report a content error the engine is playing through: to the host's onError, and to the log. */
|
|
944
|
+
private reportError;
|
|
875
945
|
private evalExpr;
|
|
876
946
|
/** The deserialised (in-memory) AST for an expression, cached per Expression. Shared by the
|
|
877
947
|
* evaluator and the Best-match specificity walker so both work off one parse. */
|
|
@@ -1095,11 +1165,11 @@ declare function gameDataValue(fields: GameDataField[], node: GameData | undefin
|
|
|
1095
1165
|
declare function effectiveGameData(fields: GameDataField[], node: GameData | undefined): GameData;
|
|
1096
1166
|
|
|
1097
1167
|
/**
|
|
1098
|
-
* Map every node id (scene / block / group / snippet / beat) to its accumulated
|
|
1168
|
+
* Map every node id (scene / block / group / snippet / beat, prompt beats included) to its accumulated
|
|
1099
1169
|
* tags. Node ids are globally unique within a project (the validator enforces
|
|
1100
1170
|
* it), so one flat map suffices. Nodes with no tags anywhere up the chain map to
|
|
1101
1171
|
* an empty array.
|
|
1102
1172
|
*/
|
|
1103
1173
|
declare function buildTagIndex(bundle: Bundle): Map<string, string[]>;
|
|
1104
1174
|
|
|
1105
|
-
export { type AddressSummary, type AdvanceToStopResult, type BeatInfo, type BundleCounts, type BundleDescription, type BundleIdentity, Checkpoint, type ChoiceOption, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, type FlatBeat, Flow, type GameDataFieldSummary, type GameDataSummary, type HostScopeSummary, type LogEntry, type OpenFlowOptions, type OutlineBlock, type OutlineNode, type OutlineScene, type OwnedProperties, type PropertySummary, type StepResult, type TraceEvent, type TraceHandler, type WorldResolver, buildTagIndex, describeBundle, effectiveGameData, gameDataFields, gameDataValue };
|
|
1175
|
+
export { type AddressSummary, type AdvanceToStopResult, type BeatInfo, type BundleCounts, type BundleDescription, type BundleIdentity, Checkpoint, type ChoiceOption, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, type FlatBeat, Flow, type GameDataFieldSummary, type GameDataSummary, type HostScope, type HostScopeSummary, type LogEntry, type OpenFlowOptions, type OutlineBlock, type OutlineNode, type OutlineScene, type OwnedProperties, type PlayError, type PropertySummary, type StepResult, type TraceEvent, type TraceHandler, type WorldResolver, buildTagIndex, describeBundle, effectiveGameData, gameDataFields, gameDataValue };
|
package/dist/index.d.ts
CHANGED
|
@@ -127,12 +127,14 @@ interface ChoiceOption {
|
|
|
127
127
|
gameData?: GameData;
|
|
128
128
|
}
|
|
129
129
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* comes from the compiled bundle's
|
|
130
|
+
* A host scope's resolver: a `{ get, set? }` the game provides so the story can read (and, if you allow
|
|
131
|
+
* it, write) values the game owns, `@world.*` among them, at runtime. Property metadata (types,
|
|
132
|
+
* read-only) comes from the compiled bundle's declarations; the values themselves live in the host and
|
|
133
133
|
* are never stored or saved by this engine.
|
|
134
134
|
*/
|
|
135
|
-
type
|
|
135
|
+
type HostScope = ScopeResolver;
|
|
136
|
+
/** @deprecated Use {@link HostScope}, the name every Patterplay runtime uses. */
|
|
137
|
+
type WorldResolver = HostScope;
|
|
136
138
|
/** What the engine DECIDED, as opposed to what it produced. A step tells you the line that
|
|
137
139
|
* played; a trace event tells you why that line and not its siblings - which children were
|
|
138
140
|
* eligible, where the selector's cursor stood, which choice options were live.
|
|
@@ -194,12 +196,36 @@ type TraceEvent = {
|
|
|
194
196
|
value: ScalarValue;
|
|
195
197
|
prev?: ScalarValue;
|
|
196
198
|
}
|
|
197
|
-
/**
|
|
199
|
+
/** Content the engine could not evaluate and played through (see {@link PlayError}): a condition
|
|
200
|
+
* counted as false, an effect skipped, or a Best-match part scored as false. */
|
|
198
201
|
| {
|
|
199
202
|
type: "diagnostic";
|
|
200
|
-
|
|
203
|
+
kind: PlayError["kind"];
|
|
204
|
+
node: string;
|
|
205
|
+
source?: string;
|
|
201
206
|
message: string;
|
|
202
207
|
};
|
|
208
|
+
/**
|
|
209
|
+
* A content error the engine played through. Content can fail at run time in ways the compiler cannot
|
|
210
|
+
* see: a division by zero, a host value of the wrong type, a story write to a read-only `@world` value.
|
|
211
|
+
* The story never stops for one. A condition that fails counts as false; an effect that fails is skipped
|
|
212
|
+
* and the rest of its list still runs; a part of a Best-match condition that fails scores as false. Each
|
|
213
|
+
* is reported, through `EngineOptions.onError` and as a `diagnostic` entry in the decision log. The same
|
|
214
|
+
* rule holds on all four runtimes, including the two whose languages cannot throw.
|
|
215
|
+
*/
|
|
216
|
+
interface PlayError {
|
|
217
|
+
/** The flow it happened in. */
|
|
218
|
+
flow: string;
|
|
219
|
+
/** What failed: a condition, an effect, or a part of a condition being scored for Best match. */
|
|
220
|
+
kind: "condition" | "effect" | "best-match";
|
|
221
|
+
/** The snippet, group, or option whose condition failed, or that owns the effect (a scene, for its onEntry). */
|
|
222
|
+
node: string;
|
|
223
|
+
/** The expression's source text, when the bundle carries it. */
|
|
224
|
+
source?: string;
|
|
225
|
+
message: string;
|
|
226
|
+
}
|
|
227
|
+
/** @deprecated Nothing takes one: a flow has no tap of its own, and `onTrace` takes an
|
|
228
|
+
* {@link EngineTraceHandler}. Goes in a later release. */
|
|
203
229
|
type TraceHandler = (event: TraceEvent) => void;
|
|
204
230
|
/** The engine-level tap: every flow's events, tagged with the flow id - one stream for tools. */
|
|
205
231
|
type EngineTraceHandler = (flow: string, event: TraceEvent) => void;
|
|
@@ -227,14 +253,18 @@ interface EngineOptions {
|
|
|
227
253
|
/** Active locale for string lookups (embedded localisation). Defaults to the bundle's default locale.
|
|
228
254
|
* Ignored by an "ids" bundle, which emits beat IDs for the game to localise itself. */
|
|
229
255
|
locale?: string;
|
|
230
|
-
/** The host
|
|
231
|
-
* reads
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
|
|
256
|
+
/** The host scopes the game binds, by token: for each, a resolver over values the game owns and the
|
|
257
|
+
* story reads, such as `{ world: { get, set } }` for **World Properties** (`@world.*`). A declared
|
|
258
|
+
* scope left unbound is self-backed from its defaults, as a property the registry stores and saves.
|
|
259
|
+
* Shared by all flows. A game running several engines registers its scopes in its registry itself
|
|
260
|
+
* instead, and binds none here. The same option on every runtime. */
|
|
261
|
+
hostScopes?: Record<string, HostScope>;
|
|
262
|
+
/** @deprecated Use `hostScopes: { world }`, the option every Patterplay runtime takes. Goes in a later
|
|
263
|
+
* release. */
|
|
264
|
+
world?: HostScope;
|
|
235
265
|
/** The game's registry: ONE per game, holding every engine's properties except those the game keeps
|
|
236
266
|
* itself, saved once. Given one, the engine registers its own scopes in it (`@patter` under `patter`,
|
|
237
|
-
* its per-flow and per-scene bags under keys starting `patter/`, and
|
|
267
|
+
* its per-flow and per-scene bags under keys starting `patter/`, and each scope `hostScopes` binds),
|
|
238
268
|
* reads every other scope from it, and `saveGame()` leaves the property values to the game. Host
|
|
239
269
|
* scopes the bundle declares (`@world`) are then the game's to register: owned if the registry should
|
|
240
270
|
* store them, foreign if the game keeps them. Omit it and the engine makes its own registry and acts as
|
|
@@ -256,6 +286,10 @@ interface EngineOptions {
|
|
|
256
286
|
* unchanged; this only makes the fall-through observable. The coverage harness uses it to flag choices
|
|
257
287
|
* that ran dry. Leave it unset in shipped games (zero cost). */
|
|
258
288
|
onDryChoice?: (groupId: string) => void;
|
|
289
|
+
/** Called with each content error the engine played through (see {@link PlayError}): a condition
|
|
290
|
+
* that failed and counted as false, an effect that failed and was skipped. Unset, each is written
|
|
291
|
+
* to `console.warn`, so a content bug is never silent. */
|
|
292
|
+
onError?: (error: PlayError) => void;
|
|
259
293
|
/** Retain a trace of the engine's DECISIONS (what it chose and why), readable through
|
|
260
294
|
* `engine.log()` and `flow.log()`. Off by default: a shipped game pays nothing for a
|
|
261
295
|
* debugging surface it never reads. `onDryChoice` above is unaffected and stays useful
|
|
@@ -298,8 +332,13 @@ declare class Checkpoint {
|
|
|
298
332
|
}
|
|
299
333
|
/** Shared, read-mostly context the engine hands to every flow it owns. */
|
|
300
334
|
interface FlowHost {
|
|
301
|
-
/** True when the run asked for a log
|
|
335
|
+
/** True when the run asked for a log. */
|
|
302
336
|
logEnabled: boolean;
|
|
337
|
+
/** Each declaration set's quality ladders, built on first use (see `ladders`). */
|
|
338
|
+
qualityLadders: Map<string, Map<string, readonly string[]>>;
|
|
339
|
+
/** True when anything takes the decisions: the log, or an {@link Engine.onTrace} handler. Flows skip
|
|
340
|
+
* the work only a decision needs (a `run` walk's entry, a write's previous value) otherwise. */
|
|
341
|
+
tracing: boolean;
|
|
303
342
|
/** A flow's events reach the ENGINE's stream through here, tagged with the flow id. */
|
|
304
343
|
emitEngine: (flow: string, event: TraceEvent, scene?: string) => void;
|
|
305
344
|
bundle: Bundle;
|
|
@@ -365,6 +404,8 @@ interface FlowHost {
|
|
|
365
404
|
* fallback - so it falls through and the flow continues past it. Zero cost when unset; the coverage
|
|
366
405
|
* harness passes it to surface silent fall-throughs. Not a gameplay signal (the behaviour is unchanged). */
|
|
367
406
|
onDryChoice?: (groupId: string) => void;
|
|
407
|
+
/** Where a content error the engine played through is reported (see PlayError). */
|
|
408
|
+
onError: (error: PlayError) => void;
|
|
368
409
|
/** Memoised `splitRef` results (ref string -> {scope,name}). The split depends only on the registry's
|
|
369
410
|
* scope set, so the cache is dropped whenever that moves (`refSplitRevision`). */
|
|
370
411
|
refSplitCache: Map<string, {
|
|
@@ -391,11 +432,13 @@ declare class Engine {
|
|
|
391
432
|
private readonly sceneGameIdToId;
|
|
392
433
|
private readonly blockGameIdToId;
|
|
393
434
|
/** The options this engine was built with - reused verbatim by `hotSwap` so the replacement
|
|
394
|
-
* engine keeps the same
|
|
435
|
+
* engine keeps the same host scopes, custom RNG, and diagnostic hooks. */
|
|
395
436
|
private readonly creationOptions;
|
|
396
437
|
/** The run's ordered stream: every flow's events, each naming its flow. Empty and
|
|
397
438
|
* unwritten unless `options.log` asked for it. */
|
|
398
439
|
private readonly engineLog;
|
|
440
|
+
/** The next engine-log `seq`: its own counter, since the log's length restarts after a clear. */
|
|
441
|
+
private engineSeq;
|
|
399
442
|
private readonly engineTraceHandlers;
|
|
400
443
|
constructor(bundle: Bundle, options?: EngineOptions);
|
|
401
444
|
/** The active locale (string + character-name lookups resolve in it). */
|
|
@@ -403,6 +446,9 @@ declare class Engine {
|
|
|
403
446
|
/** True for a source-only DEBUG build: the embedded strings are the source language (for debugging),
|
|
404
447
|
* not a shippable localised build. An IDs-only ship build is `false`. */
|
|
405
448
|
get isSourceDebug(): boolean;
|
|
449
|
+
/** The bundle's build identity (its content hash), the one a debug link handshakes with, or an empty
|
|
450
|
+
* string for a bundle compiled without one. */
|
|
451
|
+
get buildId(): string;
|
|
406
452
|
/**
|
|
407
453
|
* Switch the active locale LIVE - a real game's "language" setting can change mid-session. Subsequent
|
|
408
454
|
* string lookups (new beats, re-resolved character names, `{@ref}` interpolation) render in the new
|
|
@@ -501,9 +547,12 @@ declare class Engine {
|
|
|
501
547
|
* is an internal id from any scene. Neither: the bundle's first scene. The same rule on every runtime.
|
|
502
548
|
*/
|
|
503
549
|
private resolveOpenAddress;
|
|
504
|
-
/** Resolve a scene reference
|
|
550
|
+
/** Resolve a scene reference to its internal id by the one address rule (see resolveScene); an
|
|
551
|
+
* unknown one passes through, for the caller to find nothing. */
|
|
505
552
|
private resolveSceneRef;
|
|
506
|
-
/** Resolve a block reference
|
|
553
|
+
/** Resolve a block reference to its internal id by the one address rule (see resolveBlock): within its
|
|
554
|
+
* scene, so a block of another scene resolves to nothing. With no scene, only an internal id resolves,
|
|
555
|
+
* since a block's gameId is only unique within its scene. */
|
|
507
556
|
private resolveBlockRef;
|
|
508
557
|
/** The host-facing address (gameId) of a scene / block by internal id, or undefined if unknown.
|
|
509
558
|
* The inverse of the resolve helpers - for a host that wants to display / log the address. */
|
|
@@ -644,6 +693,9 @@ declare class Engine {
|
|
|
644
693
|
* retained. Returns its own unsubscribe. */
|
|
645
694
|
onTrace(handler: EngineTraceHandler): () => void;
|
|
646
695
|
private emitEngine;
|
|
696
|
+
/** How many times each node has been entered across every flow, by node id: the shared count. With a
|
|
697
|
+
* flow's own {@link Flow.getVisitCounts}, every visit count the run keeps, read without a save. */
|
|
698
|
+
getVisitCounts(): Record<string, number>;
|
|
647
699
|
listProperties(): PropertyRow[];
|
|
648
700
|
private splitShared;
|
|
649
701
|
/** The shared `@patter` values alone, as `{ patter: {...} }`.
|
|
@@ -711,15 +763,20 @@ declare class Flow {
|
|
|
711
763
|
/** The stage ladder of `@scope.name` when it is a declared quality, else undefined. Names compare
|
|
712
764
|
* lowercase, as the compiler emits references (the selfBackedResolver lesson). */
|
|
713
765
|
private stagesFor;
|
|
714
|
-
/** Begin this flow at a scene (and optionally a specific block within it).
|
|
715
|
-
|
|
766
|
+
/** Begin this flow at a scene (and optionally a specific block within it). The engine's own entry point:
|
|
767
|
+
* openFlow and a goto on an unstarted flow begin a flow here. A game calls {@link reset}.
|
|
768
|
+
* @internal */
|
|
769
|
+
begin(sceneId?: string, blockId?: string): void;
|
|
716
770
|
/**
|
|
717
771
|
* Forget everything in this flow and begin again - its per-flow state (not-shared
|
|
718
772
|
* `@patter` globals + `@scene` props), cursor, callstack, selector cursors, and
|
|
719
773
|
* visit counts. Shared state (shared `@patter` / `@scene`, world visit counts) is
|
|
720
|
-
* untouched.
|
|
774
|
+
* untouched. The one public way to begin a flow again (openFlow begins a new one).
|
|
721
775
|
*/
|
|
722
776
|
reset(sceneId?: string, blockId?: string): void;
|
|
777
|
+
/** @deprecated Use {@link reset}, the one public name for beginning a flow again on every runtime. `start`
|
|
778
|
+
* was the same call under a second name, and goes in a later release. */
|
|
779
|
+
start(sceneId?: string, blockId?: string): void;
|
|
723
780
|
/**
|
|
724
781
|
* Send this flow's cursor to an ADDRESS, exactly as an authored `go` jump would: the target scene's
|
|
725
782
|
* `onEntry` effects run, entering counts as a visit, and the callstack is REPLACED - any pending
|
|
@@ -749,6 +806,13 @@ declare class Flow {
|
|
|
749
806
|
* cannot quietly mutate the world. Terminal: unlike ending, a close is never revived.
|
|
750
807
|
*/
|
|
751
808
|
close(): void;
|
|
809
|
+
/**
|
|
810
|
+
* Drop everything waiting to be delivered: an open choice, and a chosen option's prompt still to be
|
|
811
|
+
* replayed. Every move that abandons the flow's place (start and reset, goto, close) does this, so none
|
|
812
|
+
* leaves a stale prompt behind. start() cleared only the choice, so a reset between choose() and the
|
|
813
|
+
* next advance() replayed the abandoned run's prompt as the new run's first beat.
|
|
814
|
+
*/
|
|
815
|
+
private clearPending;
|
|
752
816
|
/** Inside a checkpoint, the first change to this flow records its cursor and PRNG, so a rollback can
|
|
753
817
|
* put them back: a handful of fields and a copy of the call stack (a few frames), never its history.
|
|
754
818
|
* Its visits, selector cursors, and property values are recorded change by change as they happen. */
|
|
@@ -787,9 +851,11 @@ declare class Flow {
|
|
|
787
851
|
log(): readonly LogEntry[];
|
|
788
852
|
/** Drop the retained entries. `seq` keeps counting, so order survives a clear. */
|
|
789
853
|
clearLog(): void;
|
|
790
|
-
/** Record one decision, on this flow's log and the engine's
|
|
791
|
-
*
|
|
854
|
+
/** Record one decision, on this flow's log and the engine's, and hand it to any `onTrace`. Each call
|
|
855
|
+
* site checks `host.tracing` first, so with nothing logging or tracing the entry is never built. */
|
|
792
856
|
private emit;
|
|
857
|
+
/** How many times this flow has entered each node, by node id. */
|
|
858
|
+
getVisitCounts(): Record<string, number>;
|
|
793
859
|
/** The options of a pending choice (empty when not at a choice point). */
|
|
794
860
|
getChoices(): ChoiceOption[];
|
|
795
861
|
/** Pick an eligible option by id; the next `advance()` runs it. */
|
|
@@ -870,8 +936,12 @@ declare class Flow {
|
|
|
870
936
|
private matchedSpec;
|
|
871
937
|
/** A selector's cursor state - shared across flows (`group.shared`) or this flow's own. */
|
|
872
938
|
private selectorState;
|
|
939
|
+
/** Run an effect list. `owner` is the snippet or scene it belongs to, for an error report. */
|
|
873
940
|
private runEffects;
|
|
941
|
+
/** Whether a node's condition holds. A condition that fails to evaluate counts as false (see PlayError). */
|
|
874
942
|
private eligible;
|
|
943
|
+
/** Report a content error the engine is playing through: to the host's onError, and to the log. */
|
|
944
|
+
private reportError;
|
|
875
945
|
private evalExpr;
|
|
876
946
|
/** The deserialised (in-memory) AST for an expression, cached per Expression. Shared by the
|
|
877
947
|
* evaluator and the Best-match specificity walker so both work off one parse. */
|
|
@@ -1095,11 +1165,11 @@ declare function gameDataValue(fields: GameDataField[], node: GameData | undefin
|
|
|
1095
1165
|
declare function effectiveGameData(fields: GameDataField[], node: GameData | undefined): GameData;
|
|
1096
1166
|
|
|
1097
1167
|
/**
|
|
1098
|
-
* Map every node id (scene / block / group / snippet / beat) to its accumulated
|
|
1168
|
+
* Map every node id (scene / block / group / snippet / beat, prompt beats included) to its accumulated
|
|
1099
1169
|
* tags. Node ids are globally unique within a project (the validator enforces
|
|
1100
1170
|
* it), so one flat map suffices. Nodes with no tags anywhere up the chain map to
|
|
1101
1171
|
* an empty array.
|
|
1102
1172
|
*/
|
|
1103
1173
|
declare function buildTagIndex(bundle: Bundle): Map<string, string[]>;
|
|
1104
1174
|
|
|
1105
|
-
export { type AddressSummary, type AdvanceToStopResult, type BeatInfo, type BundleCounts, type BundleDescription, type BundleIdentity, Checkpoint, type ChoiceOption, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, type FlatBeat, Flow, type GameDataFieldSummary, type GameDataSummary, type HostScopeSummary, type LogEntry, type OpenFlowOptions, type OutlineBlock, type OutlineNode, type OutlineScene, type OwnedProperties, type PropertySummary, type StepResult, type TraceEvent, type TraceHandler, type WorldResolver, buildTagIndex, describeBundle, effectiveGameData, gameDataFields, gameDataValue };
|
|
1175
|
+
export { type AddressSummary, type AdvanceToStopResult, type BeatInfo, type BundleCounts, type BundleDescription, type BundleIdentity, Checkpoint, type ChoiceOption, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, type FlatBeat, Flow, type GameDataFieldSummary, type GameDataSummary, type HostScope, type HostScopeSummary, type LogEntry, type OpenFlowOptions, type OutlineBlock, type OutlineNode, type OutlineScene, type OwnedProperties, type PlayError, type PropertySummary, type StepResult, type TraceEvent, type TraceHandler, type WorldResolver, buildTagIndex, describeBundle, effectiveGameData, gameDataFields, gameDataValue };
|