@storylet-studio/runtime 0.2.0 → 0.4.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.cjs +55 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -8
- package/dist/index.d.ts +18 -8
- package/dist/index.js +55 -18
- package/dist/index.js.map +1 -1
- package/package.json +8 -3
- package/dist/storyletengine.min.js +0 -2
- package/dist/storyletengine.min.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -453,8 +453,12 @@ var PropertyBag = class _PropertyBag {
|
|
|
453
453
|
* long-standing contract); a product whose names are case-significant
|
|
454
454
|
* passes identity. */
|
|
455
455
|
norm;
|
|
456
|
+
/** The address prefix this bag's rows carry, separator included (`@`,
|
|
457
|
+
* `@scene.`, `world.`, `deck.<id>.`). Empty means a row's path is its name. */
|
|
458
|
+
pathPrefix;
|
|
456
459
|
constructor(declarations = [], opts) {
|
|
457
460
|
this.norm = opts?.normalise ?? ((n) => n.toLowerCase());
|
|
461
|
+
this.pathPrefix = opts?.pathPrefix ?? "";
|
|
458
462
|
this.seed(declarations);
|
|
459
463
|
}
|
|
460
464
|
seed(declarations) {
|
|
@@ -498,7 +502,7 @@ var PropertyBag = class _PropertyBag {
|
|
|
498
502
|
/** Examiner rows: the declared surface only (stray values are storage,
|
|
499
503
|
* not surface). */
|
|
500
504
|
rows() {
|
|
501
|
-
return [...this.decls.entries()].map(([name, d]) => rowFor(d, this.get(name), void 0, name));
|
|
505
|
+
return [...this.decls.entries()].map(([name, d]) => rowFor(d, this.get(name), void 0, name, this.pathPrefix));
|
|
502
506
|
}
|
|
503
507
|
declarations() {
|
|
504
508
|
return [...this.decls.values()];
|
|
@@ -507,7 +511,7 @@ var PropertyBag = class _PropertyBag {
|
|
|
507
511
|
* duplicated, the normalisation policy carried, subscriptions NOT
|
|
508
512
|
* carried. */
|
|
509
513
|
clone() {
|
|
510
|
-
const c = new _PropertyBag([], { normalise: this.norm });
|
|
514
|
+
const c = new _PropertyBag([], { normalise: this.norm, pathPrefix: this.pathPrefix });
|
|
511
515
|
c.decls = new Map(this.decls);
|
|
512
516
|
Object.assign(c.values, structuredClone(this.values));
|
|
513
517
|
return c;
|
|
@@ -530,13 +534,19 @@ var PropertyBag = class _PropertyBag {
|
|
|
530
534
|
for (const [k, v] of Object.entries(values)) this.values[this.norm(k)] = v;
|
|
531
535
|
}
|
|
532
536
|
};
|
|
533
|
-
function rowFor(d, value, writable, name) {
|
|
537
|
+
function rowFor(d, value, writable, name, pathPrefix = "") {
|
|
538
|
+
const rowName = name ?? d.name.toLowerCase();
|
|
534
539
|
return {
|
|
535
|
-
name:
|
|
540
|
+
name: rowName,
|
|
541
|
+
path: pathPrefix + rowName,
|
|
536
542
|
type: d.type,
|
|
537
543
|
value,
|
|
538
544
|
default: d.default ?? defaultFor(d),
|
|
539
545
|
...d.values !== void 0 ? { values: d.values } : {},
|
|
546
|
+
// `stages` was added to the row so an examiner could offer a quality's ladder
|
|
547
|
+
// instead of a free-text box, and then never populated here: every quality row
|
|
548
|
+
// this function built came out without one. Fixed 2026-09-02.
|
|
549
|
+
...d.stages !== void 0 ? { stages: d.stages } : {},
|
|
540
550
|
writable: writable ?? d.writable ?? true
|
|
541
551
|
};
|
|
542
552
|
}
|
|
@@ -556,6 +566,13 @@ function defaultFor(d) {
|
|
|
556
566
|
// A quality starts at the first rung of its ladder.
|
|
557
567
|
case "quality":
|
|
558
568
|
return d.stages?.[0] ?? "";
|
|
569
|
+
// Unreachable for a well-typed declaration, and deliberately present anyway: a bundle
|
|
570
|
+
// is DATA, and a hand-edited or newer-than-this-build one can carry a type string the
|
|
571
|
+
// union does not have. Falling off the switch would seed `undefined`, which is not a
|
|
572
|
+
// ScalarValue and travels a long way before it fails. Patterplay's copy of this had the
|
|
573
|
+
// guard and this one did not, which is the drift you only find by removing a duplicate.
|
|
574
|
+
default:
|
|
575
|
+
return false;
|
|
559
576
|
}
|
|
560
577
|
}
|
|
561
578
|
|
|
@@ -563,7 +580,7 @@ function defaultFor(d) {
|
|
|
563
580
|
var tagKey = (groupId, tagId) => `${groupId}${tagId}`;
|
|
564
581
|
var cardIsShared = (card, deckShared) => card.shared ?? deckShared;
|
|
565
582
|
var sharedCap = (card) => card.sharedCopies ?? card.copies ?? 1;
|
|
566
|
-
var bagFromDecls = (decls) => new PropertyBag(decls, { normalise: (n) => n });
|
|
583
|
+
var bagFromDecls = (decls, pathPrefix) => new PropertyBag(decls, { normalise: (n) => n, pathPrefix });
|
|
567
584
|
function conditionPasses(v) {
|
|
568
585
|
if (typeof v === "boolean") return v;
|
|
569
586
|
if (typeof v === "number") return v !== 0;
|
|
@@ -583,18 +600,18 @@ var handDeclsOf = (internals, hand) => {
|
|
|
583
600
|
var buildPartition = (internals, half) => {
|
|
584
601
|
const b = internals.bundle;
|
|
585
602
|
return {
|
|
586
|
-
story: bagFromDecls(half("story", b.story.properties)),
|
|
587
|
-
box: new Map(b.boxes.map((box) => [box.id, bagFromDecls(half("box", box.properties))])),
|
|
603
|
+
story: bagFromDecls(half("story", b.story.properties), "story."),
|
|
604
|
+
box: new Map(b.boxes.map((box) => [box.id, bagFromDecls(half("box", box.properties), `box.${box.id}.`)])),
|
|
588
605
|
deck: new Map(b.boxes.flatMap((box) => box.decks.map(
|
|
589
|
-
(deck) => [deck.id, bagFromDecls(half("deck", deck.properties))]
|
|
606
|
+
(deck) => [deck.id, bagFromDecls(half("deck", deck.properties), `deck.${deck.id}.`)]
|
|
590
607
|
))),
|
|
591
608
|
// A template instance inherits the template's property declarations;
|
|
592
609
|
// a standalone hand declares its own (schema 2.6).
|
|
593
610
|
hand: new Map(b.boxes.flatMap((box) => box.hands.map(
|
|
594
|
-
(hand) => [hand.id, bagFromDecls(half("hand", handDeclsOf(internals, hand)))]
|
|
611
|
+
(hand) => [hand.id, bagFromDecls(half("hand", handDeclsOf(internals, hand)), `hand.${hand.id}.`)]
|
|
595
612
|
))),
|
|
596
613
|
value: new Map(b.boxes.flatMap((box) => box.tagGroups.flatMap((group) => group.tags.map(
|
|
597
|
-
(tag) => [tag.id, bagFromDecls(half("value", tag.properties ?? []))]
|
|
614
|
+
(tag) => [tag.id, bagFromDecls(half("value", tag.properties ?? []), `value.${tag.id}.`)]
|
|
598
615
|
))))
|
|
599
616
|
};
|
|
600
617
|
};
|
|
@@ -616,6 +633,7 @@ var loadPartition = (p, values) => {
|
|
|
616
633
|
var Engine = class {
|
|
617
634
|
internals;
|
|
618
635
|
seed;
|
|
636
|
+
onReplacedFlow;
|
|
619
637
|
flowsById = /* @__PURE__ */ new Map();
|
|
620
638
|
engineTraceHandlers = /* @__PURE__ */ new Set();
|
|
621
639
|
/** The host's @world binding, if the engine was built with one: it
|
|
@@ -624,6 +642,7 @@ var Engine = class {
|
|
|
624
642
|
hostWorld;
|
|
625
643
|
constructor(bundle, opts = {}) {
|
|
626
644
|
this.seed = opts.seed ?? 0;
|
|
645
|
+
this.onReplacedFlow = opts.onReplacedFlow;
|
|
627
646
|
if (opts.world !== void 0) this.hostWorld = opts.world;
|
|
628
647
|
const internals = {
|
|
629
648
|
bundle,
|
|
@@ -644,6 +663,7 @@ var Engine = class {
|
|
|
644
663
|
flowDecls: { story: [], box: /* @__PURE__ */ new Map(), deck: /* @__PURE__ */ new Map(), hand: /* @__PURE__ */ new Map(), value: /* @__PURE__ */ new Map() },
|
|
645
664
|
shared: void 0,
|
|
646
665
|
worldResolver: void 0,
|
|
666
|
+
worldReadOnly: /* @__PURE__ */ new Set(),
|
|
647
667
|
emitEngine: (flow, event, turn) => {
|
|
648
668
|
if (this.internals.logCap !== void 0) {
|
|
649
669
|
this.engineLog.push({ ...event, flow, seq: this.engineSeq++, ...turn !== void 0 ? { turn } : {} });
|
|
@@ -701,10 +721,11 @@ var Engine = class {
|
|
|
701
721
|
initShared(hostWorld) {
|
|
702
722
|
const internals = this.internals;
|
|
703
723
|
internals.shared = buildPartition(internals, sharedHalf);
|
|
724
|
+
internals.worldReadOnly = new Set(internals.bundle.world.properties.filter((d) => d.writable === false).map((d) => d.name));
|
|
704
725
|
if (hostWorld !== void 0) {
|
|
705
726
|
internals.worldResolver = hostWorld;
|
|
706
727
|
} else {
|
|
707
|
-
const bag = bagFromDecls(internals.bundle.world.properties);
|
|
728
|
+
const bag = bagFromDecls(internals.bundle.world.properties, "world.");
|
|
708
729
|
internals.worldResolver = {
|
|
709
730
|
get: (n) => bag.get(n),
|
|
710
731
|
set: (n, v) => {
|
|
@@ -744,7 +765,12 @@ var Engine = class {
|
|
|
744
765
|
* state; shared state is untouched. There is no default flow: "main" is
|
|
745
766
|
* a caller convention, not an engine rule. */
|
|
746
767
|
openFlow(id, opts = {}) {
|
|
747
|
-
this.flowsById.get(id)
|
|
768
|
+
const existing = this.flowsById.get(id);
|
|
769
|
+
if (existing) {
|
|
770
|
+
const dealt = existing.heldCardIds().length;
|
|
771
|
+
if (dealt > 0) this.onReplacedFlow?.(id, dealt);
|
|
772
|
+
existing.markClosed();
|
|
773
|
+
}
|
|
748
774
|
const flow = new Flow(this, this.internals, id, opts.seed ?? this.seed);
|
|
749
775
|
this.flowsById.set(id, flow);
|
|
750
776
|
return flow;
|
|
@@ -880,11 +906,16 @@ var Engine = class {
|
|
|
880
906
|
value: value ?? d.default,
|
|
881
907
|
default: d.default,
|
|
882
908
|
...d.values !== void 0 ? { values: d.values } : {},
|
|
883
|
-
...d.stages !== void 0 ? { stages: d.stages } : {}
|
|
909
|
+
...d.stages !== void 0 ? { stages: d.stages } : {},
|
|
910
|
+
// @world is FOREIGN - a host resolver backs it - so writability is whether that
|
|
911
|
+
// resolver can be written at all, which is the shared registry's own rule for a
|
|
912
|
+
// foreign scope. The `as PropertyView` cast this replaced was hiding the field's
|
|
913
|
+
// absence: the row type has always required it, and these rows shipped without one.
|
|
914
|
+
writable: this.internals.worldResolver.set !== void 0
|
|
884
915
|
});
|
|
885
916
|
}
|
|
886
|
-
const add = (
|
|
887
|
-
for (const row of bag.rows()) out.push(
|
|
917
|
+
const add = (_prefix, bag) => {
|
|
918
|
+
for (const row of bag.rows()) out.push(row);
|
|
888
919
|
};
|
|
889
920
|
add("story", this.internals.shared.story);
|
|
890
921
|
for (const [id, bag] of this.internals.shared.box) add(`box.${id}`, bag);
|
|
@@ -1705,6 +1736,7 @@ var Flow = class {
|
|
|
1705
1736
|
case "world": {
|
|
1706
1737
|
const resolver = this.internals.worldResolver;
|
|
1707
1738
|
if (!resolver.set) throw new Error(`@world.${name} cannot be written: the host bound @world read-only`);
|
|
1739
|
+
if (this.internals.worldReadOnly.has(name)) throw new Error(`'@world.${name}' is read-only (writable: false)`);
|
|
1708
1740
|
const prev = resolver.get(name);
|
|
1709
1741
|
resolver.set(name, value);
|
|
1710
1742
|
return { path: `world.${name}`, ...prev !== void 0 ? { prev } : {} };
|
|
@@ -1776,13 +1808,18 @@ var Flow = class {
|
|
|
1776
1808
|
value: value ?? d.default,
|
|
1777
1809
|
default: d.default,
|
|
1778
1810
|
...d.values !== void 0 ? { values: d.values } : {},
|
|
1779
|
-
...d.stages !== void 0 ? { stages: d.stages } : {}
|
|
1811
|
+
...d.stages !== void 0 ? { stages: d.stages } : {},
|
|
1812
|
+
// @world is FOREIGN - a host resolver backs it - so writability is whether that
|
|
1813
|
+
// resolver can be written at all, which is the shared registry's own rule for a
|
|
1814
|
+
// foreign scope. The `as PropertyView` cast this replaced was hiding the field's
|
|
1815
|
+
// absence: the row type has always required it, and these rows shipped without one.
|
|
1816
|
+
writable: this.internals.worldResolver.set !== void 0
|
|
1780
1817
|
});
|
|
1781
1818
|
}
|
|
1782
|
-
const add = (
|
|
1819
|
+
const add = (_prefix, shared, own) => {
|
|
1783
1820
|
for (const bag of [shared, own]) {
|
|
1784
1821
|
if (bag === void 0) continue;
|
|
1785
|
-
for (const row of bag.rows()) out.push(
|
|
1822
|
+
for (const row of bag.rows()) out.push(row);
|
|
1786
1823
|
}
|
|
1787
1824
|
};
|
|
1788
1825
|
add("story", this.internals.shared.story, this.stores.story);
|