@patterkit/runtime 0.20.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 CHANGED
@@ -33,6 +33,22 @@ version number always means the same runtime behaviour. This package is versione
33
33
 
34
34
  ## [Unreleased]
35
35
 
36
+ ## [0.21.0] - 2026-10-07
37
+
38
+ ### Changed
39
+
40
+ - **A new `Engine` on a bundle another engine already uses no longer rebuilds the tag index.** It is built once
41
+ per bundle and shared, so a tool that starts a fresh engine for every run (coverage does) starts each in about
42
+ a third of the time: 0.5 ms rather than 1.6 ms on a 100-scene project.
43
+ - **The conformance corpus covers more, and every runtime is held to it**: the outline and beat sequence, the
44
+ audio resolver's joins, `listProperties`, what a checkpoint refuses while it is open, a save taken inside one,
45
+ and a block named under another scene.
46
+
47
+ ### Fixed
48
+
49
+ - **Tags handed out are the caller's own.** `tagsForBeat`, `tagsForScene`, `tagsForBlock`, and a delivered
50
+ line's `tags` are copies, so a game that edits one changes nothing the engine says afterwards.
51
+
36
52
  ## [0.20.0] - 2026-10-06
37
53
 
38
54
  ### Added
package/dist/index.cjs CHANGED
@@ -76,6 +76,18 @@ function buildTagIndex(bundle) {
76
76
  // src/engine.ts
77
77
  var import_model2 = require("@patterkit/model");
78
78
  var astCache = /* @__PURE__ */ new WeakMap();
79
+ var tagIndexCache = /* @__PURE__ */ new WeakMap();
80
+ function tagIndexFor(bundle) {
81
+ let index = tagIndexCache.get(bundle);
82
+ if (!index) {
83
+ index = buildTagIndex(bundle);
84
+ tagIndexCache.set(bundle, index);
85
+ }
86
+ return index;
87
+ }
88
+ function tagsOf(host, id) {
89
+ return host.tagIndex.get(id)?.slice();
90
+ }
79
91
  var OWNER = "Patter";
80
92
  var esc = (id) => id.replace(/%/g, "%25").replace(/\//g, "%2F");
81
93
  var OWNS_REGISTRY = /* @__PURE__ */ Symbol("ownsRegistry");
@@ -204,7 +216,7 @@ var Engine = class _Engine {
204
216
  sceneGameIdToId: this.sceneGameIdToId,
205
217
  blockGameIdToId: this.blockGameIdToId,
206
218
  // same instances the engine resolves with
207
- tagIndex: buildTagIndex(bundle),
219
+ tagIndex: tagIndexFor(bundle),
208
220
  registry,
209
221
  ownsRegistry,
210
222
  patterBag,
@@ -463,18 +475,18 @@ var Engine = class _Engine {
463
475
  * delivered step carries. Empty array for an unknown id or a beat with no tags anywhere up the chain.
464
476
  */
465
477
  tagsForBeat(beatId) {
466
- return this.host.tagIndex.get(beatId) ?? [];
478
+ return tagsOf(this.host, beatId) ?? [];
467
479
  }
468
480
  /** A scene's own tags (by internal id or gameId address). Empty when none / unknown. */
469
481
  tagsForScene(sceneRef) {
470
482
  const id = this.resolveSceneRef(sceneRef);
471
- return (id != null ? this.host.tagIndex.get(id) : void 0) ?? [];
483
+ return (id != null ? tagsOf(this.host, id) : void 0) ?? [];
472
484
  }
473
485
  /** A block's accumulated tags (scene + block), by scene + block ref (id or gameId). Empty when none / unknown. */
474
486
  tagsForBlock(sceneRef, blockRef) {
475
487
  const sceneId = this.resolveSceneRef(sceneRef);
476
488
  const id = this.resolveBlockRef(sceneId, blockRef);
477
- return (id != null ? this.host.tagIndex.get(id) : void 0) ?? [];
489
+ return (id != null ? tagsOf(this.host, id) : void 0) ?? [];
478
490
  }
479
491
  /**
480
492
  * A scene's own author gameData (by internal id or gameId address): the RAW sparse overrides, exactly
@@ -593,7 +605,7 @@ var Engine = class _Engine {
593
605
  }
594
606
  /** One beat's static data (source locale), the same shape a delivered step carries. */
595
607
  beatInfo(beat) {
596
- const tags = this.host.tagIndex.get(beat.id);
608
+ const tags = tagsOf(this.host, beat.id);
597
609
  const info = { id: beat.id, kind: beat.kind };
598
610
  if (beat.kind === "line") {
599
611
  if (beat.character !== void 0) {
@@ -613,7 +625,7 @@ var Engine = class _Engine {
613
625
  }
614
626
  /** A `{ tags }` fragment for an id, present only when the id has accumulated tags (keeps output tidy). */
615
627
  tagsField(id) {
616
- const tags = this.host.tagIndex.get(id);
628
+ const tags = tagsOf(this.host, id);
617
629
  return tags && tags.length ? { tags } : {};
618
630
  }
619
631
  /** Retrieve an open flow by id (undefined if none / closed). */
@@ -750,8 +762,6 @@ var Engine = class _Engine {
750
762
  this.host.registry.set(scope, name, value, { host: true });
751
763
  if (this.host.journal && prev !== void 0) this.host.journal.undo.push(() => this.host.registry.set(scope, name, prev, { host: true }));
752
764
  }
753
- /** The shared `@patter` properties, for a live state inspector: each with its ref, type, current
754
- * value, declared default (for reset), and enum options. Mirrors the Unity / Godot ports. */
755
765
  /** The run's decisions, in order, each naming the flow it happened in. Empty unless the
756
766
  * run was opened with `log: true`. A flow's own log stays flow-local; this is the only
757
767
  * place a story spanning several flows reads as one sequence. */
@@ -783,6 +793,8 @@ var Engine = class _Engine {
783
793
  getVisitCounts() {
784
794
  return Object.fromEntries(this.host.sharedVisits);
785
795
  }
796
+ /** The shared `@patter` properties, for a live state inspector: each with its ref, type, current
797
+ * value, declared default (for reset), and enum options. Every runtime has the same call. */
786
798
  listProperties() {
787
799
  return this.host.patterSharedDecls.map((d) => ({
788
800
  name: d.name,
@@ -1770,7 +1782,7 @@ var Flow = class {
1770
1782
  };
1771
1783
  // -- Strings / beats ------------------------------------------------------
1772
1784
  beatResult(beat) {
1773
- const tags = this.host.tagIndex.get(beat.id);
1785
+ const tags = tagsOf(this.host, beat.id);
1774
1786
  const withTags = tags && tags.length ? { tags } : {};
1775
1787
  switch (beat.kind) {
1776
1788
  case "gameEvent":
@@ -1837,7 +1849,7 @@ var Flow = class {
1837
1849
  /** A replayed prompt as a step: the beat's id, gameData, and tags, and the text and speaker fields the
1838
1850
  * choice showed. */
1839
1851
  promptResult(beat, shown) {
1840
- const tags = this.host.tagIndex.get(beat.id);
1852
+ const tags = tagsOf(this.host, beat.id);
1841
1853
  const withTags = tags && tags.length ? { tags } : {};
1842
1854
  if (shown.kind === "text") return { type: "text", id: beat.id, text: shown.text, gameData: beat.gameData, ...withTags };
1843
1855
  return {