@patterkit/runtime 0.4.0 → 0.4.1

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
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [614eaa8]
8
+ - @patterkit/model@0.3.0
9
+ - @patterkit/dialect@0.1.5
10
+
3
11
  ## 0.2.1
4
12
 
5
13
  ### Patch Changes
@@ -20,6 +28,7 @@ version number always means the same runtime behaviour. This package is versione
20
28
  ## [0.4.0] - 2026-07-30
21
29
 
22
30
  ### Added
31
+
23
32
  - **State logger** (parity: previously JS-only). Watches the mutable runtime state - `@patter`
24
33
  globals, per-scene `@scene` props, and visit counts (shared + per-flow) - and reports what changed
25
34
  between captures, plus a per-step trace including `gameData`. Built on the engine's save-game, so
@@ -27,18 +36,21 @@ version number always means the same runtime behaviour. This package is versione
27
36
  every runtime.
28
37
 
29
38
  ### Fixed
39
+
30
40
  - The download now includes the **MIT `LICENSE` file**; previously the zip shipped with no licence
31
41
  text at all.
32
42
 
33
43
  ## [0.3.1] - 2026-07-22
34
44
 
35
45
  ### Changed
46
+
36
47
  - Version bump only, to keep the four Patterplay runtimes in lockstep. This release fixes
37
48
  Unreal-only build issues (see the Unreal changelog and #25); the JavaScript runtime is unchanged.
38
49
 
39
50
  ## [0.3.0] - 2026-07-21
40
51
 
41
52
  ### Added
53
+
42
54
  - **Host navigation.** `flow.goto(scene, block?)` sends a running flow to a Game ID address, behaving
43
55
  exactly like an authored `go` jump: the target scene's `onEntry` runs, arriving counts as a visit, and
44
56
  the callstack is replaced (pending call-returns discarded). Being a host action it lands immediately -
@@ -54,6 +66,7 @@ version number always means the same runtime behaviour. This package is versione
54
66
  (they were JS-only).
55
67
 
56
68
  ### Changed
69
+
57
70
  - Dropping a flow now FINISHES it. `closeFlow`, `engine.reset()` and re-opening a name all leave the old
58
71
  `Flow` inert (`advance()` reports the end, `goto()` refuses), so a stale reference a game still holds
59
72
  can no longer keep running scene entry effects and moving shared state. Re-opening a name still
@@ -62,6 +75,7 @@ version number always means the same runtime behaviour. This package is versione
62
75
  ## [0.2.2] - 2026-07-13
63
76
 
64
77
  ### Changed
78
+
65
79
  - Internal: the Best match (`specificity`) selection metric now uses the shared
66
80
  `@wildwinter/expr-specificity` package instead of a per-engine inline copy. Behaviour is
67
81
  unchanged and conformance-verified across all four engines.
package/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  Engine: () => Engine,
24
24
  Flow: () => Flow,
25
25
  buildTagIndex: () => buildTagIndex,
26
+ describeBundle: () => describeBundle,
26
27
  effectiveGameData: () => effectiveGameData,
27
28
  gameDataFields: () => gameDataFields,
28
29
  gameDataValue: () => gameDataValue
@@ -1411,12 +1412,13 @@ function hostScopeDefault(decl) {
1411
1412
  }
1412
1413
  }
1413
1414
  function selfBackedResolver(decls) {
1415
+ const key = (name) => name.toLowerCase();
1414
1416
  const bag = /* @__PURE__ */ new Map();
1415
- for (const d of decls) bag.set(d.name, hostScopeDefault(d));
1417
+ for (const d of decls) bag.set(key(d.name), hostScopeDefault(d));
1416
1418
  return {
1417
- get: (name) => bag.get(name),
1419
+ get: (name) => bag.get(key(name)),
1418
1420
  set: (name, value) => {
1419
- bag.set(name, value);
1421
+ bag.set(key(name), value);
1420
1422
  }
1421
1423
  };
1422
1424
  }
@@ -1442,6 +1444,109 @@ function truthy(v) {
1442
1444
  return v.length > 0;
1443
1445
  }
1444
1446
 
1447
+ // src/describe.ts
1448
+ var import_model2 = require("@patterkit/model");
1449
+ var isShared = (d, scopeDefault) => d.shared ?? scopeDefault;
1450
+ function summariseProperty(d, scopeDefault) {
1451
+ return {
1452
+ name: d.name,
1453
+ type: d.type,
1454
+ hasDefault: d.default !== void 0,
1455
+ ...d.default !== void 0 ? { default: d.default } : {},
1456
+ shared: isShared(d, scopeDefault)
1457
+ };
1458
+ }
1459
+ function summariseField(f) {
1460
+ return {
1461
+ name: f.name,
1462
+ type: f.type,
1463
+ hasDefault: f.default !== void 0,
1464
+ ...f.values ? { values: [...f.values] } : {},
1465
+ ...f.purpose ? { purpose: f.purpose } : {}
1466
+ };
1467
+ }
1468
+ function countBlock(block, counts) {
1469
+ counts.blocks++;
1470
+ const stack = [...block.children];
1471
+ while (stack.length) {
1472
+ const node = stack.pop();
1473
+ if (node.type === "group") {
1474
+ counts.groups++;
1475
+ if (node.prompt) counts.prompts++;
1476
+ stack.push(...node.children);
1477
+ continue;
1478
+ }
1479
+ counts.snippets++;
1480
+ for (const beat of node.beats ?? []) {
1481
+ counts.beats++;
1482
+ if (beat.kind === "gameEvent") counts.gameEvents++;
1483
+ }
1484
+ }
1485
+ }
1486
+ function describeBundle(bundle) {
1487
+ const counts = {
1488
+ scenes: 0,
1489
+ blocks: 0,
1490
+ groups: 0,
1491
+ snippets: 0,
1492
+ beats: 0,
1493
+ prompts: 0,
1494
+ gameEvents: 0,
1495
+ cast: bundle.cast?.length ?? 0
1496
+ };
1497
+ const addresses = [];
1498
+ const sceneProps = [];
1499
+ for (const scene of Object.values(bundle.scenes)) {
1500
+ counts.scenes++;
1501
+ const gameId = (0, import_model2.effectiveGameId)(scene);
1502
+ addresses.push({
1503
+ gameId,
1504
+ name: scene.name,
1505
+ blocks: scene.blocks.map((b) => ({ gameId: (0, import_model2.effectiveGameId)(b), name: b.name }))
1506
+ });
1507
+ for (const block of scene.blocks) countBlock(block, counts);
1508
+ if (scene.sceneProps?.length) {
1509
+ sceneProps.push({ gameId, properties: scene.sceneProps.map((d) => summariseProperty(d, false)) });
1510
+ }
1511
+ }
1512
+ const hostScopes = (bundle.scopeRegistry?.scopes ?? []).map((s) => ({
1513
+ token: s.token,
1514
+ writable: s.writable ?? true,
1515
+ opaque: s.declarations === void 0,
1516
+ // A host scope's values live outside the story, so "shared" is not a choice
1517
+ // its declarations make; they are world-wide by nature.
1518
+ properties: (s.declarations ?? []).map((d) => summariseProperty(d, true))
1519
+ }));
1520
+ const gameData = Object.entries(bundle.gameDataFields ?? {}).filter(([, fields]) => (fields?.length ?? 0) > 0).map(([kind, fields]) => ({
1521
+ kind,
1522
+ fields: (fields ?? []).map(summariseField)
1523
+ }));
1524
+ return {
1525
+ identity: {
1526
+ schema: bundle.schema,
1527
+ project: bundle.content.project,
1528
+ ...bundle.content.version !== void 0 ? { version: bundle.content.version } : {},
1529
+ ...bundle.content.hash !== void 0 ? { hash: bundle.content.hash } : {},
1530
+ ...bundle.content.structureHash !== void 0 ? { structureHash: bundle.content.structureHash } : {},
1531
+ voiced: bundle.voiced,
1532
+ defaultLocale: bundle.locales.default,
1533
+ locales: [...bundle.locales.included],
1534
+ // Absent means "embedded": the back-compat default a bundle written before
1535
+ // the field existed relies on.
1536
+ localisation: bundle.localisation?.mode ?? "embedded",
1537
+ sourceDebug: bundle.localisation?.sourceDebug ?? false
1538
+ },
1539
+ addresses,
1540
+ hostScopes,
1541
+ properties: {
1542
+ patter: (bundle.properties ?? []).map((d) => summariseProperty(d, true)),
1543
+ scene: sceneProps
1544
+ },
1545
+ gameData,
1546
+ counts
1547
+ };
1548
+ }
1549
+
1445
1550
  // src/gamedata.ts
1446
1551
  function gameDataFields(bundle, kind) {
1447
1552
  return bundle.gameDataFields?.[kind] ?? [];
@@ -1464,6 +1569,7 @@ function effectiveGameData(fields, node) {
1464
1569
  Engine,
1465
1570
  Flow,
1466
1571
  buildTagIndex,
1572
+ describeBundle,
1467
1573
  effectiveGameData,
1468
1574
  gameDataFields,
1469
1575
  gameDataValue