@storylet-studio/runtime 0.8.2 → 0.9.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/README.md +2 -2
- package/dist/index.cjs +149 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -20
- package/dist/index.d.ts +56 -20
- package/dist/index.js +155 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @storylet-studio/runtime
|
|
2
2
|
|
|
3
3
|
The Storylet Engine **JS reference runtime**: the Engine and Flow surface of
|
|
4
|
-
[the bundle format](https://
|
|
4
|
+
[the bundle format](https://storylets.dev/format/bundle/) section 5 -
|
|
5
5
|
the engine (`openFlow` / `getFlow` / `flows` / `closeFlow` / `reset` /
|
|
6
6
|
`saveGame` / `loadGame` / `saveFlow` / `previewLoad` / `previewFlowRestore` /
|
|
7
7
|
`subscribeTrace` / `log` / `clearLog` /
|
|
@@ -24,4 +24,4 @@ game. Pass the game's as `new Engine(bundle, { registry })` to share it with any
|
|
|
24
24
|
starting `storylets/`, `@world` is the game's to register, and `saveGame()` leaves the values
|
|
25
25
|
to the game, which saves the registry once. Without one the engine makes its own registry, self-backs
|
|
26
26
|
`@world`, and `saveGame()` carries every value, so one call is still the whole run. Version 1
|
|
27
|
-
envelopes still load. See [Running it with Patter](https://
|
|
27
|
+
envelopes still load. See [Running it with Patter](https://storylets.dev/play/with-patter/#one-registry).
|
package/dist/index.cjs
CHANGED
|
@@ -40,7 +40,7 @@ var import_scoperegistry = require("@wildwinter/scoperegistry");
|
|
|
40
40
|
var import_expr = require("@wildwinter/expr");
|
|
41
41
|
|
|
42
42
|
// src/engine.ts
|
|
43
|
-
var tagKey = (groupId, tagId) => `${groupId}${tagId}`;
|
|
43
|
+
var tagKey = (boxId, groupId, tagId) => `${boxId}${groupId}${tagId}`;
|
|
44
44
|
var cardIsShared = (card, deckShared) => card.shared ?? deckShared;
|
|
45
45
|
var sharedCap = (card) => card.sharedCopies ?? card.copies ?? 1;
|
|
46
46
|
var OWNER = "Storylet Engine";
|
|
@@ -97,11 +97,12 @@ var isShared = (scope, d) => d.shared ?? SCOPE_DEFAULT_SHARED[scope];
|
|
|
97
97
|
var sharedHalf = (scope, decls) => decls.filter((d) => isShared(scope, d));
|
|
98
98
|
var flowHalf = (scope, decls) => decls.filter((d) => !isShared(scope, d));
|
|
99
99
|
var OWNED_SCOPES = ["box", "deck", "hand", "value"];
|
|
100
|
+
var emptyOwnerIndex = () => ({ gameId: /* @__PURE__ */ new Map(), id: /* @__PURE__ */ new Map(), repeated: /* @__PURE__ */ new Map(), zoneQualified: /* @__PURE__ */ new Map() });
|
|
100
101
|
var emptyOwnerIndexes = () => ({
|
|
101
|
-
box:
|
|
102
|
-
deck:
|
|
103
|
-
hand:
|
|
104
|
-
value:
|
|
102
|
+
box: emptyOwnerIndex(),
|
|
103
|
+
deck: emptyOwnerIndex(),
|
|
104
|
+
hand: emptyOwnerIndex(),
|
|
105
|
+
value: emptyOwnerIndex()
|
|
105
106
|
});
|
|
106
107
|
var indexOwner = (index, entity) => {
|
|
107
108
|
const gameId = (0, import_model.effectiveGameId)(entity);
|
|
@@ -113,6 +114,7 @@ var indexValueOwners = (index, bundle) => {
|
|
|
113
114
|
for (const [id, segment] of addresses.print) index.gameId.set(id, segment);
|
|
114
115
|
for (const [segment, id] of addresses.accept) index.id.set(segment, id);
|
|
115
116
|
for (const [gameId, candidates] of addresses.repeated) index.repeated.set(gameId, candidates);
|
|
117
|
+
for (const [segment, zone] of addresses.zoneQualified) index.zoneQualified.set(segment, zone);
|
|
116
118
|
};
|
|
117
119
|
var handDeclsOf = (internals, hand) => {
|
|
118
120
|
if (hand.template !== void 0) {
|
|
@@ -124,6 +126,8 @@ var addressOf = (internals, kind, id) => `${kind}.${internals.owners[kind].gameI
|
|
|
124
126
|
var resolveOwner = (internals, kind, segment) => {
|
|
125
127
|
const candidates = internals.owners[kind].repeated.get(segment);
|
|
126
128
|
if (candidates !== void 0) return { ambiguous: candidates };
|
|
129
|
+
const zone = internals.owners[kind].zoneQualified.get(segment);
|
|
130
|
+
if (zone !== void 0) return { zone };
|
|
127
131
|
const byGameId = internals.owners[kind].id.get(segment);
|
|
128
132
|
if (byGameId !== void 0) return { id: byGameId, legacy: false };
|
|
129
133
|
if (internals.owners[kind].gameId.has(segment)) return { id: segment, legacy: true };
|
|
@@ -133,6 +137,7 @@ var ownerOrThrow = (internals, kind, segment, name) => {
|
|
|
133
137
|
const owner = resolveOwner(internals, kind, segment);
|
|
134
138
|
if (owner === void 0) throw new Error(`no ${kind} store "${segment}"`);
|
|
135
139
|
if ("ambiguous" in owner) throw new Error((0, import_model.ambiguousValueAddressMessage)(segment, name, owner.ambiguous));
|
|
140
|
+
if ("zone" in owner) throw new Error((0, import_model.zoneQualifiedValueAddressMessage)(segment, owner.zone, name));
|
|
136
141
|
return owner;
|
|
137
142
|
};
|
|
138
143
|
var legacyAddressMessage = (internals, kind, segment, name) => `"${kind}.${segment}.${name}" names the ${kind} by its internal id; write "${addressOf(internals, kind, segment)}.${name}". The internal-id form is refused after the next release.`;
|
|
@@ -150,9 +155,12 @@ var buildPartition = (internals, half) => {
|
|
|
150
155
|
hand: new Map(b.boxes.flatMap((box) => box.hands.map(
|
|
151
156
|
(hand) => [hand.id, bagFromDecls(half("hand", handDeclsOf(internals, hand)), at("hand", hand.id))]
|
|
152
157
|
))),
|
|
153
|
-
|
|
158
|
+
// Every box's tags, then the project map's zones ONCE (design/project-
|
|
159
|
+
// map-contract.md 3.3): a zone is one bag per partition, whichever boxes'
|
|
160
|
+
// hands are dealt to it.
|
|
161
|
+
value: new Map((0, import_model.allTagGroups)(b).flatMap((group) => group.tags.map(
|
|
154
162
|
(tag) => [tag.id, bagFromDecls(half("value", tag.properties ?? []), at("value", tag.id))]
|
|
155
|
-
)))
|
|
163
|
+
)))
|
|
156
164
|
};
|
|
157
165
|
};
|
|
158
166
|
var partitionValues = (p) => ({
|
|
@@ -269,6 +277,68 @@ function finishReport(bundle, saved, flows, draft) {
|
|
|
269
277
|
retypedProperties
|
|
270
278
|
};
|
|
271
279
|
}
|
|
280
|
+
var refuseUnreadableBundle = (bundle) => {
|
|
281
|
+
const schema = bundle.schema;
|
|
282
|
+
if (typeof schema !== "string" || !import_model.BUNDLE_SCHEMAS.includes(schema)) {
|
|
283
|
+
throw new Error(`unsupported bundle schema: ${String(schema)} (this runtime reads ${import_model.BUNDLE_SCHEMAS.join(" and ")})`);
|
|
284
|
+
}
|
|
285
|
+
const problems = [];
|
|
286
|
+
const map = bundle.map?.group;
|
|
287
|
+
const mapName = map !== void 0 ? (0, import_model.effectiveGameId)(map) : void 0;
|
|
288
|
+
if (map !== void 0 && mapName === import_model.PLACE_GROUP) {
|
|
289
|
+
problems.push(`the project map's tag group is called "${import_model.PLACE_GROUP}", which is reserved for a box's own hands`);
|
|
290
|
+
}
|
|
291
|
+
const boxTags = /* @__PURE__ */ new Map();
|
|
292
|
+
for (const box of bundle.boxes) {
|
|
293
|
+
for (const group of box.tagGroups) {
|
|
294
|
+
for (const tag of group.tags) {
|
|
295
|
+
const gameId = (0, import_model.effectiveGameId)(tag);
|
|
296
|
+
if (!boxTags.has(gameId)) boxTags.set(gameId, { box: (0, import_model.effectiveGameId)(box), group: (0, import_model.effectiveGameId)(group) });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
for (const tag of map?.tags ?? []) {
|
|
301
|
+
const zone = (0, import_model.effectiveGameId)(tag);
|
|
302
|
+
const clash = boxTags.get(zone);
|
|
303
|
+
if (clash !== void 0) {
|
|
304
|
+
problems.push(`the project map's zone "${zone}" has the name of tag "${zone}" in box "${clash.box}", group "${clash.group}", so "value.${zone}.<name>" would name two things`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
for (const box of bundle.boxes) {
|
|
308
|
+
const boxName = (0, import_model.effectiveGameId)(box);
|
|
309
|
+
if (box.usesMap === true) {
|
|
310
|
+
if (map === void 0) {
|
|
311
|
+
problems.push(`box "${boxName}" uses the project map, but the bundle has no map`);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
const twin = box.tagGroups.find((g) => (0, import_model.effectiveGameId)(g) === mapName);
|
|
315
|
+
if (twin !== void 0) {
|
|
316
|
+
problems.push(`box "${boxName}" uses the project map and declares its own tag group "${mapName}", the map's name`);
|
|
317
|
+
}
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
if (map === void 0) continue;
|
|
321
|
+
const names = (where) => {
|
|
322
|
+
problems.push(`box "${boxName}" is not on the project map, but ${where} names the map's tag group "${mapName}"`);
|
|
323
|
+
};
|
|
324
|
+
for (const deck of box.decks) {
|
|
325
|
+
for (const card of deck.cards) {
|
|
326
|
+
if (card.tags?.[map.id] !== void 0) names(`card "${(0, import_model.effectiveGameId)(card)}"`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
for (const template of box.handTemplates) {
|
|
330
|
+
if (template.bindings?.[map.id] !== void 0 || template.chooses?.includes(map.id) === true) {
|
|
331
|
+
names(`hand template "${(0, import_model.effectiveGameId)(template)}"`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
for (const hand of box.hands) {
|
|
335
|
+
if (hand.chosen?.[map.id] !== void 0 || hand.rule?.bindings?.[map.id] !== void 0) {
|
|
336
|
+
names(`hand "${(0, import_model.effectiveGameId)(hand)}"`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (problems.length > 0) throw new Error(`bundle refused: ${problems.join("; ")}`);
|
|
341
|
+
};
|
|
272
342
|
var Engine = class _Engine {
|
|
273
343
|
internals;
|
|
274
344
|
seed;
|
|
@@ -285,6 +355,7 @@ var Engine = class _Engine {
|
|
|
285
355
|
* hotSwap puts this engine back exactly as it was. */
|
|
286
356
|
sharedMounts = [];
|
|
287
357
|
constructor(bundle, opts = {}) {
|
|
358
|
+
refuseUnreadableBundle(bundle);
|
|
288
359
|
this.creationOptions = opts;
|
|
289
360
|
this.seed = opts.seed ?? 0;
|
|
290
361
|
this.onReplacedFlow = opts.onReplacedFlow;
|
|
@@ -368,6 +439,10 @@ var Engine = class _Engine {
|
|
|
368
439
|
indexOwner(internals.owners.hand, hand);
|
|
369
440
|
}
|
|
370
441
|
}
|
|
442
|
+
if (bundle.map !== void 0) {
|
|
443
|
+
internals.groupsById.set(bundle.map.group.id, { group: bundle.map.group });
|
|
444
|
+
if (bundle.map.group.required === true) internals.requiredGroups.add(bundle.map.group.id);
|
|
445
|
+
}
|
|
371
446
|
this.initLadders();
|
|
372
447
|
const declSet = (half) => ({
|
|
373
448
|
story: half("story", bundle.story.properties),
|
|
@@ -378,9 +453,9 @@ var Engine = class _Engine {
|
|
|
378
453
|
hand: new Map(bundle.boxes.flatMap((box) => box.hands.map(
|
|
379
454
|
(hand) => [hand.id, half("hand", handDeclsOf(internals, hand))]
|
|
380
455
|
))),
|
|
381
|
-
value: new Map(
|
|
456
|
+
value: new Map((0, import_model.allTagGroups)(bundle).flatMap((group) => group.tags.map(
|
|
382
457
|
(tag) => [tag.id, half("value", tag.properties ?? [])]
|
|
383
|
-
)))
|
|
458
|
+
)))
|
|
384
459
|
});
|
|
385
460
|
internals.flowDecls = declSet(flowHalf);
|
|
386
461
|
internals.sharedDecls = declSet(sharedHalf);
|
|
@@ -481,6 +556,7 @@ var Engine = class _Engine {
|
|
|
481
556
|
}
|
|
482
557
|
for (const hand of box.hands) internals.ladders.hand.set(hand.id, grab(handDeclsOf(internals, hand)));
|
|
483
558
|
}
|
|
559
|
+
for (const tag of b.map?.group.tags ?? []) internals.ladders.value.set(tag.id, grab(tag.properties));
|
|
484
560
|
const any = (m) => [...m.values()].some((x) => x.size > 0);
|
|
485
561
|
internals.hasQualities = internals.ladders.world.size > 0 || internals.ladders.story.size > 0 || any(internals.ladders.box) || any(internals.ladders.deck) || any(internals.ladders.value) || any(internals.ladders.hand);
|
|
486
562
|
}
|
|
@@ -804,11 +880,35 @@ var Engine = class _Engine {
|
|
|
804
880
|
return structuredClone({
|
|
805
881
|
schema: import_model2.SAVE_SCHEMA,
|
|
806
882
|
content: this.internals.bundle.content,
|
|
807
|
-
...this.internals.ownsRegistry ? { registry: this.
|
|
883
|
+
...this.internals.ownsRegistry ? { registry: this.registrySection() } : {},
|
|
808
884
|
shared: { spent: [...this.spent].sort() },
|
|
809
885
|
flows: Object.fromEntries([...this.flowsById].map(([id, flow]) => [id, flow.snapshot(false)]))
|
|
810
886
|
});
|
|
811
887
|
}
|
|
888
|
+
/** The registry's values in CANONICAL order, the order a load rebuilds them
|
|
889
|
+
* in: the engine-wide keys as the constructor registered them, then each
|
|
890
|
+
* flow's keys in `flows()` order (each flow's own registration order), then
|
|
891
|
+
* anything else the registry holds (values still waiting for a key), as the
|
|
892
|
+
* registry lists it. The registry itself lists keys in registration order,
|
|
893
|
+
* and a flow replaced in place (`open()` above keeps its slot in
|
|
894
|
+
* `flowsById`) re-registers its keys at the END, so `openFlow("a");
|
|
895
|
+
* openFlow("b"); openFlow("a")` saved b's keys before a's while a load
|
|
896
|
+
* rebuilt a's first: the same run, different `.storyletsave` bytes, and a
|
|
897
|
+
* save loaded and saved again no longer equal to itself. It is the
|
|
898
|
+
* 2026-08-29 rule carried into the section save@2 moved the per-flow values
|
|
899
|
+
* to (2026-10-01). Order does not matter on READ (`partitionsFromSections`
|
|
900
|
+
* sorts by key shape), so a save written in the old order loads as before. */
|
|
901
|
+
registrySection() {
|
|
902
|
+
const all = this.internals.registry.save();
|
|
903
|
+
const out = {};
|
|
904
|
+
const take = (key) => {
|
|
905
|
+
if (Object.prototype.hasOwnProperty.call(all, key) && !Object.prototype.hasOwnProperty.call(out, key)) out[key] = all[key];
|
|
906
|
+
};
|
|
907
|
+
for (const { key } of this.sharedMounts) take(key);
|
|
908
|
+
for (const flow of this.flowsById.values()) for (const key of flow.registeredKeys()) take(key);
|
|
909
|
+
for (const key of Object.keys(all)) take(key);
|
|
910
|
+
return out;
|
|
911
|
+
}
|
|
812
912
|
/** ONE flow's blob, to park a visit that is walking away: the same shape
|
|
813
913
|
* the envelope carries per flow, and the same shape `openFlow`'s `restore`
|
|
814
914
|
* option takes back (design/engine-server.md 4.1). Saving the whole
|
|
@@ -1138,9 +1238,16 @@ var Flow = class {
|
|
|
1138
1238
|
/** Tag group names are box-scoped: two boxes may name a group the same way
|
|
1139
1239
|
* (schema 1 - boxes namespace their groups), so a name is only ever
|
|
1140
1240
|
* resolved inside the box being asked, never bundle-wide. Ids are
|
|
1141
|
-
* project-unique and accepted here too, still confined to the box.
|
|
1241
|
+
* project-unique and accepted here too, still confined to the box.
|
|
1242
|
+
*
|
|
1243
|
+
* A box on the project map sees ONE namespace: its own groups, then the
|
|
1244
|
+
* map's group (design/project-map-contract.md 3.1). A box that has not
|
|
1245
|
+
* opted in does not see the map's name at all, so a peek naming it there
|
|
1246
|
+
* is the ordinary unknown-group refusal. Own groups first is stated for
|
|
1247
|
+
* determinism only: a bundle that loads never has the two share a name. */
|
|
1142
1248
|
groupInBox(box, ref) {
|
|
1143
|
-
|
|
1249
|
+
const groups = (0, import_model.groupsOfBox)(this.internals.bundle, box);
|
|
1250
|
+
return groups.find((g) => (0, import_model.effectiveGameId)(g) === ref) ?? groups.find((g) => g.id === ref);
|
|
1144
1251
|
}
|
|
1145
1252
|
/** Fold one play into the indexes. O(the card's tags), not O(the log). */
|
|
1146
1253
|
indexPlay(record) {
|
|
@@ -1150,7 +1257,7 @@ var Flow = class {
|
|
|
1150
1257
|
if (!entry) return;
|
|
1151
1258
|
for (const [groupId, tagIds] of Object.entries(entry.card.tags ?? {})) {
|
|
1152
1259
|
for (const tagId of tagIds) {
|
|
1153
|
-
const key = tagKey(groupId, tagId);
|
|
1260
|
+
const key = tagKey(entry.box.id, groupId, tagId);
|
|
1154
1261
|
this.tagPlayCount.set(key, (this.tagPlayCount.get(key) ?? 0) + 1);
|
|
1155
1262
|
this.lastPlayInTag.set(key, record);
|
|
1156
1263
|
}
|
|
@@ -1166,8 +1273,10 @@ var Flow = class {
|
|
|
1166
1273
|
for (const record of this.playLog) this.indexPlay(record);
|
|
1167
1274
|
}
|
|
1168
1275
|
/** `box` is the box whose ask is being evaluated: the play-history
|
|
1169
|
-
* functions take a bare group name, so it resolves there
|
|
1170
|
-
*
|
|
1276
|
+
* functions take a bare group name, so it resolves there, and they count
|
|
1277
|
+
* only that box's own plays (the box is in the index key). That was
|
|
1278
|
+
* automatic while every group was a box's; a project-map zone is shared,
|
|
1279
|
+
* and its history is still not (design/project-map-contract.md 3.7, D7).
|
|
1171
1280
|
* History is THIS flow's: countPlayed answers "have I done this". */
|
|
1172
1281
|
/** One host per box, built once.
|
|
1173
1282
|
*
|
|
@@ -1192,7 +1301,7 @@ var Flow = class {
|
|
|
1192
1301
|
const keyOf = (group, tag) => {
|
|
1193
1302
|
const found = this.groupInBox(box, group);
|
|
1194
1303
|
const t = found?.tags.find((v) => v.gameId === tag);
|
|
1195
|
-
return found && t ? tagKey(found.id, t.id) : void 0;
|
|
1304
|
+
return found && t ? tagKey(box.id, found.id, t.id) : void 0;
|
|
1196
1305
|
};
|
|
1197
1306
|
const since = (record) => {
|
|
1198
1307
|
const entry = this.internals.cardsByGameId.get(record.card);
|
|
@@ -1399,7 +1508,7 @@ var Flow = class {
|
|
|
1399
1508
|
* is what says otherwise.
|
|
1400
1509
|
*/
|
|
1401
1510
|
bindStateGroups(box, boundTags, askNames) {
|
|
1402
|
-
for (const group of
|
|
1511
|
+
for (const group of (0, import_model.groupsOfBox)(this.internals.bundle, box)) {
|
|
1403
1512
|
if (group.boundBy === void 0 || boundTags.has(group.id)) continue;
|
|
1404
1513
|
const ref = /^@(world|story)\.([a-z][a-z0-9_-]*)$/.exec(group.boundBy);
|
|
1405
1514
|
if (!ref) {
|
|
@@ -2074,12 +2183,12 @@ var handDecls = (hand, box) => {
|
|
|
2074
2183
|
}
|
|
2075
2184
|
return hand.properties ?? [];
|
|
2076
2185
|
};
|
|
2077
|
-
var movableHoles = (hand, box) => {
|
|
2186
|
+
var movableHoles = (bundle, hand, box) => {
|
|
2078
2187
|
const filled = hand.template !== void 0 ? hand.chosen : hand.rule?.bindings;
|
|
2079
2188
|
const out = [];
|
|
2080
2189
|
for (const [groupId, value] of Object.entries(filled ?? {})) {
|
|
2081
2190
|
if (!(0, import_model3.isHoleRef)(value)) continue;
|
|
2082
|
-
const group = box.
|
|
2191
|
+
const group = (0, import_model3.groupsOfBox)(bundle, box).find((g) => g.id === groupId);
|
|
2083
2192
|
if (group === void 0) continue;
|
|
2084
2193
|
out.push({ group: (0, import_model3.effectiveGameId)(group), from: value });
|
|
2085
2194
|
}
|
|
@@ -2104,6 +2213,7 @@ function describeBundle(bundle) {
|
|
|
2104
2213
|
boxes.push({
|
|
2105
2214
|
gameId: boxGameId,
|
|
2106
2215
|
...box.title !== void 0 ? { title: box.title } : {},
|
|
2216
|
+
...box.usesMap === true ? { usesMap: true } : {},
|
|
2107
2217
|
ranking: { specificity: box.ranking.specificity },
|
|
2108
2218
|
...box.turn !== void 0 ? { turn: { seconds: box.turn.seconds } } : {},
|
|
2109
2219
|
...durableCardCount(box) > 0 ? { durableCards: durableCardCount(box) } : {},
|
|
@@ -2127,7 +2237,7 @@ function describeBundle(bundle) {
|
|
|
2127
2237
|
totals.tagGroups += box.tagGroups.length;
|
|
2128
2238
|
for (const hand of box.hands) {
|
|
2129
2239
|
const template = hand.template !== void 0 ? box.handTemplates.find((t) => t.id === hand.template) : void 0;
|
|
2130
|
-
const movable = movableHoles(hand, box);
|
|
2240
|
+
const movable = movableHoles(bundle, hand, box);
|
|
2131
2241
|
hands.push({
|
|
2132
2242
|
gameId: (0, import_model3.effectiveGameId)(hand),
|
|
2133
2243
|
...hand.title !== void 0 ? { title: hand.title } : {},
|
|
@@ -2156,6 +2266,15 @@ function describeBundle(bundle) {
|
|
|
2156
2266
|
}
|
|
2157
2267
|
}
|
|
2158
2268
|
}
|
|
2269
|
+
const map = bundle.map;
|
|
2270
|
+
if (map !== void 0) {
|
|
2271
|
+
const group = (0, import_model3.effectiveGameId)(map.group);
|
|
2272
|
+
for (const tag of map.group.tags) {
|
|
2273
|
+
const decls = tag.properties ?? [];
|
|
2274
|
+
if (decls.length > 0) properties.push({ scope: "tag", owner: (0, import_model3.effectiveGameId)(tag), group, properties: summarise(decls) });
|
|
2275
|
+
}
|
|
2276
|
+
totals.tagGroups += 1;
|
|
2277
|
+
}
|
|
2159
2278
|
return {
|
|
2160
2279
|
identity: {
|
|
2161
2280
|
schema: bundle.schema,
|
|
@@ -2168,13 +2287,16 @@ function describeBundle(bundle) {
|
|
|
2168
2287
|
boxes,
|
|
2169
2288
|
hands,
|
|
2170
2289
|
properties,
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2290
|
+
...map !== void 0 ? {
|
|
2291
|
+
map: {
|
|
2292
|
+
group: (0, import_model3.effectiveGameId)(map.group),
|
|
2293
|
+
tags: map.group.tags.map((tag) => (0, import_model3.effectiveGameId)(tag)),
|
|
2294
|
+
boxes: bundle.boxes.filter((box) => box.usesMap === true).map((box) => (0, import_model3.effectiveGameId)(box)),
|
|
2295
|
+
zones: map.geometry?.zones.length ?? 0,
|
|
2296
|
+
backgrounds: map.geometry?.backgrounds?.length ?? 0,
|
|
2297
|
+
sites: Object.fromEntries(Object.entries(map.geometry?.sites ?? {}).map(([box, sites]) => [box, sites.length]))
|
|
2298
|
+
}
|
|
2299
|
+
} : {}
|
|
2178
2300
|
};
|
|
2179
2301
|
}
|
|
2180
2302
|
// Annotate the CommonJS export names for ESM import in node:
|