@patterkit/runtime 0.4.5 → 0.5.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.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [f97f6eb]
8
+ - @patterkit/model@0.4.0
9
+ - @patterkit/dialect@0.1.6
10
+
3
11
  ## 0.4.1
4
12
 
5
13
  ### Patch Changes
@@ -25,6 +33,26 @@ version number always means the same runtime behaviour. This package is versione
25
33
 
26
34
  ## [Unreleased]
27
35
 
36
+ ### Added
37
+
38
+ - **The `quality` property type: a story stage as an ordered ladder of named stages.** The value is a
39
+ stage name; ordering operators compare by ladder POSITION, `advance(@q)` steps to the next stage
40
+ saturating at the last, and a save carries the stage by name - so a stage inserted mid-production
41
+ shifts nothing. Declared with `stages` on the property; seeds at the first stage. Corpus-locked
42
+ across all four runtimes (gating, stepping, and the insertion story through a live hot swap).
43
+
44
+ ## [0.5.0] - 2026-08-21
45
+
46
+ ### Added
47
+
48
+ - **Cast lists you can query at runtime.** Three static reads answer "who is in this?": the cast the
49
+ project declares, the speakers of a scene, and the speakers of one block. Scene and block refs take
50
+ an internal id or a gameId address. The result is the character token a line beat carries, deduped
51
+ and ordered by first appearance, and it is derived from the AUTHORED structure, so a speaker behind a
52
+ condition, inside a group, or voicing a choice prompt is included: it answers who _can_ speak, not
53
+ who a given playthrough heard. Held across all four runtimes by the conformance corpus.
54
+ `engine.getCast()`, `engine.castForScene(sceneRef)`, `engine.castForBlock(sceneRef, blockRef)`.
55
+
28
56
  ## [0.4.5] - 2026-08-20
29
57
 
30
58
  ### Changed
package/dist/index.cjs CHANGED
@@ -360,6 +360,42 @@ var Engine = class _Engine {
360
360
  const id = this.resolveBlockRef(sceneId, blockRef);
361
361
  return (id != null ? this.host.tagIndex.get(id) : void 0) ?? [];
362
362
  }
363
+ /**
364
+ * Every cast member the PROJECT declares, in authored order - the same list `describeBundle` counts.
365
+ * A superset of any scene's cast: the validator holds a beat's `character` to a declared member, so
366
+ * {@link castForScene} and {@link castForBlock} only ever return names that appear here.
367
+ */
368
+ getCast() {
369
+ const names = [];
370
+ for (const c of this.host.bundle.cast ?? []) if (c?.name) names.push(c.name);
371
+ return names;
372
+ }
373
+ /**
374
+ * A scene's cast: the `character` token of every speaker with a line anywhere in it, deduped, in
375
+ * first-appearance order. Static, like {@link getOutline}: it walks the authored structure, so a
376
+ * speaker behind a condition, inside any group, or voicing a choice prompt counts - this is who CAN
377
+ * speak in the scene, not who a given playthrough heard. Empty for an unknown ref, or a scene with no
378
+ * dialogue. Tokens, not display names: resolve those through the delivered step (`characterName`),
379
+ * which is what follows `setLocale`.
380
+ */
381
+ castForScene(sceneRef) {
382
+ const id = this.resolveSceneRef(sceneRef);
383
+ const scene = id != null ? this.host.bundle.scenes[id] : void 0;
384
+ if (!scene) return [];
385
+ const out = /* @__PURE__ */ new Set();
386
+ for (const block of scene.blocks) collectCast(block.children, out);
387
+ return [...out];
388
+ }
389
+ /** One block's cast, by scene + block ref (id or gameId). {@link castForScene} scoped to a block. */
390
+ castForBlock(sceneRef, blockRef) {
391
+ const sceneId = this.resolveSceneRef(sceneRef);
392
+ const id = this.resolveBlockRef(sceneId, blockRef);
393
+ const block = id != null ? this.host.blockById.get(id) : void 0;
394
+ if (!block) return [];
395
+ const out = /* @__PURE__ */ new Set();
396
+ collectCast(block.children, out);
397
+ return [...out];
398
+ }
363
399
  /**
364
400
  * The authored structure as a nested tree: scenes -> blocks -> children (groups + snippets, groups
365
401
  * preserved) -> a snippet's beats. Static (no flow / play state); per-beat data is read at the source
@@ -489,6 +525,7 @@ var Engine = class _Engine {
489
525
  ref: `@${d.name}`,
490
526
  type: d.type,
491
527
  values: d.values,
528
+ stages: d.stages,
492
529
  value: this.getProperty(`@${d.name}`),
493
530
  default: declDefault(d)
494
531
  }));
@@ -621,9 +658,28 @@ var Flow = class {
621
658
  nextRandom: this.rng,
622
659
  visits: (id2) => this.visitCounts.get(id2) ?? 0,
623
660
  patterVisits: (id2) => this.host.sharedVisits.get(id2) ?? 0
624
- }
661
+ },
662
+ // The quality channel (expr 0.4.0): hands the evaluator a property's stage ladder, which is what
663
+ // makes ordering compare by position and advance() step. Wired by hand because this context takes
664
+ // only the registry's SCOPES (the patter/scene resolvers here are the flow's own merged views),
665
+ // and because @scene declarations belong to whichever scene the flow is in RIGHT NOW.
666
+ qualities: (scope, name) => this.stagesFor(scope, name)
625
667
  };
626
668
  }
669
+ /** The stage ladder of `@scope.name` when it is a declared quality, else undefined. Names compare
670
+ * lowercase, as the compiler emits references (the selfBackedResolver lesson). */
671
+ stagesFor(scope, name) {
672
+ const key = name.toLowerCase();
673
+ const fromDecls = (decls) => decls?.find((d) => d.name.toLowerCase() === key && d.type === "quality")?.stages;
674
+ if (scope === "patter") {
675
+ return fromDecls(this.host.patterSharedDecls) ?? fromDecls(this.host.patterLocalDecls);
676
+ }
677
+ if (scope === "scene") {
678
+ const scene = this.currentSceneId != null ? this.host.bundle.scenes[this.currentSceneId] : void 0;
679
+ return fromDecls(scene?.sceneProps);
680
+ }
681
+ return fromDecls(this.host.bundle.scopeRegistry?.scopes.find((s) => s.token === scope)?.declarations);
682
+ }
627
683
  // -- Host API -------------------------------------------------------------
628
684
  /** Begin this flow at a scene (and optionally a specific block within it). */
629
685
  start(sceneId, blockId) {
@@ -1353,6 +1409,15 @@ var Flow = class {
1353
1409
  }
1354
1410
  }
1355
1411
  };
1412
+ function collectCast(nodes, out) {
1413
+ (0, import_model.walkNodes)(nodes, (n) => {
1414
+ if (n.type === "group") {
1415
+ if (n.prompt?.kind === "line" && n.prompt.character) out.add(n.prompt.character);
1416
+ return;
1417
+ }
1418
+ for (const beat of n.beats ?? []) if (beat.kind === "line" && beat.character) out.add(beat.character);
1419
+ });
1420
+ }
1356
1421
  function serialiseSelectors(map) {
1357
1422
  const out = {};
1358
1423
  for (const [id, st] of map) {
@@ -1376,7 +1441,7 @@ function deserialiseSelectors(rec) {
1376
1441
  return map;
1377
1442
  }
1378
1443
  function toDecl(decl) {
1379
- return { name: decl.name, type: decl.type, values: decl.values, default: decl.default };
1444
+ return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default };
1380
1445
  }
1381
1446
  function declDefault(d) {
1382
1447
  if (d.default !== void 0) return d.default;
@@ -1389,12 +1454,14 @@ function declDefault(d) {
1389
1454
  return [];
1390
1455
  case "enum":
1391
1456
  return d.values?.[0] ?? "";
1457
+ case "quality":
1458
+ return d.stages?.[0] ?? "";
1392
1459
  default:
1393
1460
  return false;
1394
1461
  }
1395
1462
  }
1396
1463
  function toForeignDecl(decl) {
1397
- return { name: decl.name, type: decl.type, values: decl.values, default: decl.default, writable: decl.writable };
1464
+ return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default, writable: decl.writable };
1398
1465
  }
1399
1466
  function hostScopeDefault(decl) {
1400
1467
  if (decl.default !== void 0) return decl.default;
@@ -1409,6 +1476,8 @@ function hostScopeDefault(decl) {
1409
1476
  return [];
1410
1477
  case "enum":
1411
1478
  return decl.values?.[0] ?? "";
1479
+ case "quality":
1480
+ return decl.stages?.[0] ?? "";
1412
1481
  }
1413
1482
  }
1414
1483
  function selfBackedResolver(decls) {
@@ -1435,6 +1504,8 @@ function sceneDefault(decl) {
1435
1504
  return [];
1436
1505
  case "enum":
1437
1506
  return decl.values?.[0] ?? "";
1507
+ case "quality":
1508
+ return decl.stages?.[0] ?? "";
1438
1509
  }
1439
1510
  }
1440
1511
  function truthy(v) {