@storylet-studio/runtime 0.2.0 → 0.3.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/dist/index.d.cts CHANGED
@@ -2,6 +2,7 @@ import { ScalarValue, ScopeResolver, ExprNode } from '@wildwinter/expr';
2
2
  export { Prng, makePrng, shuffleInPlace } from '@wildwinter/expr';
3
3
  import { Bundle, Card, Expression, Deck, Box, Hand, HandTemplate, TagGroup, PropertyDecl, FlowSave, SaveEnvelope, PropertyType, ScalarValue as ScalarValue$1 } from '@storylet-studio/model';
4
4
  import { PropertyBag, PropertyRow } from '@wildwinter/scoperegistry';
5
+ export { PropertyRow } from '@wildwinter/scoperegistry';
5
6
 
6
7
  interface EngineOptions {
7
8
  /** Default seed for each flow's PRNG; override per flow in openFlow
@@ -142,11 +143,6 @@ interface CardEntry {
142
143
  deck: Deck<Expression>;
143
144
  box: Box<Expression>;
144
145
  }
145
- /** One examiner row, addressed by the property-path grammar
146
- * (getProperty / setProperty take the same `path`). */
147
- interface PropertyView extends PropertyRow {
148
- path: string;
149
- }
150
146
  /** One kernel bag with its store path prefix (story / box.<id> / deck.<id>
151
147
  * / hand.<id> / value.<id>): the state logger's mount surface
152
148
  * (design/engine-runtimes.md 3.4 - the logger builds on the PropertyBag
@@ -308,7 +304,7 @@ declare class Engine {
308
304
  private resolveShared;
309
305
  /** The shared surface as examiner rows: @world (read through the
310
306
  * resolver) then the shared partitions. Per-flow rows live on each Flow. */
311
- listProperties(): PropertyView[];
307
+ listProperties(): PropertyRow[];
312
308
  /** The SHARED kernel bags with their store path prefixes (the state
313
309
  * logger's mount surface). The @world container is the host's own bag -
314
310
  * the host mounts it itself. */
@@ -525,7 +521,7 @@ declare class Flow {
525
521
  * resolver, then per scope the shared values and this flow's own.
526
522
  * Bundle order: world, story, then per-box / per-deck / per-hand /
527
523
  * per-tag stores. */
528
- listProperties(): PropertyView[];
524
+ listProperties(): PropertyRow[];
529
525
  /** Read by path: "world.x", "story.gold", "value.v_docks.danger",
530
526
  * "box.b_x.heat", "deck.k_main.n", "hand.h_board.owner" - the flow's
531
527
  * merged view, routed by the declaration's sharing. */
@@ -653,4 +649,4 @@ interface BundleDescription {
653
649
  * throughout; the same shape every runtime returns. */
654
650
  declare function describeBundle(bundle: Bundle): BundleDescription;
655
651
 
656
- export { type BagMount, type BoxSummary, type BoxView, type BundleDescription, type BundleIdentity, type DealtCard, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, Flow, type HandSummary, type LogEntry, type MapSummary, type OpenFlowOptions, type OutcomeView, type PlayOptions, type PropertyScopeKind, type PropertyScopeSummary, type PropertySummary, type PropertyView, type RankedList, type TagGroupSummary, type TraceEvent, type TraceHandler, type TraceVerdict, describeBundle };
652
+ export { type BagMount, type BoxSummary, type BoxView, type BundleDescription, type BundleIdentity, type DealtCard, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, Flow, type HandSummary, type LogEntry, type MapSummary, type OpenFlowOptions, type OutcomeView, type PlayOptions, type PropertyScopeKind, type PropertyScopeSummary, type PropertySummary, type RankedList, type TagGroupSummary, type TraceEvent, type TraceHandler, type TraceVerdict, describeBundle };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { ScalarValue, ScopeResolver, ExprNode } from '@wildwinter/expr';
2
2
  export { Prng, makePrng, shuffleInPlace } from '@wildwinter/expr';
3
3
  import { Bundle, Card, Expression, Deck, Box, Hand, HandTemplate, TagGroup, PropertyDecl, FlowSave, SaveEnvelope, PropertyType, ScalarValue as ScalarValue$1 } from '@storylet-studio/model';
4
4
  import { PropertyBag, PropertyRow } from '@wildwinter/scoperegistry';
5
+ export { PropertyRow } from '@wildwinter/scoperegistry';
5
6
 
6
7
  interface EngineOptions {
7
8
  /** Default seed for each flow's PRNG; override per flow in openFlow
@@ -142,11 +143,6 @@ interface CardEntry {
142
143
  deck: Deck<Expression>;
143
144
  box: Box<Expression>;
144
145
  }
145
- /** One examiner row, addressed by the property-path grammar
146
- * (getProperty / setProperty take the same `path`). */
147
- interface PropertyView extends PropertyRow {
148
- path: string;
149
- }
150
146
  /** One kernel bag with its store path prefix (story / box.<id> / deck.<id>
151
147
  * / hand.<id> / value.<id>): the state logger's mount surface
152
148
  * (design/engine-runtimes.md 3.4 - the logger builds on the PropertyBag
@@ -308,7 +304,7 @@ declare class Engine {
308
304
  private resolveShared;
309
305
  /** The shared surface as examiner rows: @world (read through the
310
306
  * resolver) then the shared partitions. Per-flow rows live on each Flow. */
311
- listProperties(): PropertyView[];
307
+ listProperties(): PropertyRow[];
312
308
  /** The SHARED kernel bags with their store path prefixes (the state
313
309
  * logger's mount surface). The @world container is the host's own bag -
314
310
  * the host mounts it itself. */
@@ -525,7 +521,7 @@ declare class Flow {
525
521
  * resolver, then per scope the shared values and this flow's own.
526
522
  * Bundle order: world, story, then per-box / per-deck / per-hand /
527
523
  * per-tag stores. */
528
- listProperties(): PropertyView[];
524
+ listProperties(): PropertyRow[];
529
525
  /** Read by path: "world.x", "story.gold", "value.v_docks.danger",
530
526
  * "box.b_x.heat", "deck.k_main.n", "hand.h_board.owner" - the flow's
531
527
  * merged view, routed by the declaration's sharing. */
@@ -653,4 +649,4 @@ interface BundleDescription {
653
649
  * throughout; the same shape every runtime returns. */
654
650
  declare function describeBundle(bundle: Bundle): BundleDescription;
655
651
 
656
- export { type BagMount, type BoxSummary, type BoxView, type BundleDescription, type BundleIdentity, type DealtCard, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, Flow, type HandSummary, type LogEntry, type MapSummary, type OpenFlowOptions, type OutcomeView, type PlayOptions, type PropertyScopeKind, type PropertyScopeSummary, type PropertySummary, type PropertyView, type RankedList, type TagGroupSummary, type TraceEvent, type TraceHandler, type TraceVerdict, describeBundle };
652
+ export { type BagMount, type BoxSummary, type BoxView, type BundleDescription, type BundleIdentity, type DealtCard, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, Flow, type HandSummary, type LogEntry, type MapSummary, type OpenFlowOptions, type OutcomeView, type PlayOptions, type PropertyScopeKind, type PropertyScopeSummary, type PropertySummary, type RankedList, type TagGroupSummary, type TraceEvent, type TraceHandler, type TraceVerdict, describeBundle };
package/dist/index.js CHANGED
@@ -423,8 +423,12 @@ var PropertyBag = class _PropertyBag {
423
423
  * long-standing contract); a product whose names are case-significant
424
424
  * passes identity. */
425
425
  norm;
426
+ /** The address prefix this bag's rows carry, separator included (`@`,
427
+ * `@scene.`, `world.`, `deck.<id>.`). Empty means a row's path is its name. */
428
+ pathPrefix;
426
429
  constructor(declarations = [], opts) {
427
430
  this.norm = opts?.normalise ?? ((n) => n.toLowerCase());
431
+ this.pathPrefix = opts?.pathPrefix ?? "";
428
432
  this.seed(declarations);
429
433
  }
430
434
  seed(declarations) {
@@ -468,7 +472,7 @@ var PropertyBag = class _PropertyBag {
468
472
  /** Examiner rows: the declared surface only (stray values are storage,
469
473
  * not surface). */
470
474
  rows() {
471
- return [...this.decls.entries()].map(([name, d]) => rowFor(d, this.get(name), void 0, name));
475
+ return [...this.decls.entries()].map(([name, d]) => rowFor(d, this.get(name), void 0, name, this.pathPrefix));
472
476
  }
473
477
  declarations() {
474
478
  return [...this.decls.values()];
@@ -477,7 +481,7 @@ var PropertyBag = class _PropertyBag {
477
481
  * duplicated, the normalisation policy carried, subscriptions NOT
478
482
  * carried. */
479
483
  clone() {
480
- const c = new _PropertyBag([], { normalise: this.norm });
484
+ const c = new _PropertyBag([], { normalise: this.norm, pathPrefix: this.pathPrefix });
481
485
  c.decls = new Map(this.decls);
482
486
  Object.assign(c.values, structuredClone(this.values));
483
487
  return c;
@@ -500,13 +504,19 @@ var PropertyBag = class _PropertyBag {
500
504
  for (const [k, v] of Object.entries(values)) this.values[this.norm(k)] = v;
501
505
  }
502
506
  };
503
- function rowFor(d, value, writable, name) {
507
+ function rowFor(d, value, writable, name, pathPrefix = "") {
508
+ const rowName = name ?? d.name.toLowerCase();
504
509
  return {
505
- name: name ?? d.name.toLowerCase(),
510
+ name: rowName,
511
+ path: pathPrefix + rowName,
506
512
  type: d.type,
507
513
  value,
508
514
  default: d.default ?? defaultFor(d),
509
515
  ...d.values !== void 0 ? { values: d.values } : {},
516
+ // `stages` was added to the row so an examiner could offer a quality's ladder
517
+ // instead of a free-text box, and then never populated here: every quality row
518
+ // this function built came out without one. Fixed 2026-09-02.
519
+ ...d.stages !== void 0 ? { stages: d.stages } : {},
510
520
  writable: writable ?? d.writable ?? true
511
521
  };
512
522
  }
@@ -526,6 +536,13 @@ function defaultFor(d) {
526
536
  // A quality starts at the first rung of its ladder.
527
537
  case "quality":
528
538
  return d.stages?.[0] ?? "";
539
+ // Unreachable for a well-typed declaration, and deliberately present anyway: a bundle
540
+ // is DATA, and a hand-edited or newer-than-this-build one can carry a type string the
541
+ // union does not have. Falling off the switch would seed `undefined`, which is not a
542
+ // ScalarValue and travels a long way before it fails. Patterplay's copy of this had the
543
+ // guard and this one did not, which is the drift you only find by removing a duplicate.
544
+ default:
545
+ return false;
529
546
  }
530
547
  }
531
548
 
@@ -533,7 +550,7 @@ function defaultFor(d) {
533
550
  var tagKey = (groupId, tagId) => `${groupId}${tagId}`;
534
551
  var cardIsShared = (card, deckShared) => card.shared ?? deckShared;
535
552
  var sharedCap = (card) => card.sharedCopies ?? card.copies ?? 1;
536
- var bagFromDecls = (decls) => new PropertyBag(decls, { normalise: (n) => n });
553
+ var bagFromDecls = (decls, pathPrefix) => new PropertyBag(decls, { normalise: (n) => n, pathPrefix });
537
554
  function conditionPasses(v) {
538
555
  if (typeof v === "boolean") return v;
539
556
  if (typeof v === "number") return v !== 0;
@@ -553,18 +570,18 @@ var handDeclsOf = (internals, hand) => {
553
570
  var buildPartition = (internals, half) => {
554
571
  const b = internals.bundle;
555
572
  return {
556
- story: bagFromDecls(half("story", b.story.properties)),
557
- box: new Map(b.boxes.map((box) => [box.id, bagFromDecls(half("box", box.properties))])),
573
+ story: bagFromDecls(half("story", b.story.properties), "story."),
574
+ box: new Map(b.boxes.map((box) => [box.id, bagFromDecls(half("box", box.properties), `box.${box.id}.`)])),
558
575
  deck: new Map(b.boxes.flatMap((box) => box.decks.map(
559
- (deck) => [deck.id, bagFromDecls(half("deck", deck.properties))]
576
+ (deck) => [deck.id, bagFromDecls(half("deck", deck.properties), `deck.${deck.id}.`)]
560
577
  ))),
561
578
  // A template instance inherits the template's property declarations;
562
579
  // a standalone hand declares its own (schema 2.6).
563
580
  hand: new Map(b.boxes.flatMap((box) => box.hands.map(
564
- (hand) => [hand.id, bagFromDecls(half("hand", handDeclsOf(internals, hand)))]
581
+ (hand) => [hand.id, bagFromDecls(half("hand", handDeclsOf(internals, hand)), `hand.${hand.id}.`)]
565
582
  ))),
566
583
  value: new Map(b.boxes.flatMap((box) => box.tagGroups.flatMap((group) => group.tags.map(
567
- (tag) => [tag.id, bagFromDecls(half("value", tag.properties ?? []))]
584
+ (tag) => [tag.id, bagFromDecls(half("value", tag.properties ?? []), `value.${tag.id}.`)]
568
585
  ))))
569
586
  };
570
587
  };
@@ -674,7 +691,7 @@ var Engine = class {
674
691
  if (hostWorld !== void 0) {
675
692
  internals.worldResolver = hostWorld;
676
693
  } else {
677
- const bag = bagFromDecls(internals.bundle.world.properties);
694
+ const bag = bagFromDecls(internals.bundle.world.properties, "world.");
678
695
  internals.worldResolver = {
679
696
  get: (n) => bag.get(n),
680
697
  set: (n, v) => {
@@ -850,11 +867,16 @@ var Engine = class {
850
867
  value: value ?? d.default,
851
868
  default: d.default,
852
869
  ...d.values !== void 0 ? { values: d.values } : {},
853
- ...d.stages !== void 0 ? { stages: d.stages } : {}
870
+ ...d.stages !== void 0 ? { stages: d.stages } : {},
871
+ // @world is FOREIGN - a host resolver backs it - so writability is whether that
872
+ // resolver can be written at all, which is the shared registry's own rule for a
873
+ // foreign scope. The `as PropertyView` cast this replaced was hiding the field's
874
+ // absence: the row type has always required it, and these rows shipped without one.
875
+ writable: this.internals.worldResolver.set !== void 0
854
876
  });
855
877
  }
856
- const add = (prefix, bag) => {
857
- for (const row of bag.rows()) out.push({ path: `${prefix}.${row.name}`, ...row });
878
+ const add = (_prefix, bag) => {
879
+ for (const row of bag.rows()) out.push(row);
858
880
  };
859
881
  add("story", this.internals.shared.story);
860
882
  for (const [id, bag] of this.internals.shared.box) add(`box.${id}`, bag);
@@ -1746,13 +1768,18 @@ var Flow = class {
1746
1768
  value: value ?? d.default,
1747
1769
  default: d.default,
1748
1770
  ...d.values !== void 0 ? { values: d.values } : {},
1749
- ...d.stages !== void 0 ? { stages: d.stages } : {}
1771
+ ...d.stages !== void 0 ? { stages: d.stages } : {},
1772
+ // @world is FOREIGN - a host resolver backs it - so writability is whether that
1773
+ // resolver can be written at all, which is the shared registry's own rule for a
1774
+ // foreign scope. The `as PropertyView` cast this replaced was hiding the field's
1775
+ // absence: the row type has always required it, and these rows shipped without one.
1776
+ writable: this.internals.worldResolver.set !== void 0
1750
1777
  });
1751
1778
  }
1752
- const add = (prefix, shared, own) => {
1779
+ const add = (_prefix, shared, own) => {
1753
1780
  for (const bag of [shared, own]) {
1754
1781
  if (bag === void 0) continue;
1755
- for (const row of bag.rows()) out.push({ path: `${prefix}.${row.name}`, ...row });
1782
+ for (const row of bag.rows()) out.push(row);
1756
1783
  }
1757
1784
  };
1758
1785
  add("story", this.internals.shared.story, this.stores.story);