@dreamboard-games/sdk 0.5.0-alpha.2 → 0.5.0-alpha.4

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.
Files changed (151) hide show
  1. package/README.md +186 -45
  2. package/dist/board-target-B0QG6OYx.d.ts +8 -0
  3. package/dist/chunk-3JXUL4VN.js +139 -0
  4. package/dist/chunk-3JXUL4VN.js.map +1 -0
  5. package/dist/{chunk-S2JEDVWH.js → chunk-C77SYIVT.js} +6102 -6505
  6. package/dist/chunk-C77SYIVT.js.map +1 -0
  7. package/dist/chunk-DMFVAZI2.js +16 -0
  8. package/dist/chunk-DMFVAZI2.js.map +1 -0
  9. package/dist/chunk-OOFMBCFF.js +1338 -0
  10. package/dist/chunk-OOFMBCFF.js.map +1 -0
  11. package/dist/chunk-UG2Q4NLG.js +590 -0
  12. package/dist/chunk-UG2Q4NLG.js.map +1 -0
  13. package/dist/chunk-XU6WJW6K.js +847 -0
  14. package/dist/chunk-XU6WJW6K.js.map +1 -0
  15. package/dist/diagnostics-B8d5aLad.d.ts +65 -0
  16. package/dist/extract-gVKcB8Og.d.ts +4736 -0
  17. package/dist/index.d.ts +3782 -1
  18. package/dist/index.js +1096 -6
  19. package/dist/index.js.map +1 -1
  20. package/dist/model-vdpLHrxh.d.ts +432 -0
  21. package/dist/react.d.ts +60 -0
  22. package/dist/react.js +208 -0
  23. package/dist/react.js.map +1 -0
  24. package/dist/reducer.d.ts +1244 -78
  25. package/dist/reducer.js +4204 -121
  26. package/dist/reducer.js.map +1 -1
  27. package/dist/testing.d.ts +520 -564
  28. package/dist/testing.js +2451 -915
  29. package/dist/testing.js.map +1 -1
  30. package/dist/types-Bj5ZLTUV.d.ts +114 -0
  31. package/package.json +40 -142
  32. package/dist/HandView-Bi2lxzQP.d.ts +0 -79
  33. package/dist/ResourceCounter-BFxJknqp.d.ts +0 -98
  34. package/dist/ThemeProvider-CElaL2ik.d.ts +0 -99
  35. package/dist/attributes-DbvyMbXw.d.ts +0 -68
  36. package/dist/browser-interaction.d.ts +0 -910
  37. package/dist/browser-interaction.js +0 -119
  38. package/dist/browser-interaction.js.map +0 -1
  39. package/dist/chunk-32PFKDV7.js +0 -11705
  40. package/dist/chunk-32PFKDV7.js.map +0 -1
  41. package/dist/chunk-3BAD4VC2.js +0 -263
  42. package/dist/chunk-3BAD4VC2.js.map +0 -1
  43. package/dist/chunk-3SKDNDPG.js +0 -104
  44. package/dist/chunk-3SKDNDPG.js.map +0 -1
  45. package/dist/chunk-5YV2WHJ4.js +0 -25
  46. package/dist/chunk-5YV2WHJ4.js.map +0 -1
  47. package/dist/chunk-ANRB4XKA.js +0 -90
  48. package/dist/chunk-ANRB4XKA.js.map +0 -1
  49. package/dist/chunk-BI4G3PM2.js +0 -1864
  50. package/dist/chunk-BI4G3PM2.js.map +0 -1
  51. package/dist/chunk-EVCLTY4Q.js +0 -935
  52. package/dist/chunk-EVCLTY4Q.js.map +0 -1
  53. package/dist/chunk-GDPKTBUR.js +0 -564
  54. package/dist/chunk-GDPKTBUR.js.map +0 -1
  55. package/dist/chunk-H6VDGFL5.js +0 -2903
  56. package/dist/chunk-H6VDGFL5.js.map +0 -1
  57. package/dist/chunk-LR3ZTWQF.js +0 -5960
  58. package/dist/chunk-LR3ZTWQF.js.map +0 -1
  59. package/dist/chunk-PF7L4BMG.js +0 -117
  60. package/dist/chunk-PF7L4BMG.js.map +0 -1
  61. package/dist/chunk-PN5O6GG2.js +0 -32
  62. package/dist/chunk-PN5O6GG2.js.map +0 -1
  63. package/dist/chunk-PZ5AY32C.js +0 -10
  64. package/dist/chunk-PZ5AY32C.js.map +0 -1
  65. package/dist/chunk-S2JEDVWH.js.map +0 -1
  66. package/dist/chunk-T3ZKNUZ7.js +0 -1
  67. package/dist/chunk-T3ZKNUZ7.js.map +0 -1
  68. package/dist/chunk-UGH54WZ2.js +0 -223
  69. package/dist/chunk-UGH54WZ2.js.map +0 -1
  70. package/dist/chunk-VCDWP5VL.js +0 -746
  71. package/dist/chunk-VCDWP5VL.js.map +0 -1
  72. package/dist/chunk-VDXOF4FW.js +0 -69
  73. package/dist/chunk-VDXOF4FW.js.map +0 -1
  74. package/dist/chunk-WHR5UW3F.js +0 -1988
  75. package/dist/chunk-WHR5UW3F.js.map +0 -1
  76. package/dist/chunk-Y75CFE77.js +0 -17
  77. package/dist/chunk-Y75CFE77.js.map +0 -1
  78. package/dist/chunk-Z7QCREWI.js +0 -3435
  79. package/dist/chunk-Z7QCREWI.js.map +0 -1
  80. package/dist/chunk-ZZGN2ATS.js +0 -1941
  81. package/dist/chunk-ZZGN2ATS.js.map +0 -1
  82. package/dist/components-gAbIz7hK.d.ts +0 -1284
  83. package/dist/definitions-BgmV_GhC.d.ts +0 -300
  84. package/dist/diagnostics-DIi3ina7.d.ts +0 -50
  85. package/dist/digest.d.ts +0 -8
  86. package/dist/game-CsZScpcn.d.ts +0 -763
  87. package/dist/hex-board-view-aKkblDp6.d.ts +0 -1228
  88. package/dist/index-CcT9Q7N7.d.ts +0 -193
  89. package/dist/index.d-DjzoK7zn.d.ts +0 -2101
  90. package/dist/package-set.d.ts +0 -13
  91. package/dist/package-set.js +0 -12
  92. package/dist/package-set.js.map +0 -1
  93. package/dist/player-state-Cqpyeql0.d.ts +0 -371
  94. package/dist/plugin-runtime-contract.d.ts +0 -17
  95. package/dist/plugin-runtime-contract.js +0 -92
  96. package/dist/plugin-runtime-contract.js.map +0 -1
  97. package/dist/primitive-props-BNHDkgd7.d.ts +0 -16
  98. package/dist/protocol-dYgafTYY.d.ts +0 -314
  99. package/dist/reducer/advanced.d.ts +0 -82
  100. package/dist/reducer/advanced.js +0 -51
  101. package/dist/reducer/advanced.js.map +0 -1
  102. package/dist/reducer-contract.d.ts +0 -11
  103. package/dist/reducer-contract.js +0 -16
  104. package/dist/reducer-contract.js.map +0 -1
  105. package/dist/reference-games/index.d.ts +0 -31
  106. package/dist/reference-games/index.js +0 -48
  107. package/dist/reference-games/index.js.map +0 -1
  108. package/dist/runtime/primitives.d.ts +0 -250
  109. package/dist/runtime/primitives.js +0 -189
  110. package/dist/runtime/primitives.js.map +0 -1
  111. package/dist/runtime/runtime-api.d.ts +0 -2
  112. package/dist/runtime/runtime-api.js +0 -1
  113. package/dist/runtime/runtime-api.js.map +0 -1
  114. package/dist/runtime/workspace-contract.d.ts +0 -392
  115. package/dist/runtime/workspace-contract.js +0 -28
  116. package/dist/runtime/workspace-contract.js.map +0 -1
  117. package/dist/runtime-RJ5orDJU.d.ts +0 -1699
  118. package/dist/runtime-api-Bz5pwNU_.d.ts +0 -296
  119. package/dist/runtime-json-CQ9QbLZ5.d.ts +0 -5
  120. package/dist/runtime.d.ts +0 -83
  121. package/dist/runtime.js +0 -242
  122. package/dist/runtime.js.map +0 -1
  123. package/dist/schema.d.ts +0 -379
  124. package/dist/stale-contract-artifact-error-XLaweZtF.d.ts +0 -18
  125. package/dist/testing-compiler.d.ts +0 -37
  126. package/dist/testing-compiler.js +0 -272
  127. package/dist/testing-compiler.js.map +0 -1
  128. package/dist/testing-runtime.d.ts +0 -119
  129. package/dist/testing-runtime.js +0 -212
  130. package/dist/testing-runtime.js.map +0 -1
  131. package/dist/types-DJj5MJkl.d.ts +0 -256
  132. package/dist/types-DR7DoB1x.d.ts +0 -28
  133. package/dist/types-JWCYHmu7.d.ts +0 -122
  134. package/dist/types.d.ts +0 -1958
  135. package/dist/types.js +0 -14
  136. package/dist/types.js.map +0 -1
  137. package/dist/ui/components.d.ts +0 -17
  138. package/dist/ui/components.js +0 -216
  139. package/dist/ui/components.js.map +0 -1
  140. package/dist/ui/defaults.d.ts +0 -19
  141. package/dist/ui/defaults.js +0 -104
  142. package/dist/ui/defaults.js.map +0 -1
  143. package/dist/ui/player-state.d.ts +0 -2
  144. package/dist/ui/player-state.js +0 -1
  145. package/dist/ui/player-state.js.map +0 -1
  146. package/dist/ui/plugin-styles.css +0 -2
  147. package/dist/ui-contract-GTUPkf8z.d.ts +0 -1168
  148. package/dist/ui.d.ts +0 -317
  149. package/dist/ui.js +0 -277
  150. package/dist/ui.js.map +0 -1
  151. package/dist/views-BpuWcOyN.d.ts +0 -1641
@@ -1,3435 +0,0 @@
1
- // src/reducer/manifest/hex-geometry.ts
2
- var HEX_CORNERS = ["ne-e", "e-se", "se-sw", "sw-w", "w-nw", "nw-ne"];
3
- var HEX_CORNER_OFFSETS = {
4
- "ne-e": [2, -1, -1],
5
- "e-se": [1, -2, 1],
6
- "se-sw": [-1, -1, 2],
7
- "sw-w": [-2, 1, 1],
8
- "w-nw": [-1, 2, -1],
9
- "nw-ne": [1, 1, -2]
10
- };
11
- function cubeFromAxial(space) {
12
- const x = space.q;
13
- const z = space.r;
14
- return [x, -x - z, z];
15
- }
16
- function cornerGeometryKey(space, corner) {
17
- const [x, y, z] = cubeFromAxial(space);
18
- const [dx, dy, dz] = HEX_CORNER_OFFSETS[corner];
19
- return `${3 * x + dx},${3 * y + dy},${3 * z + dz}`;
20
- }
21
- function resolveHexVertexGeometryKey(ref, spacesById) {
22
- const resolvedSpaces = [...ref.spaces].sort((left, right) => left.localeCompare(right)).map((spaceId) => {
23
- const space = spacesById.get(spaceId);
24
- if (!space) {
25
- throw new Error(
26
- `Hex vertex ref references unknown space '${spaceId}'.`
27
- );
28
- }
29
- return space;
30
- });
31
- const keyCounts = /* @__PURE__ */ new Map();
32
- for (const space of resolvedSpaces) {
33
- for (const corner of HEX_CORNERS) {
34
- const key = cornerGeometryKey(space, corner);
35
- keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
36
- }
37
- }
38
- const candidates = [...keyCounts.entries()].filter(([, count]) => count === resolvedSpaces.length).map(([key]) => key).sort((left, right) => left.localeCompare(right));
39
- if (candidates.length !== 1) {
40
- throw new Error(
41
- `Hex vertex ref spaces '${ref.spaces.join(", ")}' do not resolve to exactly one shared vertex.`
42
- );
43
- }
44
- const vertexKey = candidates[0];
45
- if (vertexKey === void 0) {
46
- throw new Error("internal: expected exactly one hex vertex geometry key");
47
- }
48
- return vertexKey;
49
- }
50
-
51
- // src/reducer/manifest/preset-card-sets.ts
52
- var STANDARD_DECK_ID = "standard_52_deck";
53
- function createEnumPropertySchema(enums, description) {
54
- return {
55
- type: "enum",
56
- description,
57
- enums
58
- };
59
- }
60
- function createStringPropertySchema(description) {
61
- return {
62
- type: "string",
63
- description
64
- };
65
- }
66
- function createStandard52CardDeck(defaultHome = { type: "detached" }) {
67
- const suits = ["SPADES", "HEARTS", "CLUBS", "DIAMONDS"];
68
- const ranks = [
69
- "3",
70
- "4",
71
- "5",
72
- "6",
73
- "7",
74
- "8",
75
- "9",
76
- "10",
77
- "J",
78
- "Q",
79
- "K",
80
- "A",
81
- "2"
82
- ];
83
- const cards = [];
84
- for (const suit of suits) {
85
- for (const rank of ranks) {
86
- cards.push({
87
- type: `${suit}_${rank}`,
88
- name: `${rank} of ${suit}`,
89
- imageUrl: `/cards/${suit.toLowerCase()}_${rank.toLowerCase()}.png`,
90
- text: `A playing card: ${rank} of ${suit}. Value: ${getBigTwoCardValue(rank)}, Suit value: ${getSuitValue(suit)}`,
91
- count: 1,
92
- cardType: `${suit}_${rank}`,
93
- properties: {
94
- suit,
95
- rank
96
- }
97
- });
98
- }
99
- }
100
- return {
101
- id: STANDARD_DECK_ID,
102
- name: "Standard 52-Card Deck",
103
- type: "manual",
104
- defaultHome,
105
- cards,
106
- cardSchema: {
107
- properties: {
108
- suit: createEnumPropertySchema(
109
- ["SPADES", "HEARTS", "CLUBS", "DIAMONDS"],
110
- "The suit of the card"
111
- ),
112
- rank: createStringPropertySchema("The rank of the card")
113
- }
114
- }
115
- };
116
- }
117
- function addStandardDecksIfNeeded(manifest) {
118
- return {
119
- ...manifest,
120
- cardSets: manifest.cardSets.map(
121
- (cardSet) => cardSet.type === "preset" && cardSet.presetId === STANDARD_DECK_ID ? {
122
- ...createStandard52CardDeck(cardSet.defaultHome),
123
- id: cardSet.id,
124
- name: cardSet.name
125
- } : cardSet
126
- )
127
- };
128
- }
129
- function materializePresetCardSet(presetCardSet) {
130
- if (presetCardSet.presetId !== STANDARD_DECK_ID) {
131
- throw new Error(`Unsupported preset deck: ${presetCardSet.presetId}`);
132
- }
133
- const standardDeck = createStandard52CardDeck(presetCardSet.defaultHome);
134
- return {
135
- ...standardDeck,
136
- id: presetCardSet.id,
137
- name: presetCardSet.name
138
- };
139
- }
140
- function materializeCardSet(cardSet) {
141
- return cardSet.type === "manual" ? cardSet : materializePresetCardSet(cardSet);
142
- }
143
- function getBigTwoCardValue(rank) {
144
- switch (rank) {
145
- case "3":
146
- return 3;
147
- case "4":
148
- return 4;
149
- case "5":
150
- return 5;
151
- case "6":
152
- return 6;
153
- case "7":
154
- return 7;
155
- case "8":
156
- return 8;
157
- case "9":
158
- return 9;
159
- case "10":
160
- return 10;
161
- case "J":
162
- return 11;
163
- case "Q":
164
- return 12;
165
- case "K":
166
- return 13;
167
- case "A":
168
- return 14;
169
- case "2":
170
- return 15;
171
- default:
172
- return 0;
173
- }
174
- }
175
- function getSuitValue(suit) {
176
- switch (suit) {
177
- case "DIAMONDS":
178
- return 1;
179
- case "CLUBS":
180
- return 2;
181
- case "HEARTS":
182
- return 3;
183
- case "SPADES":
184
- return 4;
185
- default:
186
- return 0;
187
- }
188
- }
189
-
190
- // src/reducer/manifest/manifest-validation.ts
191
- function isHexBoardTemplateSpec(boardTemplate) {
192
- return boardTemplate.layout === "hex";
193
- }
194
- function isHexBoardSpec(board) {
195
- return board.layout === "hex";
196
- }
197
- function collectDuplicateIdIssues(options) {
198
- const issues = [];
199
- const pathsById = /* @__PURE__ */ new Map();
200
- for (const entry of options.entries) {
201
- const id = entry.id?.trim();
202
- if (!id) {
203
- continue;
204
- }
205
- const paths = pathsById.get(id) ?? [];
206
- paths.push(entry.path);
207
- pathsById.set(id, paths);
208
- }
209
- for (const [id, paths] of pathsById.entries()) {
210
- if (paths.length > 1) {
211
- issues.push(`${paths.join(", ")}: Duplicate ${options.label} '${id}'.`);
212
- }
213
- }
214
- return issues;
215
- }
216
- var PROTOTYPE_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
217
- "__proto__",
218
- "prototype",
219
- "constructor"
220
- ]);
221
- function validateRecordKey(value, path) {
222
- if (!value || !PROTOTYPE_SENSITIVE_KEYS.has(value)) {
223
- return [];
224
- }
225
- return [
226
- `${path}: '${value}' is reserved and cannot be used as a generated record key.`
227
- ];
228
- }
229
- function collectKeyIssues(entries) {
230
- return entries.flatMap(({ value, path }) => validateRecordKey(value, path));
231
- }
232
- function toHandleKey(input) {
233
- if (!/[_-]/.test(input)) {
234
- return input.charAt(0).toLowerCase() + input.slice(1);
235
- }
236
- const [first = "", ...rest] = input.split(/[_-]/g).filter(Boolean);
237
- return [
238
- first.toLowerCase(),
239
- ...rest.map((part) => {
240
- const lower = part.toLowerCase();
241
- return lower.charAt(0).toUpperCase() + lower.slice(1);
242
- })
243
- ].join("");
244
- }
245
- function collectHandleKeyCollisions(values, label) {
246
- const idsByHandle = /* @__PURE__ */ new Map();
247
- for (const value of values) {
248
- const handle = toHandleKey(value);
249
- idsByHandle.set(handle, [...idsByHandle.get(handle) ?? [], value]);
250
- }
251
- return [...idsByHandle.entries()].filter(([, ids]) => ids.length > 1).map(
252
- ([handle, ids]) => `${label} values ${ids.join(", ")} all generate handle '${handle}'.`
253
- );
254
- }
255
- function dedupeSorted(values) {
256
- return Array.from(new Set(values)).sort();
257
- }
258
- function renderCardInstanceIds(card) {
259
- return card.count > 1 ? Array.from(
260
- { length: card.count },
261
- (_, index) => `${card.type}-${index + 1}`
262
- ) : [card.type];
263
- }
264
- function collectPropertySchemaKeyIssues(schema, path) {
265
- if (!schema) {
266
- return [];
267
- }
268
- const issues = [];
269
- if (schema.type === "object") {
270
- for (const [key, property] of Object.entries(schema.properties ?? {})) {
271
- issues.push(
272
- ...validateRecordKey(key, `${path}.properties.${key}`),
273
- ...collectPropertySchemaKeyIssues(
274
- property,
275
- `${path}.properties.${key}`
276
- )
277
- );
278
- }
279
- }
280
- if (schema.type === "array") {
281
- issues.push(
282
- ...collectPropertySchemaKeyIssues(schema.items, `${path}.items`)
283
- );
284
- }
285
- if (schema.type === "record") {
286
- issues.push(
287
- ...collectPropertySchemaKeyIssues(schema.values, `${path}.values`)
288
- );
289
- }
290
- return issues;
291
- }
292
- function collectObjectSchemaKeyIssues(schema, path) {
293
- const issues = [];
294
- for (const [key, property] of Object.entries(schema?.properties ?? {})) {
295
- issues.push(
296
- ...validateRecordKey(key, `${path}.properties.${key}`),
297
- ...collectPropertySchemaKeyIssues(property, `${path}.properties.${key}`)
298
- );
299
- }
300
- return issues;
301
- }
302
- function collectCardSchemaKeyIssues(cardSet, path) {
303
- if (cardSet.type !== "manual") {
304
- return [];
305
- }
306
- const schema = cardSet.cardSchema;
307
- if ("variants" in schema) {
308
- return [
309
- ...Object.entries(schema.shared ?? {}).flatMap(([key, property]) => [
310
- ...validateRecordKey(key, `${path}.cardSchema.shared.${key}`),
311
- ...collectPropertySchemaKeyIssues(
312
- property,
313
- `${path}.cardSchema.shared.${key}`
314
- )
315
- ]),
316
- ...Object.entries(schema.variants).flatMap(
317
- ([variantKey, variantSchema]) => [
318
- ...validateRecordKey(
319
- variantKey,
320
- `${path}.cardSchema.variants.${variantKey}`
321
- ),
322
- ...collectObjectSchemaKeyIssues(
323
- variantSchema,
324
- `${path}.cardSchema.variants.${variantKey}`
325
- )
326
- ]
327
- )
328
- ];
329
- }
330
- return collectObjectSchemaKeyIssues(schema, `${path}.cardSchema`);
331
- }
332
- function expandSeedIds(seeds) {
333
- return seeds.flatMap((seed) => {
334
- const count = seed.count ?? 1;
335
- const baseId = seed.id ?? seed.typeId;
336
- return count > 1 ? Array.from({ length: count }, (_, index) => `${baseId}-${index + 1}`) : [baseId];
337
- });
338
- }
339
- function validateTypeSlotDuplicates(options) {
340
- const issues = [];
341
- for (const [index, pieceType] of options.pieceTypes.entries()) {
342
- issues.push(
343
- ...collectDuplicateIdIssues({
344
- entries: (pieceType.slots ?? []).map((slot, slotIndex) => ({
345
- id: slot.id,
346
- path: `manifest.pieceTypes[${index}].slots[${slotIndex}].id`
347
- })),
348
- label: "piece slot id"
349
- })
350
- );
351
- }
352
- for (const [index, dieType] of options.dieTypes.entries()) {
353
- issues.push(
354
- ...collectDuplicateIdIssues({
355
- entries: (dieType.slots ?? []).map((slot, slotIndex) => ({
356
- id: slot.id,
357
- path: `manifest.dieTypes[${index}].slots[${slotIndex}].id`
358
- })),
359
- label: "die slot id"
360
- })
361
- );
362
- }
363
- return issues;
364
- }
365
- function validateSlotHostsAndHomes(manifest) {
366
- const issues = [];
367
- const pieceTypesById = new Map(
368
- (manifest.pieceTypes ?? []).map(
369
- (pieceType) => [pieceType.id, pieceType]
370
- )
371
- );
372
- const dieTypesById = new Map(
373
- (manifest.dieTypes ?? []).map((dieType) => [dieType.id, dieType])
374
- );
375
- const slotIdsByHostKey = /* @__PURE__ */ new Map();
376
- for (const [index, seed] of (manifest.pieceSeeds ?? []).entries()) {
377
- const pieceType = pieceTypesById.get(seed.typeId);
378
- const slotIds = (pieceType?.slots ?? []).map((slot) => slot.id);
379
- if (slotIds.length === 0) {
380
- continue;
381
- }
382
- if (typeof seed.id !== "string" || seed.id.length === 0) {
383
- issues.push(
384
- `manifest.pieceSeeds[${index}].id: Piece seed for slot-bearing type '${seed.typeId}' must declare an explicit id.`
385
- );
386
- continue;
387
- }
388
- if ((seed.count ?? 1) !== 1) {
389
- issues.push(
390
- `manifest.pieceSeeds[${index}].count: Piece seed '${seed.id}' for slot-bearing type '${seed.typeId}' must omit count or set it to 1.`
391
- );
392
- continue;
393
- }
394
- slotIdsByHostKey.set(`piece:${seed.id}`, new Set(slotIds));
395
- }
396
- for (const [index, seed] of (manifest.dieSeeds ?? []).entries()) {
397
- const dieType = dieTypesById.get(seed.typeId);
398
- const slotIds = (dieType?.slots ?? []).map((slot) => slot.id);
399
- if (slotIds.length === 0) {
400
- continue;
401
- }
402
- if (typeof seed.id !== "string" || seed.id.length === 0) {
403
- issues.push(
404
- `manifest.dieSeeds[${index}].id: Die seed for slot-bearing type '${seed.typeId}' must declare an explicit id.`
405
- );
406
- continue;
407
- }
408
- if ((seed.count ?? 1) !== 1) {
409
- issues.push(
410
- `manifest.dieSeeds[${index}].count: Die seed '${seed.id}' for slot-bearing type '${seed.typeId}' must omit count or set it to 1.`
411
- );
412
- continue;
413
- }
414
- slotIdsByHostKey.set(`die:${seed.id}`, new Set(slotIds));
415
- }
416
- const validateHome = (home, path) => {
417
- if (home?.type !== "slot") {
418
- return;
419
- }
420
- const hostKey = `${home.host.kind}:${home.host.id}`;
421
- const slotIds = slotIdsByHostKey.get(hostKey);
422
- if (!slotIds) {
423
- issues.push(
424
- `${path}.host: Unknown strict slot host '${home.host.kind}:${home.host.id}'. Hosts must be singleton piece/die seeds whose type declares slots.`
425
- );
426
- return;
427
- }
428
- if (!slotIds.has(home.slotId)) {
429
- issues.push(
430
- `${path}.slotId: Unknown slot '${home.slotId}' for host '${home.host.kind}:${home.host.id}'.`
431
- );
432
- }
433
- };
434
- for (const [cardSetIndex, cardSet] of manifest.cardSets.entries()) {
435
- if (cardSet.type !== "manual") {
436
- continue;
437
- }
438
- validateHome(
439
- cardSet.defaultHome,
440
- `manifest.cardSets[${cardSetIndex}].defaultHome`
441
- );
442
- for (const [cardIndex, card] of cardSet.cards.entries()) {
443
- validateHome(
444
- card.home,
445
- `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].home`
446
- );
447
- }
448
- }
449
- for (const [index, seed] of (manifest.pieceSeeds ?? []).entries()) {
450
- validateHome(seed.home, `manifest.pieceSeeds[${index}].home`);
451
- }
452
- for (const [index, seed] of (manifest.dieSeeds ?? []).entries()) {
453
- validateHome(seed.home, `manifest.dieSeeds[${index}].home`);
454
- }
455
- return issues;
456
- }
457
- function validateSetupProfileReferences(manifest) {
458
- const issues = [];
459
- const optionChoiceIdsByOptionId = new Map(
460
- (manifest.setupOptions ?? []).map((option, optionIndex) => {
461
- const choiceIds = /* @__PURE__ */ new Set();
462
- for (const choice of option.choices ?? []) {
463
- choiceIds.add(choice.id);
464
- }
465
- if (!option.id) {
466
- issues.push(
467
- `manifest.setupOptions[${optionIndex}].id: Missing option id.`
468
- );
469
- }
470
- return [option.id, choiceIds];
471
- })
472
- );
473
- for (const [profileIndex, profile] of (manifest.setupProfiles ?? []).entries()) {
474
- const optionValues = profile.optionValues ?? {};
475
- for (const [optionId, choiceId] of Object.entries(optionValues)) {
476
- const allowedChoices = optionChoiceIdsByOptionId.get(optionId);
477
- if (!allowedChoices) {
478
- issues.push(
479
- `manifest.setupProfiles[${profileIndex}].optionValues.${optionId}: Unknown setup option '${optionId}'.`
480
- );
481
- continue;
482
- }
483
- if (!allowedChoices.has(choiceId)) {
484
- issues.push(
485
- `manifest.setupProfiles[${profileIndex}].optionValues.${optionId}: Unknown choice '${choiceId}' for setup option '${optionId}'.`
486
- );
487
- }
488
- }
489
- }
490
- return issues;
491
- }
492
- function validatePlayerScopedSeedHomes(manifest) {
493
- const issues = [];
494
- const boardScopeById = new Map(
495
- (manifest.boards ?? []).map((board) => [board.id, board.scope])
496
- );
497
- const zoneScopeById = new Map(
498
- (manifest.zones ?? []).map((zone) => [zone.id, zone.scope])
499
- );
500
- const validateSeedHome = (seed, path, label) => {
501
- const authoredId = seed.id ?? seed.typeId;
502
- if (seed.ownerId) {
503
- return;
504
- }
505
- if (homeTargetsBoard(seed.home) && boardScopeById.get(seed.home.boardId) === "perPlayer") {
506
- issues.push(
507
- `${path}.boardId: ${label} '${authoredId}' requires ownerId because board '${seed.home.boardId}' has scope 'perPlayer'. Add ownerId to resolve the player-scoped destination.`
508
- );
509
- return;
510
- }
511
- if (seed.home?.type === "zone" && zoneScopeById.get(seed.home.zoneId) === "perPlayer") {
512
- issues.push(
513
- `${path}.zoneId: ${label} '${authoredId}' requires ownerId because zone '${seed.home.zoneId}' has scope 'perPlayer'. Add ownerId to resolve the player-scoped destination.`
514
- );
515
- }
516
- };
517
- for (const [index, seed] of (manifest.pieceSeeds ?? []).entries()) {
518
- validateSeedHome(seed, `manifest.pieceSeeds[${index}].home`, "Piece seed");
519
- }
520
- for (const [index, seed] of (manifest.dieSeeds ?? []).entries()) {
521
- validateSeedHome(seed, `manifest.dieSeeds[${index}].home`, "Die seed");
522
- }
523
- return issues;
524
- }
525
- function validateCardHomes(manifest) {
526
- const issues = [];
527
- const boardScopeById = new Map(
528
- (manifest.boards ?? []).map((board) => [board.id, board.scope])
529
- );
530
- const zoneScopeById = new Map(
531
- (manifest.zones ?? []).map((zone) => [zone.id, zone.scope])
532
- );
533
- for (const [cardSetIndex, cardSet] of manifest.cardSets.entries()) {
534
- if (!cardSet.defaultHome) {
535
- issues.push(
536
- `manifest.cardSets[${cardSetIndex}].defaultHome: ${cardSet.type === "manual" ? "Manual" : "Preset"} card sets must declare defaultHome.`
537
- );
538
- continue;
539
- }
540
- const validateCardHome = (home, path, label) => {
541
- if (home?.type === "zone" && zoneScopeById.get(home.zoneId) === "perPlayer") {
542
- issues.push(
543
- `${path}.zoneId: ${label} cannot target per-player zone '${home.zoneId}' because card inventory has no ownerId. Place it during reducer setup instead.`
544
- );
545
- }
546
- if (homeTargetsBoard(home) && boardScopeById.get(home.boardId) === "perPlayer") {
547
- issues.push(
548
- `${path}.boardId: ${label} cannot target per-player board '${home.boardId}' because card inventory has no ownerId. Place it during reducer setup instead.`
549
- );
550
- }
551
- };
552
- validateCardHome(
553
- cardSet.defaultHome,
554
- `manifest.cardSets[${cardSetIndex}].defaultHome`,
555
- `Card set '${cardSet.id}' defaultHome`
556
- );
557
- for (const [cardIndex, card] of (cardSet.type === "manual" ? cardSet.cards : []).entries()) {
558
- const path = `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].home`;
559
- validateCardHome(card.home, path, `Card '${card.type}'`);
560
- }
561
- }
562
- return issues;
563
- }
564
- function homeTargetsBoard(home) {
565
- return home?.type === "space" || home?.type === "container" || home?.type === "edge" || home?.type === "vertex";
566
- }
567
- function validateBoardTemplateDuplicates(boardTemplates) {
568
- const issues = [];
569
- for (const [index, boardTemplate] of boardTemplates.entries()) {
570
- if (isHexBoardTemplateSpec(boardTemplate)) {
571
- issues.push(
572
- ...collectDuplicateIdIssues({
573
- entries: (boardTemplate.spaces ?? []).map((space, spaceIndex) => ({
574
- id: space.id,
575
- path: `manifest.boardTemplates[${index}].spaces[${spaceIndex}].id`
576
- })),
577
- label: "space id"
578
- })
579
- );
580
- continue;
581
- }
582
- issues.push(
583
- ...collectDuplicateIdIssues({
584
- entries: (boardTemplate.spaces ?? []).map((space, spaceIndex) => ({
585
- id: space.id,
586
- path: `manifest.boardTemplates[${index}].spaces[${spaceIndex}].id`
587
- })),
588
- label: "space id"
589
- })
590
- );
591
- issues.push(
592
- ...collectDuplicateIdIssues({
593
- entries: (boardTemplate.containers ?? []).map(
594
- (container, containerIndex) => ({
595
- id: container.id,
596
- path: `manifest.boardTemplates[${index}].containers[${containerIndex}].id`
597
- })
598
- ),
599
- label: "container id"
600
- })
601
- );
602
- issues.push(
603
- ...collectDuplicateIdIssues({
604
- entries: (boardTemplate.relations ?? []).map(
605
- (relation, relationIndex) => ({
606
- id: relation.id,
607
- path: `manifest.boardTemplates[${index}].relations[${relationIndex}].id`
608
- })
609
- ),
610
- label: "relation id"
611
- })
612
- );
613
- }
614
- return issues;
615
- }
616
- function validateBoardDuplicates(boards) {
617
- const issues = [];
618
- for (const [index, board] of boards.entries()) {
619
- if (isHexBoardSpec(board)) {
620
- issues.push(
621
- ...collectDuplicateIdIssues({
622
- entries: (board.spaces ?? []).map((space, spaceIndex) => ({
623
- id: space.id,
624
- path: `manifest.boards[${index}].spaces[${spaceIndex}].id`
625
- })),
626
- label: "space id"
627
- })
628
- );
629
- continue;
630
- }
631
- issues.push(
632
- ...collectDuplicateIdIssues({
633
- entries: (board.spaces ?? []).map((space, spaceIndex) => ({
634
- id: space.id,
635
- path: `manifest.boards[${index}].spaces[${spaceIndex}].id`
636
- })),
637
- label: "space id"
638
- })
639
- );
640
- issues.push(
641
- ...collectDuplicateIdIssues({
642
- entries: (board.containers ?? []).map((container, containerIndex) => ({
643
- id: container.id,
644
- path: `manifest.boards[${index}].containers[${containerIndex}].id`
645
- })),
646
- label: "container id"
647
- })
648
- );
649
- issues.push(
650
- ...collectDuplicateIdIssues({
651
- entries: (board.relations ?? []).map((relation, relationIndex) => ({
652
- id: relation.id,
653
- path: `manifest.boards[${index}].relations[${relationIndex}].id`
654
- })),
655
- label: "relation id"
656
- })
657
- );
658
- }
659
- return issues;
660
- }
661
- function validateSetupOptionChoiceDuplicates(options) {
662
- const issues = [];
663
- for (const [optionIndex, option] of options.entries()) {
664
- issues.push(
665
- ...collectDuplicateIdIssues({
666
- entries: (option.choices ?? []).map((choice, choiceIndex) => ({
667
- id: choice.id,
668
- path: `manifest.setupOptions[${optionIndex}].choices[${choiceIndex}].id`
669
- })),
670
- label: "setup option choice id"
671
- })
672
- );
673
- }
674
- return issues;
675
- }
676
- function resolveHexSpaces(board, template) {
677
- if (!template) {
678
- return [...board.spaces ?? []].sort(
679
- (left, right) => left.id.localeCompare(right.id)
680
- );
681
- }
682
- const templateSpacesById = new Map(
683
- (template.spaces ?? []).map((space) => [space.id, space])
684
- );
685
- const overridesById = new Map(
686
- (board.spaces ?? []).map((space) => [space.id, space])
687
- );
688
- for (const overrideId of overridesById.keys()) {
689
- if (!templateSpacesById.has(overrideId)) {
690
- continue;
691
- }
692
- }
693
- return (template.spaces ?? []).map((templateSpace) => {
694
- const override = overridesById.get(templateSpace.id);
695
- if (!override) {
696
- return templateSpace;
697
- }
698
- return {
699
- ...templateSpace,
700
- ...override,
701
- id: templateSpace.id,
702
- q: templateSpace.q,
703
- r: templateSpace.r
704
- };
705
- }).sort((left, right) => left.id.localeCompare(right.id));
706
- }
707
- function validateHexVertexRefs(options) {
708
- const issues = [];
709
- const spacesById = new Map(
710
- options.spaces.map((space) => [space.id, space])
711
- );
712
- for (const [vertexIndex, vertex] of options.vertices.entries()) {
713
- try {
714
- resolveHexVertexGeometryKey(vertex.ref, spacesById);
715
- } catch (error) {
716
- const message = error instanceof Error ? error.message : "Unknown hex vertex error.";
717
- issues.push(
718
- `${options.ownerPath}.vertices[${vertexIndex}].ref: ${options.ownerLabel} with spaces '${vertex.ref.spaces.join(", ")}' failed validation. ${message}`
719
- );
720
- }
721
- }
722
- return issues;
723
- }
724
- function validateHexBoardVertexRefs(manifest) {
725
- const issues = [];
726
- const hexTemplatesById = new Map(
727
- (manifest.boardTemplates ?? []).filter(isHexBoardTemplateSpec).map((boardTemplate) => [boardTemplate.id, boardTemplate])
728
- );
729
- for (const [templateIndex, template] of (manifest.boardTemplates ?? []).entries()) {
730
- if (!isHexBoardTemplateSpec(template)) {
731
- continue;
732
- }
733
- issues.push(
734
- ...validateHexVertexRefs({
735
- ownerPath: `manifest.boardTemplates[${templateIndex}]`,
736
- ownerLabel: `Hex board template '${template.id}'`,
737
- spaces: template.spaces ?? [],
738
- vertices: template.vertices ?? []
739
- })
740
- );
741
- }
742
- for (const [boardIndex, board] of (manifest.boards ?? []).entries()) {
743
- if (!isHexBoardSpec(board)) {
744
- continue;
745
- }
746
- issues.push(
747
- ...validateHexVertexRefs({
748
- ownerPath: `manifest.boards[${boardIndex}]`,
749
- ownerLabel: `Hex board '${board.id}'`,
750
- spaces: resolveHexSpaces(
751
- board,
752
- hexTemplatesById.get(board.templateId ?? "")
753
- ),
754
- vertices: board.vertices ?? []
755
- })
756
- );
757
- }
758
- return issues;
759
- }
760
- function collectAmbiguousBoardTypeWarnings(manifest) {
761
- const warnings = [];
762
- const boardTemplatesById = new Map(
763
- (manifest.boardTemplates ?? []).map(
764
- (template) => [template.id, template]
765
- )
766
- );
767
- const boardsBySpaceType = /* @__PURE__ */ new Map();
768
- const boardsByEdgeType = /* @__PURE__ */ new Map();
769
- const boardsByVertexType = /* @__PURE__ */ new Map();
770
- const addBoardUsage = (target, typeId, boardId) => {
771
- if (!typeId) {
772
- return;
773
- }
774
- const boardIds = target.get(typeId) ?? /* @__PURE__ */ new Set();
775
- boardIds.add(boardId);
776
- target.set(typeId, boardIds);
777
- };
778
- for (const board of manifest.boards ?? []) {
779
- const template = board.templateId ? boardTemplatesById.get(board.templateId) : void 0;
780
- for (const space of [
781
- ...(template?.layout === board.layout ? template.spaces : void 0) ?? [],
782
- ...board.spaces ?? []
783
- ]) {
784
- addBoardUsage(boardsBySpaceType, space.typeId, board.id);
785
- }
786
- if (board.layout === "hex" || board.layout === "square") {
787
- for (const edge of [
788
- ...(template?.layout === board.layout ? template.edges : void 0) ?? [],
789
- ...board.edges ?? []
790
- ]) {
791
- addBoardUsage(boardsByEdgeType, edge.typeId, board.id);
792
- }
793
- for (const vertex of [
794
- ...(template?.layout === board.layout ? template.vertices : void 0) ?? [],
795
- ...board.vertices ?? []
796
- ]) {
797
- addBoardUsage(boardsByVertexType, vertex.typeId, board.id);
798
- }
799
- }
800
- }
801
- const pushWarnings = (kind, boardsByType, helperName) => {
802
- for (const [typeId, boardIds] of boardsByType.entries()) {
803
- if (boardIds.size < 2) {
804
- continue;
805
- }
806
- warnings.push(
807
- `Ambiguous ${kind}.typeId '${typeId}' is authored on multiple boards (${Array.from(boardIds).sort().join(", ")}). Prefer ${helperName} for board-scoped lookups.`
808
- );
809
- }
810
- };
811
- pushWarnings(
812
- "space",
813
- boardsBySpaceType,
814
- "boardHelpers.spaceIdsByBoardId / boardHelpers.spaceTypeIdByBoardId"
815
- );
816
- pushWarnings(
817
- "edge",
818
- boardsByEdgeType,
819
- "boardHelpers.edgeIdsByBoardIdAndTypeId"
820
- );
821
- pushWarnings(
822
- "vertex",
823
- boardsByVertexType,
824
- "boardHelpers.vertexIdsByBoardIdAndTypeId"
825
- );
826
- return warnings;
827
- }
828
- function collectBoardRecordKeyIssues(manifest) {
829
- const issues = [];
830
- const maxPlayers = manifest.players.maxPlayers;
831
- const playerIds = Array.from(
832
- { length: maxPlayers },
833
- (_, index) => `player-${index + 1}`
834
- );
835
- for (const [templateIndex, boardTemplate] of (manifest.boardTemplates ?? []).entries()) {
836
- const templatePath = `manifest.boardTemplates[${templateIndex}]`;
837
- issues.push(
838
- ...collectKeyIssues([
839
- { value: boardTemplate.id, path: `${templatePath}.id` },
840
- { value: boardTemplate.typeId, path: `${templatePath}.typeId` }
841
- ]),
842
- ...collectObjectSchemaKeyIssues(
843
- boardTemplate.boardFieldsSchema,
844
- `${templatePath}.boardFieldsSchema`
845
- ),
846
- ...collectObjectSchemaKeyIssues(
847
- boardTemplate.spaceFieldsSchema,
848
- `${templatePath}.spaceFieldsSchema`
849
- )
850
- );
851
- if (boardTemplate.layout !== "hex") {
852
- issues.push(
853
- ...collectObjectSchemaKeyIssues(
854
- boardTemplate.relationFieldsSchema,
855
- `${templatePath}.relationFieldsSchema`
856
- ),
857
- ...collectObjectSchemaKeyIssues(
858
- boardTemplate.containerFieldsSchema,
859
- `${templatePath}.containerFieldsSchema`
860
- ),
861
- ...collectKeyIssues([
862
- ...(boardTemplate.containers ?? []).flatMap(
863
- (container, containerIndex) => [
864
- {
865
- value: container.id,
866
- path: `${templatePath}.containers[${containerIndex}].id`
867
- },
868
- {
869
- value: container.host.type === "space" ? container.host.spaceId : void 0,
870
- path: `${templatePath}.containers[${containerIndex}].host.spaceId`
871
- }
872
- ]
873
- ),
874
- ...(boardTemplate.relations ?? []).flatMap(
875
- (relation, relationIndex) => [
876
- {
877
- value: relation.id,
878
- path: `${templatePath}.relations[${relationIndex}].id`
879
- },
880
- {
881
- value: relation.typeId,
882
- path: `${templatePath}.relations[${relationIndex}].typeId`
883
- },
884
- {
885
- value: relation.fromSpaceId,
886
- path: `${templatePath}.relations[${relationIndex}].fromSpaceId`
887
- },
888
- {
889
- value: relation.toSpaceId,
890
- path: `${templatePath}.relations[${relationIndex}].toSpaceId`
891
- }
892
- ]
893
- )
894
- ])
895
- );
896
- }
897
- if (boardTemplate.layout !== "generic") {
898
- issues.push(
899
- ...collectObjectSchemaKeyIssues(
900
- boardTemplate.edgeFieldsSchema,
901
- `${templatePath}.edgeFieldsSchema`
902
- ),
903
- ...collectObjectSchemaKeyIssues(
904
- boardTemplate.vertexFieldsSchema,
905
- `${templatePath}.vertexFieldsSchema`
906
- ),
907
- ...collectKeyIssues([
908
- ...(boardTemplate.edges ?? []).map((edge, edgeIndex) => ({
909
- value: edge.typeId,
910
- path: `${templatePath}.edges[${edgeIndex}].typeId`
911
- })),
912
- ...(boardTemplate.vertices ?? []).map((vertex, vertexIndex) => ({
913
- value: vertex.typeId,
914
- path: `${templatePath}.vertices[${vertexIndex}].typeId`
915
- }))
916
- ])
917
- );
918
- }
919
- issues.push(
920
- ...collectKeyIssues(
921
- (boardTemplate.spaces ?? []).flatMap((space, spaceIndex) => [
922
- {
923
- value: space.id,
924
- path: `${templatePath}.spaces[${spaceIndex}].id`
925
- },
926
- {
927
- value: space.typeId,
928
- path: `${templatePath}.spaces[${spaceIndex}].typeId`
929
- }
930
- ])
931
- )
932
- );
933
- }
934
- for (const [boardIndex, board] of (manifest.boards ?? []).entries()) {
935
- const boardPath = `manifest.boards[${boardIndex}]`;
936
- const runtimeBoardIds = board.scope === "perPlayer" ? playerIds.map((playerId) => `${board.id}:${playerId}`) : [board.id];
937
- issues.push(
938
- ...collectKeyIssues([
939
- { value: board.id, path: `${boardPath}.id` },
940
- { value: board.typeId, path: `${boardPath}.typeId` },
941
- { value: board.templateId, path: `${boardPath}.templateId` },
942
- ...runtimeBoardIds.map((runtimeBoardId) => ({
943
- value: runtimeBoardId,
944
- path: `${boardPath}.runtimeBoardId`
945
- }))
946
- ]),
947
- ...collectObjectSchemaKeyIssues(
948
- board.boardFieldsSchema,
949
- `${boardPath}.boardFieldsSchema`
950
- ),
951
- ...collectObjectSchemaKeyIssues(
952
- board.spaceFieldsSchema,
953
- `${boardPath}.spaceFieldsSchema`
954
- )
955
- );
956
- if (board.layout !== "hex") {
957
- issues.push(
958
- ...collectObjectSchemaKeyIssues(
959
- board.relationFieldsSchema,
960
- `${boardPath}.relationFieldsSchema`
961
- ),
962
- ...collectObjectSchemaKeyIssues(
963
- board.containerFieldsSchema,
964
- `${boardPath}.containerFieldsSchema`
965
- ),
966
- ...collectKeyIssues([
967
- ...(board.containers ?? []).flatMap((container, containerIndex) => [
968
- {
969
- value: container.id,
970
- path: `${boardPath}.containers[${containerIndex}].id`
971
- },
972
- {
973
- value: container.host.type === "space" ? container.host.spaceId : void 0,
974
- path: `${boardPath}.containers[${containerIndex}].host.spaceId`
975
- }
976
- ]),
977
- ...(board.relations ?? []).flatMap((relation, relationIndex) => [
978
- {
979
- value: relation.id,
980
- path: `${boardPath}.relations[${relationIndex}].id`
981
- },
982
- {
983
- value: relation.typeId,
984
- path: `${boardPath}.relations[${relationIndex}].typeId`
985
- },
986
- {
987
- value: relation.fromSpaceId,
988
- path: `${boardPath}.relations[${relationIndex}].fromSpaceId`
989
- },
990
- {
991
- value: relation.toSpaceId,
992
- path: `${boardPath}.relations[${relationIndex}].toSpaceId`
993
- }
994
- ])
995
- ])
996
- );
997
- }
998
- if (board.layout !== "generic") {
999
- issues.push(
1000
- ...collectObjectSchemaKeyIssues(
1001
- board.edgeFieldsSchema,
1002
- `${boardPath}.edgeFieldsSchema`
1003
- ),
1004
- ...collectObjectSchemaKeyIssues(
1005
- board.vertexFieldsSchema,
1006
- `${boardPath}.vertexFieldsSchema`
1007
- ),
1008
- ...collectKeyIssues([
1009
- ...(board.edges ?? []).map((edge, edgeIndex) => ({
1010
- value: edge.typeId,
1011
- path: `${boardPath}.edges[${edgeIndex}].typeId`
1012
- })),
1013
- ...(board.vertices ?? []).map((vertex, vertexIndex) => ({
1014
- value: vertex.typeId,
1015
- path: `${boardPath}.vertices[${vertexIndex}].typeId`
1016
- }))
1017
- ])
1018
- );
1019
- }
1020
- issues.push(
1021
- ...collectKeyIssues(
1022
- (board.spaces ?? []).flatMap((space, spaceIndex) => [
1023
- {
1024
- value: space.id,
1025
- path: `${boardPath}.spaces[${spaceIndex}].id`
1026
- },
1027
- {
1028
- value: space.typeId,
1029
- path: `${boardPath}.spaces[${spaceIndex}].typeId`
1030
- }
1031
- ])
1032
- )
1033
- );
1034
- }
1035
- return issues;
1036
- }
1037
- function collectManifestRecordKeyIssues(manifest) {
1038
- const cardSets = manifest.cardSets;
1039
- const manualCards = cardSets.flatMap(
1040
- (cardSet, cardSetIndex) => cardSet.type === "manual" ? cardSet.cards.flatMap(
1041
- (card, cardIndex) => renderCardInstanceIds(card).map((cardId) => ({
1042
- card,
1043
- cardId,
1044
- cardIndex,
1045
- cardSetIndex
1046
- }))
1047
- ) : []
1048
- );
1049
- return [
1050
- ...collectKeyIssues([
1051
- ...cardSets.map((cardSet, index) => ({
1052
- value: cardSet.id,
1053
- path: `manifest.cardSets[${index}].id`
1054
- })),
1055
- ...manualCards.flatMap(({ card, cardId, cardIndex, cardSetIndex }) => [
1056
- {
1057
- value: card.type,
1058
- path: `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].type`
1059
- },
1060
- {
1061
- value: card.cardType,
1062
- path: `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].cardType`
1063
- },
1064
- {
1065
- value: cardId,
1066
- path: `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].runtimeId`
1067
- }
1068
- ]),
1069
- ...(manifest.zones ?? []).map((zone, index) => ({
1070
- value: zone.id,
1071
- path: `manifest.zones[${index}].id`
1072
- })),
1073
- ...(manifest.resources ?? []).map((resource, index) => ({
1074
- value: resource.id,
1075
- path: `manifest.resources[${index}].id`
1076
- })),
1077
- ...(manifest.pieceTypes ?? []).flatMap((pieceType, typeIndex) => [
1078
- {
1079
- value: pieceType.id,
1080
- path: `manifest.pieceTypes[${typeIndex}].id`
1081
- },
1082
- ...(pieceType.slots ?? []).map((slot, slotIndex) => ({
1083
- value: slot.id,
1084
- path: `manifest.pieceTypes[${typeIndex}].slots[${slotIndex}].id`
1085
- }))
1086
- ]),
1087
- ...(manifest.dieTypes ?? []).flatMap((dieType, typeIndex) => [
1088
- {
1089
- value: dieType.id,
1090
- path: `manifest.dieTypes[${typeIndex}].id`
1091
- },
1092
- ...(dieType.slots ?? []).map((slot, slotIndex) => ({
1093
- value: slot.id,
1094
- path: `manifest.dieTypes[${typeIndex}].slots[${slotIndex}].id`
1095
- }))
1096
- ]),
1097
- ...expandSeedIds(manifest.pieceSeeds ?? []).map((pieceId, index) => ({
1098
- value: pieceId,
1099
- path: `manifest.pieceSeeds[*][${index}]`
1100
- })),
1101
- ...expandSeedIds(manifest.dieSeeds ?? []).map((dieId, index) => ({
1102
- value: dieId,
1103
- path: `manifest.dieSeeds[*][${index}]`
1104
- })),
1105
- ...(manifest.setupOptions ?? []).flatMap((option, optionIndex) => [
1106
- {
1107
- value: option.id,
1108
- path: `manifest.setupOptions[${optionIndex}].id`
1109
- },
1110
- ...(option.choices ?? []).map((choice, choiceIndex) => ({
1111
- value: choice.id,
1112
- path: `manifest.setupOptions[${optionIndex}].choices[${choiceIndex}].id`
1113
- }))
1114
- ]),
1115
- ...(manifest.setupProfiles ?? []).flatMap((profile, profileIndex) => [
1116
- {
1117
- value: profile.id,
1118
- path: `manifest.setupProfiles[${profileIndex}].id`
1119
- },
1120
- ...(profile.guidance?.steps ?? []).map((step, stepIndex) => ({
1121
- value: step.id,
1122
- path: `manifest.setupProfiles[${profileIndex}].guidance.steps[${stepIndex}].id`
1123
- })),
1124
- ...Object.entries(profile.optionValues ?? {}).flatMap(
1125
- ([optionId, choiceId]) => [
1126
- {
1127
- value: optionId,
1128
- path: `manifest.setupProfiles[${profileIndex}].optionValues.${optionId}`
1129
- },
1130
- {
1131
- value: choiceId,
1132
- path: `manifest.setupProfiles[${profileIndex}].optionValues.${optionId}`
1133
- }
1134
- ]
1135
- )
1136
- ])
1137
- ]),
1138
- ...cardSets.flatMap(
1139
- (cardSet, cardSetIndex) => collectCardSchemaKeyIssues(cardSet, `manifest.cardSets[${cardSetIndex}]`)
1140
- ),
1141
- ...(manifest.pieceTypes ?? []).flatMap(
1142
- (pieceType, typeIndex) => collectObjectSchemaKeyIssues(
1143
- pieceType.fieldsSchema,
1144
- `manifest.pieceTypes[${typeIndex}].fieldsSchema`
1145
- )
1146
- ),
1147
- ...(manifest.dieTypes ?? []).flatMap(
1148
- (dieType, typeIndex) => collectObjectSchemaKeyIssues(
1149
- dieType.fieldsSchema,
1150
- `manifest.dieTypes[${typeIndex}].fieldsSchema`
1151
- )
1152
- ),
1153
- ...collectBoardRecordKeyIssues(manifest),
1154
- ...collectHandleKeyCollisions(
1155
- dedupeSorted(manualCards.map(({ card }) => card.cardType ?? card.type)),
1156
- "Card type"
1157
- ),
1158
- ...collectHandleKeyCollisions(
1159
- dedupeSorted((manifest.zones ?? []).map((zone) => zone.id)),
1160
- "Zone"
1161
- )
1162
- ];
1163
- }
1164
- function validateManifestAuthoring(manifest) {
1165
- const errors = [];
1166
- errors.push(...collectManifestRecordKeyIssues(manifest));
1167
- errors.push(
1168
- ...collectDuplicateIdIssues({
1169
- entries: manifest.cardSets.map((cardSet, index) => ({
1170
- id: cardSet.id,
1171
- path: `manifest.cardSets[${index}].id`
1172
- })),
1173
- label: "card set id"
1174
- })
1175
- );
1176
- errors.push(
1177
- ...collectDuplicateIdIssues({
1178
- entries: manifest.cardSets.flatMap(
1179
- (cardSet, cardSetIndex) => cardSet.type === "manual" ? cardSet.cards.flatMap(
1180
- (card, cardIndex) => renderCardInstanceIds(card).map((cardId) => ({
1181
- id: cardId,
1182
- path: `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].type`
1183
- }))
1184
- ) : []
1185
- ),
1186
- label: "card runtime id"
1187
- })
1188
- );
1189
- errors.push(
1190
- ...collectDuplicateIdIssues({
1191
- entries: (manifest.zones ?? []).map((zone, index) => ({
1192
- id: zone.id,
1193
- path: `manifest.zones[${index}].id`
1194
- })),
1195
- label: "zone id"
1196
- })
1197
- );
1198
- errors.push(
1199
- ...collectDuplicateIdIssues({
1200
- entries: (manifest.boardTemplates ?? []).map((boardTemplate, index) => ({
1201
- id: boardTemplate.id,
1202
- path: `manifest.boardTemplates[${index}].id`
1203
- })),
1204
- label: "board template id"
1205
- })
1206
- );
1207
- errors.push(
1208
- ...validateBoardTemplateDuplicates(manifest.boardTemplates ?? [])
1209
- );
1210
- errors.push(
1211
- ...collectDuplicateIdIssues({
1212
- entries: (manifest.boards ?? []).map((board, index) => ({
1213
- id: board.id,
1214
- path: `manifest.boards[${index}].id`
1215
- })),
1216
- label: "board id"
1217
- })
1218
- );
1219
- errors.push(...validateBoardDuplicates(manifest.boards ?? []));
1220
- errors.push(
1221
- ...validateTypeSlotDuplicates({
1222
- pieceTypes: manifest.pieceTypes ?? [],
1223
- dieTypes: manifest.dieTypes ?? []
1224
- })
1225
- );
1226
- errors.push(
1227
- ...collectDuplicateIdIssues({
1228
- entries: (manifest.pieceTypes ?? []).map((pieceType, index) => ({
1229
- id: pieceType.id,
1230
- path: `manifest.pieceTypes[${index}].id`
1231
- })),
1232
- label: "piece type id"
1233
- })
1234
- );
1235
- errors.push(
1236
- ...collectDuplicateIdIssues({
1237
- entries: expandSeedIds(manifest.pieceSeeds ?? []).map(
1238
- (pieceId, index) => ({
1239
- id: pieceId,
1240
- path: `manifest.pieceSeeds[*][${index}]`
1241
- })
1242
- ),
1243
- label: "piece runtime id"
1244
- })
1245
- );
1246
- errors.push(
1247
- ...collectDuplicateIdIssues({
1248
- entries: (manifest.dieTypes ?? []).map((dieType, index) => ({
1249
- id: dieType.id,
1250
- path: `manifest.dieTypes[${index}].id`
1251
- })),
1252
- label: "die type id"
1253
- })
1254
- );
1255
- errors.push(
1256
- ...collectDuplicateIdIssues({
1257
- entries: expandSeedIds(manifest.dieSeeds ?? []).map((dieId, index) => ({
1258
- id: dieId,
1259
- path: `manifest.dieSeeds[*][${index}]`
1260
- })),
1261
- label: "die runtime id"
1262
- })
1263
- );
1264
- errors.push(
1265
- ...collectDuplicateIdIssues({
1266
- entries: (manifest.resources ?? []).map((resource, index) => ({
1267
- id: resource.id,
1268
- path: `manifest.resources[${index}].id`
1269
- })),
1270
- label: "resource id"
1271
- })
1272
- );
1273
- errors.push(
1274
- ...collectDuplicateIdIssues({
1275
- entries: (manifest.setupOptions ?? []).map((option, index) => ({
1276
- id: option.id,
1277
- path: `manifest.setupOptions[${index}].id`
1278
- })),
1279
- label: "setup option id"
1280
- })
1281
- );
1282
- errors.push(
1283
- ...validateSetupOptionChoiceDuplicates(manifest.setupOptions ?? [])
1284
- );
1285
- errors.push(
1286
- ...collectDuplicateIdIssues({
1287
- entries: (manifest.setupProfiles ?? []).map((profile, index) => ({
1288
- id: profile.id,
1289
- path: `manifest.setupProfiles[${index}].id`
1290
- })),
1291
- label: "setup profile id"
1292
- })
1293
- );
1294
- for (const [profileIndex, profile] of (manifest.setupProfiles ?? []).entries()) {
1295
- errors.push(
1296
- ...collectDuplicateIdIssues({
1297
- entries: (profile.guidance?.steps ?? []).map((step, stepIndex) => ({
1298
- id: step.id,
1299
- path: `manifest.setupProfiles[${profileIndex}].guidance.steps[${stepIndex}].id`
1300
- })),
1301
- label: `setup profile '${profile.id}' guidance step id`
1302
- })
1303
- );
1304
- }
1305
- errors.push(...validateSlotHostsAndHomes(manifest));
1306
- errors.push(...validatePlayerScopedSeedHomes(manifest));
1307
- errors.push(...validateCardHomes(manifest));
1308
- errors.push(...validateSetupProfileReferences(manifest));
1309
- errors.push(...validateHexBoardVertexRefs(manifest));
1310
- return {
1311
- errors,
1312
- warnings: collectAmbiguousBoardTypeWarnings(manifest)
1313
- };
1314
- }
1315
-
1316
- // src/reducer/manifest/materialize.ts
1317
- function isCardPropertySchemaVariants(schema) {
1318
- return Boolean(schema && "variants" in schema);
1319
- }
1320
- function mergeSharedCardProperties(schema, cardType) {
1321
- const variant = schema.variants[cardType];
1322
- if (!variant) {
1323
- return null;
1324
- }
1325
- return {
1326
- properties: {
1327
- ...schema.shared ?? {},
1328
- ...variant.properties
1329
- }
1330
- };
1331
- }
1332
- function hasPropertySchemaDefault(property) {
1333
- return Boolean(property && "default" in property);
1334
- }
1335
- function dedupeSorted2(values) {
1336
- return Array.from(new Set(values)).sort();
1337
- }
1338
- function sortedObject(entries) {
1339
- return Object.fromEntries(
1340
- Array.from(entries).sort(([left], [right]) => left.localeCompare(right))
1341
- );
1342
- }
1343
- function createRecord() {
1344
- return /* @__PURE__ */ Object.create(null);
1345
- }
1346
- function renderCardInstanceIds2(card) {
1347
- if (card.count > 1) {
1348
- return Array.from(
1349
- { length: card.count },
1350
- (_, index) => `${card.type}-${index + 1}`
1351
- );
1352
- }
1353
- return [card.type];
1354
- }
1355
- function expandSeedIds2(seeds) {
1356
- const expanded = [];
1357
- for (const seed of seeds) {
1358
- const count = seed.count ?? 1;
1359
- const baseId = seed.id ?? seed.typeId;
1360
- if (count <= 1) {
1361
- expanded.push(baseId);
1362
- continue;
1363
- }
1364
- for (let index = 1; index <= count; index += 1) {
1365
- expanded.push(`${baseId}-${index}`);
1366
- }
1367
- }
1368
- return expanded;
1369
- }
1370
- function isHexBoardTemplateSpec2(boardTemplate) {
1371
- return boardTemplate.layout === "hex";
1372
- }
1373
- function isSquareBoardTemplateSpec(boardTemplate) {
1374
- return boardTemplate.layout === "square";
1375
- }
1376
- function isGenericBoardTemplateSpec(boardTemplate) {
1377
- return boardTemplate.layout === "generic";
1378
- }
1379
- function isHexBoardSpec2(board) {
1380
- return board.layout === "hex";
1381
- }
1382
- function isSquareBoardSpec(board) {
1383
- return board.layout === "square";
1384
- }
1385
- var HEX_SIDES = ["e", "ne", "nw", "w", "sw", "se"];
1386
- var HEX_CORNERS2 = ["ne-e", "e-se", "se-sw", "sw-w", "w-nw", "nw-ne"];
1387
- var HEX_SIDE_OFFSETS = {
1388
- e: [1, 0],
1389
- ne: [1, -1],
1390
- nw: [0, -1],
1391
- w: [-1, 0],
1392
- sw: [-1, 1],
1393
- se: [0, 1]
1394
- };
1395
- var HEX_CORNER_OFFSETS2 = {
1396
- "ne-e": [2, -1, -1],
1397
- "e-se": [1, -2, 1],
1398
- "se-sw": [-1, -1, 2],
1399
- "sw-w": [-2, 1, 1],
1400
- "w-nw": [-1, 2, -1],
1401
- "nw-ne": [1, 1, -2]
1402
- };
1403
- var HEX_SIDE_CORNERS = {
1404
- e: ["ne-e", "e-se"],
1405
- ne: ["nw-ne", "ne-e"],
1406
- nw: ["w-nw", "nw-ne"],
1407
- w: ["sw-w", "w-nw"],
1408
- sw: ["se-sw", "sw-w"],
1409
- se: ["e-se", "se-sw"]
1410
- };
1411
- var HEX_CORNER_SIDES = {
1412
- "ne-e": ["ne", "e"],
1413
- "e-se": ["e", "se"],
1414
- "se-sw": ["se", "sw"],
1415
- "sw-w": ["sw", "w"],
1416
- "w-nw": ["w", "nw"],
1417
- "nw-ne": ["nw", "ne"]
1418
- };
1419
- function cubeFromAxial2(space) {
1420
- const x = space.q;
1421
- const z = space.r;
1422
- return [x, -x - z, z];
1423
- }
1424
- function cornerGeometryKey2(space, corner) {
1425
- const [x, y, z] = cubeFromAxial2(space);
1426
- const [dx, dy, dz] = HEX_CORNER_OFFSETS2[corner];
1427
- return `${3 * x + dx},${3 * y + dy},${3 * z + dz}`;
1428
- }
1429
- function edgeGeometryKey(space, side) {
1430
- const [leftCorner, rightCorner] = HEX_SIDE_CORNERS[side];
1431
- return [
1432
- cornerGeometryKey2(space, leftCorner),
1433
- cornerGeometryKey2(space, rightCorner)
1434
- ].sort((left, right) => left.localeCompare(right)).join("::");
1435
- }
1436
- function edgeIdFromGeometryKey(key) {
1437
- return `hex-edge:${key}`;
1438
- }
1439
- function vertexIdFromGeometryKey(key) {
1440
- return `hex-vertex:${key}`;
1441
- }
1442
- var SQUARE_SIDES = ["north", "east", "south", "west"];
1443
- var SQUARE_CORNERS = ["nw", "ne", "se", "sw"];
1444
- function squareEdgeIdFromGeometryKey(key) {
1445
- return `square-edge:${key}`;
1446
- }
1447
- function squareVertexIdFromGeometryKey(key) {
1448
- return `square-vertex:${key}`;
1449
- }
1450
- function squareCornerGeometryKey(space, corner) {
1451
- switch (corner) {
1452
- case "nw":
1453
- return `${space.col},${space.row}`;
1454
- case "ne":
1455
- return `${space.col + 1},${space.row}`;
1456
- case "se":
1457
- return `${space.col + 1},${space.row + 1}`;
1458
- case "sw":
1459
- return `${space.col},${space.row + 1}`;
1460
- }
1461
- }
1462
- function squareEdgeGeometryKey(space, side) {
1463
- const endpoints = side === "north" ? [`${space.col},${space.row}`, `${space.col + 1},${space.row}`] : side === "east" ? [`${space.col + 1},${space.row}`, `${space.col + 1},${space.row + 1}`] : side === "south" ? [
1464
- `${space.col},${space.row + 1}`,
1465
- `${space.col + 1},${space.row + 1}`
1466
- ] : [`${space.col},${space.row}`, `${space.col},${space.row + 1}`];
1467
- return endpoints.sort((left, right) => left.localeCompare(right)).join("::");
1468
- }
1469
- function geometryKeyFromSquareEdgeRef(ref, spacesById) {
1470
- const resolvedSpaces = [...ref.spaces].sort((a, b) => a.localeCompare(b)).map((spaceId) => {
1471
- const space = spacesById.get(spaceId);
1472
- if (!space) {
1473
- throw new Error(
1474
- `Square edge ref references unknown space '${spaceId}'.`
1475
- );
1476
- }
1477
- return space;
1478
- });
1479
- const keyCounts = /* @__PURE__ */ new Map();
1480
- for (const space of resolvedSpaces) {
1481
- for (const side of SQUARE_SIDES) {
1482
- const key = squareEdgeGeometryKey(space, side);
1483
- keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
1484
- }
1485
- }
1486
- const candidates = [...keyCounts.entries()].filter(([, count]) => count === resolvedSpaces.length).map(([key]) => key).sort((left, right) => left.localeCompare(right));
1487
- if (candidates.length !== 1) {
1488
- throw new Error(
1489
- `Square edge ref spaces '${ref.spaces.join(", ")}' do not resolve to exactly one shared edge.`
1490
- );
1491
- }
1492
- const [only] = candidates;
1493
- if (only === void 0) {
1494
- throw new Error(
1495
- "unreachable: candidates.length === 1 but first is undefined"
1496
- );
1497
- }
1498
- return only;
1499
- }
1500
- function geometryKeyFromSquareVertexRef(ref, spacesById) {
1501
- const resolvedSpaces = [...ref.spaces].sort((a, b) => a.localeCompare(b)).map((spaceId) => {
1502
- const space = spacesById.get(spaceId);
1503
- if (!space) {
1504
- throw new Error(
1505
- `Square vertex ref references unknown space '${spaceId}'.`
1506
- );
1507
- }
1508
- return space;
1509
- });
1510
- const keyCounts = /* @__PURE__ */ new Map();
1511
- for (const space of resolvedSpaces) {
1512
- for (const corner of SQUARE_CORNERS) {
1513
- const key = squareCornerGeometryKey(space, corner);
1514
- keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
1515
- }
1516
- }
1517
- const candidates = [...keyCounts.entries()].filter(([, count]) => count === resolvedSpaces.length).map(([key]) => key).sort((left, right) => left.localeCompare(right));
1518
- if (candidates.length !== 1) {
1519
- throw new Error(
1520
- `Square vertex ref spaces '${ref.spaces.join(", ")}' do not resolve to exactly one shared vertex.`
1521
- );
1522
- }
1523
- const [only] = candidates;
1524
- if (only === void 0) {
1525
- throw new Error(
1526
- "unreachable: candidates.length === 1 but first is undefined"
1527
- );
1528
- }
1529
- return only;
1530
- }
1531
- function geometryKeyFromEdgeRef(ref, spacesById) {
1532
- const [leftId, rightId] = [...ref.spaces].sort((a, b) => a.localeCompare(b));
1533
- if (leftId === void 0 || rightId === void 0) {
1534
- throw new Error("Hex edge ref must reference exactly two spaces.");
1535
- }
1536
- const leftSpace = spacesById.get(leftId);
1537
- const rightSpace = spacesById.get(rightId);
1538
- if (!leftSpace || !rightSpace) {
1539
- throw new Error(
1540
- `Hex edge ref references unknown spaces: ${ref.spaces.join(", ")}.`
1541
- );
1542
- }
1543
- const [dq, dr] = [rightSpace.q - leftSpace.q, rightSpace.r - leftSpace.r];
1544
- const side = Object.entries(HEX_SIDE_OFFSETS).find(([, [sideQ, sideR]]) => sideQ === dq && sideR === dr)?.[0];
1545
- if (!side) {
1546
- throw new Error(
1547
- `Hex edge ref spaces '${leftId}' and '${rightId}' are not adjacent.`
1548
- );
1549
- }
1550
- return edgeGeometryKey(leftSpace, side);
1551
- }
1552
- function geometryKeyFromVertexRef(ref, spacesById) {
1553
- return resolveHexVertexGeometryKey(ref, spacesById);
1554
- }
1555
- function deriveHexEdges(spaces) {
1556
- const spacesByCoordinate = /* @__PURE__ */ new Map();
1557
- for (const space of spaces) {
1558
- spacesByCoordinate.set(`${space.q},${space.r}`, space);
1559
- }
1560
- const edgeMap = /* @__PURE__ */ new Map();
1561
- for (const space of spaces) {
1562
- for (const side of HEX_SIDES) {
1563
- const [dq, dr] = HEX_SIDE_OFFSETS[side];
1564
- const neighbor = spacesByCoordinate.get(
1565
- `${space.q + dq},${space.r + dr}`
1566
- );
1567
- const geometryKey = edgeGeometryKey(space, side);
1568
- const existing = edgeMap.get(geometryKey);
1569
- const nextSpaceIds = dedupeSorted2([
1570
- ...existing?.spaceIds ?? [],
1571
- space.id,
1572
- ...neighbor ? [neighbor.id] : []
1573
- ]);
1574
- edgeMap.set(geometryKey, {
1575
- id: edgeIdFromGeometryKey(geometryKey),
1576
- geometryKey,
1577
- spaceIds: nextSpaceIds,
1578
- typeId: existing?.typeId ?? null,
1579
- label: existing?.label ?? null,
1580
- fields: existing?.fields ?? null
1581
- });
1582
- }
1583
- }
1584
- return [...edgeMap.values()].sort(
1585
- (left, right) => left.id.localeCompare(right.id)
1586
- );
1587
- }
1588
- function deriveHexVertices(spaces) {
1589
- const spacesByCoordinate = /* @__PURE__ */ new Map();
1590
- for (const space of spaces) {
1591
- spacesByCoordinate.set(`${space.q},${space.r}`, space);
1592
- }
1593
- const vertexMap = /* @__PURE__ */ new Map();
1594
- for (const space of spaces) {
1595
- for (const corner of HEX_CORNERS2) {
1596
- const [leftSide, rightSide] = HEX_CORNER_SIDES[corner];
1597
- const [leftQ, leftR] = HEX_SIDE_OFFSETS[leftSide];
1598
- const [rightQ, rightR] = HEX_SIDE_OFFSETS[rightSide];
1599
- const leftNeighbor = spacesByCoordinate.get(
1600
- `${space.q + leftQ},${space.r + leftR}`
1601
- );
1602
- const rightNeighbor = spacesByCoordinate.get(
1603
- `${space.q + rightQ},${space.r + rightR}`
1604
- );
1605
- const geometryKey = cornerGeometryKey2(space, corner);
1606
- const existing = vertexMap.get(geometryKey);
1607
- const nextSpaceIds = dedupeSorted2([
1608
- ...existing?.spaceIds ?? [],
1609
- space.id,
1610
- ...leftNeighbor ? [leftNeighbor.id] : [],
1611
- ...rightNeighbor ? [rightNeighbor.id] : []
1612
- ]);
1613
- vertexMap.set(geometryKey, {
1614
- id: vertexIdFromGeometryKey(geometryKey),
1615
- geometryKey,
1616
- spaceIds: nextSpaceIds,
1617
- typeId: existing?.typeId ?? null,
1618
- label: existing?.label ?? null,
1619
- fields: existing?.fields ?? null
1620
- });
1621
- }
1622
- }
1623
- return [...vertexMap.values()].sort(
1624
- (left, right) => left.id.localeCompare(right.id)
1625
- );
1626
- }
1627
- function mergeBoardSpaces(templateSpaces, boardSpaces) {
1628
- return Array.from(
1629
- [...templateSpaces, ...boardSpaces].reduce((accumulator, space) => {
1630
- accumulator.set(space.id, space);
1631
- return accumulator;
1632
- }, /* @__PURE__ */ new Map()).values()
1633
- ).sort((left, right) => left.id.localeCompare(right.id));
1634
- }
1635
- function mergeBoardContainers(templateContainers, boardContainers) {
1636
- return Array.from(
1637
- [...templateContainers, ...boardContainers].reduce((accumulator, container) => {
1638
- accumulator.set(container.id, container);
1639
- return accumulator;
1640
- }, /* @__PURE__ */ new Map()).values()
1641
- ).sort((left, right) => left.id.localeCompare(right.id));
1642
- }
1643
- function resolveHexSpaces2(board, template) {
1644
- if (!template) {
1645
- return [...board.spaces ?? []].sort(
1646
- (left, right) => left.id.localeCompare(right.id)
1647
- );
1648
- }
1649
- const templateSpacesById = new Map(
1650
- (template.spaces ?? []).map((space) => [space.id, space])
1651
- );
1652
- const overridesById = new Map(
1653
- (board.spaces ?? []).map((space) => [space.id, space])
1654
- );
1655
- for (const overrideId of overridesById.keys()) {
1656
- if (!templateSpacesById.has(overrideId)) {
1657
- throw new Error(
1658
- `Hex board '${board.id}' overrides unknown space '${overrideId}' from template '${template.id}'.`
1659
- );
1660
- }
1661
- }
1662
- return (template.spaces ?? []).map((templateSpace) => {
1663
- const override = overridesById.get(templateSpace.id);
1664
- if (!override) {
1665
- return templateSpace;
1666
- }
1667
- const templateMatch = templateSpacesById.get(override.id);
1668
- if (!templateMatch) {
1669
- throw new Error(
1670
- `Hex board '${board.id}' overrides unknown space '${override.id}' from template '${template.id}'.`
1671
- );
1672
- }
1673
- if (templateMatch.q !== override.q || templateMatch.r !== override.r) {
1674
- throw new Error(
1675
- `Hex board '${board.id}' cannot change coordinates for space '${override.id}' from template '${template.id}'.`
1676
- );
1677
- }
1678
- return {
1679
- ...templateSpace,
1680
- ...override,
1681
- id: templateSpace.id,
1682
- q: templateSpace.q,
1683
- r: templateSpace.r
1684
- };
1685
- }).sort((left, right) => left.id.localeCompare(right.id));
1686
- }
1687
- function resolveAuthoredHexEdges(board, template, spaces) {
1688
- const spacesById = new Map(spaces.map((space) => [space.id, space]));
1689
- const derivedByGeometryKey = new Map(
1690
- deriveHexEdges(spaces).map((edge) => [edge.geometryKey, edge])
1691
- );
1692
- const collectSpecsByGeometryKey = (specs, ownerLabel) => {
1693
- const specsByGeometryKey = /* @__PURE__ */ new Map();
1694
- for (const spec of specs) {
1695
- const geometryKey = geometryKeyFromEdgeRef(spec.ref, spacesById);
1696
- if (specsByGeometryKey.has(geometryKey)) {
1697
- throw new Error(`${ownerLabel} contains duplicate hex edge refs.`);
1698
- }
1699
- specsByGeometryKey.set(geometryKey, spec);
1700
- }
1701
- return specsByGeometryKey;
1702
- };
1703
- const templateSpecsByGeometryKey = template ? collectSpecsByGeometryKey(
1704
- template.edges ?? [],
1705
- `Hex board template '${template.id}'`
1706
- ) : /* @__PURE__ */ new Map();
1707
- const overrideSpecsByGeometryKey = collectSpecsByGeometryKey(
1708
- board.edges ?? [],
1709
- `Hex board '${board.id}'`
1710
- );
1711
- if (template) {
1712
- for (const overrideKey of overrideSpecsByGeometryKey.keys()) {
1713
- if (!templateSpecsByGeometryKey.has(overrideKey)) {
1714
- throw new Error(
1715
- `Hex board '${board.id}' overrides unknown edge ref from template '${template.id}'.`
1716
- );
1717
- }
1718
- }
1719
- }
1720
- const mergedSpecs = template ? Array.from(templateSpecsByGeometryKey.entries()).map(
1721
- ([geometryKey, templateSpec]) => [
1722
- geometryKey,
1723
- overrideSpecsByGeometryKey.get(geometryKey) ?? templateSpec
1724
- ]
1725
- ) : Array.from(overrideSpecsByGeometryKey.entries());
1726
- return mergedSpecs.map(([geometryKey, spec]) => {
1727
- const derivedEdge = derivedByGeometryKey.get(geometryKey);
1728
- if (!derivedEdge) {
1729
- throw new Error(
1730
- `Hex edge ref on board '${board.id}' does not resolve to a derived edge.`
1731
- );
1732
- }
1733
- return {
1734
- id: derivedEdge.id,
1735
- ref: cloneJson(spec.ref),
1736
- typeId: spec.typeId ?? null,
1737
- label: spec.label ?? null,
1738
- fields: spec.fields ?? null
1739
- };
1740
- });
1741
- }
1742
- function resolveAuthoredHexVertices(board, template, spaces) {
1743
- const spacesById = new Map(spaces.map((space) => [space.id, space]));
1744
- const derivedByGeometryKey = new Map(
1745
- deriveHexVertices(spaces).map(
1746
- (vertex) => [vertex.geometryKey, vertex]
1747
- )
1748
- );
1749
- const collectSpecsByGeometryKey = (specs, ownerLabel) => {
1750
- const specsByGeometryKey = /* @__PURE__ */ new Map();
1751
- for (const spec of specs) {
1752
- const geometryKey = geometryKeyFromVertexRef(spec.ref, spacesById);
1753
- if (specsByGeometryKey.has(geometryKey)) {
1754
- throw new Error(`${ownerLabel} contains duplicate hex vertex refs.`);
1755
- }
1756
- specsByGeometryKey.set(geometryKey, spec);
1757
- }
1758
- return specsByGeometryKey;
1759
- };
1760
- const templateSpecsByGeometryKey = template ? collectSpecsByGeometryKey(
1761
- template.vertices ?? [],
1762
- `Hex board template '${template.id}'`
1763
- ) : /* @__PURE__ */ new Map();
1764
- const overrideSpecsByGeometryKey = collectSpecsByGeometryKey(
1765
- board.vertices ?? [],
1766
- `Hex board '${board.id}'`
1767
- );
1768
- if (template) {
1769
- for (const overrideKey of overrideSpecsByGeometryKey.keys()) {
1770
- if (!templateSpecsByGeometryKey.has(overrideKey)) {
1771
- throw new Error(
1772
- `Hex board '${board.id}' overrides unknown vertex ref from template '${template.id}'.`
1773
- );
1774
- }
1775
- }
1776
- }
1777
- const mergedSpecs = template ? Array.from(templateSpecsByGeometryKey.entries()).map(
1778
- ([geometryKey, templateSpec]) => [
1779
- geometryKey,
1780
- overrideSpecsByGeometryKey.get(geometryKey) ?? templateSpec
1781
- ]
1782
- ) : Array.from(overrideSpecsByGeometryKey.entries());
1783
- return mergedSpecs.map(([geometryKey, spec]) => {
1784
- const derivedVertex = derivedByGeometryKey.get(geometryKey);
1785
- if (!derivedVertex) {
1786
- throw new Error(
1787
- `Hex vertex ref on board '${board.id}' does not resolve to a derived vertex.`
1788
- );
1789
- }
1790
- return {
1791
- id: derivedVertex.id,
1792
- ref: cloneJson(spec.ref),
1793
- typeId: spec.typeId ?? null,
1794
- label: spec.label ?? null,
1795
- fields: spec.fields ?? null
1796
- };
1797
- });
1798
- }
1799
- function resolveHexEdges(board, template, spaces) {
1800
- const derived = deriveHexEdges(spaces);
1801
- const edgesById = new Map(derived.map((edge) => [edge.id, edge]));
1802
- for (const authoredEdge of resolveAuthoredHexEdges(board, template, spaces)) {
1803
- const edge = edgesById.get(authoredEdge.id);
1804
- if (!edge) {
1805
- throw new Error(
1806
- `Hex edge ref on board '${board.id}' does not resolve to a derived edge.`
1807
- );
1808
- }
1809
- edgesById.set(authoredEdge.id, {
1810
- ...edge,
1811
- typeId: authoredEdge.typeId ?? null,
1812
- label: authoredEdge.label ?? null,
1813
- fields: authoredEdge.fields ?? null
1814
- });
1815
- }
1816
- return [...edgesById.values()].sort(
1817
- (left, right) => left.id.localeCompare(right.id)
1818
- );
1819
- }
1820
- function resolveHexVertices(board, template, spaces) {
1821
- const derived = deriveHexVertices(spaces);
1822
- const verticesById = new Map(
1823
- derived.map((vertex) => [vertex.id, vertex])
1824
- );
1825
- for (const authoredVertex of resolveAuthoredHexVertices(
1826
- board,
1827
- template,
1828
- spaces
1829
- )) {
1830
- const vertex = verticesById.get(authoredVertex.id);
1831
- if (!vertex) {
1832
- throw new Error(
1833
- `Hex vertex ref on board '${board.id}' does not resolve to a derived vertex.`
1834
- );
1835
- }
1836
- verticesById.set(authoredVertex.id, {
1837
- ...vertex,
1838
- typeId: authoredVertex.typeId ?? null,
1839
- label: authoredVertex.label ?? null,
1840
- fields: authoredVertex.fields ?? null
1841
- });
1842
- }
1843
- return [...verticesById.values()].sort(
1844
- (left, right) => left.id.localeCompare(right.id)
1845
- );
1846
- }
1847
- function resolveSquareSpaces(board, template) {
1848
- if (!template) {
1849
- return [...board.spaces ?? []].sort(
1850
- (left, right) => left.id.localeCompare(right.id)
1851
- );
1852
- }
1853
- const templateSpacesById = new Map(
1854
- (template.spaces ?? []).map((space) => [space.id, space])
1855
- );
1856
- const overridesById = new Map(
1857
- (board.spaces ?? []).map((space) => [space.id, space])
1858
- );
1859
- for (const overrideId of overridesById.keys()) {
1860
- if (!templateSpacesById.has(overrideId)) {
1861
- throw new Error(
1862
- `Square board '${board.id}' overrides unknown space '${overrideId}' from template '${template.id}'.`
1863
- );
1864
- }
1865
- }
1866
- return (template.spaces ?? []).map((templateSpace) => {
1867
- const override = overridesById.get(templateSpace.id);
1868
- if (!override) {
1869
- return templateSpace;
1870
- }
1871
- const templateMatch = templateSpacesById.get(override.id);
1872
- if (!templateMatch) {
1873
- throw new Error(
1874
- `Square board '${board.id}' overrides unknown space '${override.id}' from template '${template.id}'.`
1875
- );
1876
- }
1877
- if (templateMatch.row !== override.row || templateMatch.col !== override.col) {
1878
- throw new Error(
1879
- `Square board '${board.id}' cannot change coordinates for space '${override.id}' from template '${template.id}'.`
1880
- );
1881
- }
1882
- return {
1883
- ...templateSpace,
1884
- ...override,
1885
- id: templateSpace.id,
1886
- row: templateSpace.row,
1887
- col: templateSpace.col
1888
- };
1889
- }).sort((left, right) => left.id.localeCompare(right.id));
1890
- }
1891
- function deriveSquareEdges(spaces) {
1892
- const edgeMap = /* @__PURE__ */ new Map();
1893
- for (const space of spaces) {
1894
- for (const side of SQUARE_SIDES) {
1895
- const geometryKey = squareEdgeGeometryKey(space, side);
1896
- const existing = edgeMap.get(geometryKey);
1897
- const nextSpaceIds = dedupeSorted2([
1898
- ...existing?.spaceIds ?? [],
1899
- space.id
1900
- ]);
1901
- edgeMap.set(geometryKey, {
1902
- id: squareEdgeIdFromGeometryKey(geometryKey),
1903
- geometryKey,
1904
- spaceIds: nextSpaceIds,
1905
- typeId: existing?.typeId ?? null,
1906
- label: existing?.label ?? null,
1907
- fields: existing?.fields ?? null
1908
- });
1909
- }
1910
- }
1911
- return [...edgeMap.values()].sort(
1912
- (left, right) => left.id.localeCompare(right.id)
1913
- );
1914
- }
1915
- function deriveSquareVertices(spaces) {
1916
- const vertexMap = /* @__PURE__ */ new Map();
1917
- for (const space of spaces) {
1918
- for (const corner of SQUARE_CORNERS) {
1919
- const geometryKey = squareCornerGeometryKey(space, corner);
1920
- const existing = vertexMap.get(geometryKey);
1921
- const nextSpaceIds = dedupeSorted2([
1922
- ...existing?.spaceIds ?? [],
1923
- space.id
1924
- ]);
1925
- vertexMap.set(geometryKey, {
1926
- id: squareVertexIdFromGeometryKey(geometryKey),
1927
- geometryKey,
1928
- spaceIds: nextSpaceIds,
1929
- typeId: existing?.typeId ?? null,
1930
- label: existing?.label ?? null,
1931
- fields: existing?.fields ?? null
1932
- });
1933
- }
1934
- }
1935
- return [...vertexMap.values()].sort(
1936
- (left, right) => left.id.localeCompare(right.id)
1937
- );
1938
- }
1939
- function indexSquareEdgeMetadata(specs, spacesById, ownerLabel) {
1940
- const metadataByKey = /* @__PURE__ */ new Map();
1941
- for (const spec of specs) {
1942
- const geometryKey = geometryKeyFromSquareEdgeRef(spec.ref, spacesById);
1943
- if (metadataByKey.has(geometryKey)) {
1944
- throw new Error(`${ownerLabel} contains duplicate square edge refs.`);
1945
- }
1946
- metadataByKey.set(geometryKey, {
1947
- geometryKey,
1948
- typeId: spec.typeId ?? null,
1949
- label: spec.label ?? null,
1950
- fields: spec.fields ?? null
1951
- });
1952
- }
1953
- return metadataByKey;
1954
- }
1955
- function indexSquareVertexMetadata(specs, spacesById, ownerLabel) {
1956
- const metadataByKey = /* @__PURE__ */ new Map();
1957
- for (const spec of specs) {
1958
- const geometryKey = geometryKeyFromSquareVertexRef(spec.ref, spacesById);
1959
- if (metadataByKey.has(geometryKey)) {
1960
- throw new Error(`${ownerLabel} contains duplicate square vertex refs.`);
1961
- }
1962
- metadataByKey.set(geometryKey, {
1963
- geometryKey,
1964
- typeId: spec.typeId ?? null,
1965
- label: spec.label ?? null,
1966
- fields: spec.fields ?? null
1967
- });
1968
- }
1969
- return metadataByKey;
1970
- }
1971
- function resolveSquareEdges(board, template, spaces) {
1972
- const spacesById = new Map(spaces.map((space) => [space.id, space]));
1973
- const derived = deriveSquareEdges(spaces);
1974
- const edgesByGeometryKey = new Map(
1975
- derived.map((edge) => [edge.geometryKey, edge])
1976
- );
1977
- const templateMetadata = template ? indexSquareEdgeMetadata(
1978
- template.edges ?? [],
1979
- spacesById,
1980
- `Square board template '${template.id}'`
1981
- ) : /* @__PURE__ */ new Map();
1982
- const overrideMetadata = indexSquareEdgeMetadata(
1983
- board.edges ?? [],
1984
- spacesById,
1985
- `Square board '${board.id}'`
1986
- );
1987
- if (template) {
1988
- for (const overrideKey of overrideMetadata.keys()) {
1989
- if (!templateMetadata.has(overrideKey)) {
1990
- throw new Error(
1991
- `Square board '${board.id}' overrides unknown edge ref from template '${template.id}'.`
1992
- );
1993
- }
1994
- }
1995
- }
1996
- const mergedMetadata = template ? new Map(
1997
- [...templateMetadata.entries()].map(([key, metadata]) => [
1998
- key,
1999
- {
2000
- ...metadata,
2001
- ...overrideMetadata.get(key) ?? {},
2002
- geometryKey: key
2003
- }
2004
- ])
2005
- ) : overrideMetadata;
2006
- for (const [geometryKey, metadata] of mergedMetadata.entries()) {
2007
- const edge = edgesByGeometryKey.get(geometryKey);
2008
- if (!edge) {
2009
- throw new Error(
2010
- `Square edge ref on board '${board.id}' does not resolve to a derived edge.`
2011
- );
2012
- }
2013
- edgesByGeometryKey.set(geometryKey, {
2014
- ...edge,
2015
- typeId: metadata.typeId ?? null,
2016
- label: metadata.label ?? null,
2017
- fields: metadata.fields ?? null
2018
- });
2019
- }
2020
- return [...edgesByGeometryKey.values()].sort(
2021
- (left, right) => left.id.localeCompare(right.id)
2022
- );
2023
- }
2024
- function resolveSquareVertices(board, template, spaces) {
2025
- const spacesById = new Map(spaces.map((space) => [space.id, space]));
2026
- const derived = deriveSquareVertices(spaces);
2027
- const verticesByGeometryKey = new Map(
2028
- derived.map((vertex) => [vertex.geometryKey, vertex])
2029
- );
2030
- const templateMetadata = template ? indexSquareVertexMetadata(
2031
- template.vertices ?? [],
2032
- spacesById,
2033
- `Square board template '${template.id}'`
2034
- ) : /* @__PURE__ */ new Map();
2035
- const overrideMetadata = indexSquareVertexMetadata(
2036
- board.vertices ?? [],
2037
- spacesById,
2038
- `Square board '${board.id}'`
2039
- );
2040
- if (template) {
2041
- for (const overrideKey of overrideMetadata.keys()) {
2042
- if (!templateMetadata.has(overrideKey)) {
2043
- throw new Error(
2044
- `Square board '${board.id}' overrides unknown vertex ref from template '${template.id}'.`
2045
- );
2046
- }
2047
- }
2048
- }
2049
- const mergedMetadata = template ? new Map(
2050
- [...templateMetadata.entries()].map(([key, metadata]) => [
2051
- key,
2052
- {
2053
- ...metadata,
2054
- ...overrideMetadata.get(key) ?? {},
2055
- geometryKey: key
2056
- }
2057
- ])
2058
- ) : overrideMetadata;
2059
- for (const [geometryKey, metadata] of mergedMetadata.entries()) {
2060
- const vertex = verticesByGeometryKey.get(geometryKey);
2061
- if (!vertex) {
2062
- throw new Error(
2063
- `Square vertex ref on board '${board.id}' does not resolve to a derived vertex.`
2064
- );
2065
- }
2066
- verticesByGeometryKey.set(geometryKey, {
2067
- ...vertex,
2068
- typeId: metadata.typeId ?? null,
2069
- label: metadata.label ?? null,
2070
- fields: metadata.fields ?? null
2071
- });
2072
- }
2073
- return [...verticesByGeometryKey.values()].sort(
2074
- (left, right) => left.id.localeCompare(right.id)
2075
- );
2076
- }
2077
- function analyzeBoards(manifest, playerIds) {
2078
- const boardTemplates = manifest.boardTemplates ?? [];
2079
- const genericTemplateById = new Map(
2080
- boardTemplates.filter(isGenericBoardTemplateSpec).map((boardTemplate) => [boardTemplate.id, boardTemplate])
2081
- );
2082
- const hexTemplateById = new Map(
2083
- boardTemplates.filter(isHexBoardTemplateSpec2).map((boardTemplate) => [boardTemplate.id, boardTemplate])
2084
- );
2085
- const squareTemplateById = new Map(
2086
- boardTemplates.filter(isSquareBoardTemplateSpec).map((boardTemplate) => [boardTemplate.id, boardTemplate])
2087
- );
2088
- return (manifest.boards ?? []).map((board) => {
2089
- const runtimeBoardIds = board.scope === "perPlayer" ? playerIds.map((playerId) => `${board.id}:${playerId}`) : [board.id];
2090
- if (isHexBoardSpec2(board)) {
2091
- const hexBoard = board;
2092
- const template2 = board.templateId ? hexTemplateById.get(board.templateId) : void 0;
2093
- const spaces = resolveHexSpaces2(hexBoard, template2);
2094
- return {
2095
- layout: "hex",
2096
- board: hexBoard,
2097
- template: template2,
2098
- boardTypeId: hexBoard.typeId ?? template2?.typeId,
2099
- runtimeBoardIds,
2100
- boardFieldsSchema: hexBoard.boardFieldsSchema ?? template2?.boardFieldsSchema,
2101
- spaceFieldsSchema: hexBoard.spaceFieldsSchema ?? template2?.spaceFieldsSchema,
2102
- edgeFieldsSchema: hexBoard.edgeFieldsSchema ?? template2?.edgeFieldsSchema,
2103
- vertexFieldsSchema: hexBoard.vertexFieldsSchema ?? template2?.vertexFieldsSchema,
2104
- spaces,
2105
- authoredEdges: resolveAuthoredHexEdges(hexBoard, template2, spaces),
2106
- authoredVertices: resolveAuthoredHexVertices(
2107
- hexBoard,
2108
- template2,
2109
- spaces
2110
- ),
2111
- edges: resolveHexEdges(hexBoard, template2, spaces),
2112
- vertices: resolveHexVertices(hexBoard, template2, spaces)
2113
- };
2114
- }
2115
- if (isSquareBoardSpec(board)) {
2116
- const squareBoard = board;
2117
- const template2 = board.templateId ? squareTemplateById.get(board.templateId) : void 0;
2118
- const spaces = resolveSquareSpaces(squareBoard, template2);
2119
- return {
2120
- layout: "square",
2121
- board: squareBoard,
2122
- template: template2,
2123
- boardTypeId: squareBoard.typeId ?? template2?.typeId,
2124
- runtimeBoardIds,
2125
- boardFieldsSchema: squareBoard.boardFieldsSchema ?? template2?.boardFieldsSchema,
2126
- spaceFieldsSchema: squareBoard.spaceFieldsSchema ?? template2?.spaceFieldsSchema,
2127
- relationFieldsSchema: squareBoard.relationFieldsSchema ?? template2?.relationFieldsSchema,
2128
- containerFieldsSchema: squareBoard.containerFieldsSchema ?? template2?.containerFieldsSchema,
2129
- edgeFieldsSchema: squareBoard.edgeFieldsSchema ?? template2?.edgeFieldsSchema,
2130
- vertexFieldsSchema: squareBoard.vertexFieldsSchema ?? template2?.vertexFieldsSchema,
2131
- spaces,
2132
- relations: [
2133
- ...template2?.relations ?? [],
2134
- ...squareBoard.relations ?? []
2135
- ],
2136
- containers: mergeBoardContainers(
2137
- template2?.containers ?? [],
2138
- squareBoard.containers ?? []
2139
- ),
2140
- edges: resolveSquareEdges(squareBoard, template2, spaces),
2141
- vertices: resolveSquareVertices(squareBoard, template2, spaces)
2142
- };
2143
- }
2144
- const genericBoard = board;
2145
- const template = board.templateId ? genericTemplateById.get(board.templateId) : void 0;
2146
- return {
2147
- layout: "generic",
2148
- board: genericBoard,
2149
- template,
2150
- boardTypeId: genericBoard.typeId ?? template?.typeId,
2151
- runtimeBoardIds,
2152
- boardFieldsSchema: genericBoard.boardFieldsSchema ?? template?.boardFieldsSchema,
2153
- spaceFieldsSchema: genericBoard.spaceFieldsSchema ?? template?.spaceFieldsSchema,
2154
- relationFieldsSchema: genericBoard.relationFieldsSchema ?? template?.relationFieldsSchema,
2155
- containerFieldsSchema: genericBoard.containerFieldsSchema ?? template?.containerFieldsSchema,
2156
- spaces: mergeBoardSpaces(
2157
- template?.spaces ?? [],
2158
- genericBoard.spaces ?? []
2159
- ),
2160
- relations: [
2161
- ...template?.relations ?? [],
2162
- ...genericBoard.relations ?? []
2163
- ],
2164
- containers: mergeBoardContainers(
2165
- template?.containers ?? [],
2166
- genericBoard.containers ?? []
2167
- )
2168
- };
2169
- });
2170
- }
2171
- function isSingletonExplicitSeed(seed) {
2172
- return typeof seed.id === "string" && seed.id.length > 0 && (seed.count ?? 1) === 1;
2173
- }
2174
- function analyzeManifest(inputManifest, runtimePlayerIds) {
2175
- const manifest = addStandardDecksIfNeeded(inputManifest);
2176
- const playerIds = runtimePlayerIds ? [...runtimePlayerIds] : Array.from(
2177
- { length: manifest.players.maxPlayers },
2178
- (_, index) => `player-${index + 1}`
2179
- );
2180
- const sharedZones = (manifest.zones ?? []).filter(
2181
- (zone) => zone.scope === "shared"
2182
- );
2183
- const playerZones = (manifest.zones ?? []).filter(
2184
- (zone) => zone.scope === "perPlayer"
2185
- );
2186
- const zoneIds = dedupeSorted2((manifest.zones ?? []).map((zone) => zone.id));
2187
- const cardSets = manifest.cardSets.map(materializeCardSet);
2188
- const cardSetIds = dedupeSorted2(cardSets.map((cardSet) => cardSet.id));
2189
- const cardTypes = dedupeSorted2(
2190
- cardSets.flatMap((cardSet) => cardSet.cards.map((card) => card.type))
2191
- );
2192
- const cardIds = dedupeSorted2(
2193
- cardSets.flatMap((cardSet) => cardSet.cards.flatMap(renderCardInstanceIds2))
2194
- );
2195
- const cardSetIdByCardId = /* @__PURE__ */ new Map();
2196
- const cardTypeByCardId = /* @__PURE__ */ new Map();
2197
- for (const cardSet of cardSets) {
2198
- for (const card of cardSet.cards) {
2199
- for (const cardId of renderCardInstanceIds2(card)) {
2200
- cardSetIdByCardId.set(cardId, cardSet.id);
2201
- cardTypeByCardId.set(cardId, card.cardType ?? card.type);
2202
- }
2203
- }
2204
- }
2205
- const sharedZoneCardSetIds = /* @__PURE__ */ new Map();
2206
- for (const zone of sharedZones) {
2207
- sharedZoneCardSetIds.set(
2208
- zone.id,
2209
- dedupeSorted2(zone.allowedCardSetIds ?? [])
2210
- );
2211
- }
2212
- const playerZoneCardSetIds = /* @__PURE__ */ new Map();
2213
- for (const zone of playerZones) {
2214
- playerZoneCardSetIds.set(
2215
- zone.id,
2216
- dedupeSorted2(zone.allowedCardSetIds ?? [])
2217
- );
2218
- }
2219
- const zoneCardSetIdsById = /* @__PURE__ */ new Map();
2220
- for (const [zoneId, cardSetIds2] of sharedZoneCardSetIds.entries()) {
2221
- zoneCardSetIdsById.set(zoneId, cardSetIds2);
2222
- }
2223
- for (const [zoneId, cardSetIds2] of playerZoneCardSetIds.entries()) {
2224
- zoneCardSetIdsById.set(zoneId, cardSetIds2);
2225
- }
2226
- const sharedZoneIdsByCardSetId = new Map(
2227
- cardSetIds.map((cardSetId) => [cardSetId, []])
2228
- );
2229
- for (const [zoneId, allowedCardSetIds] of sharedZoneCardSetIds.entries()) {
2230
- for (const cardSetId of allowedCardSetIds) {
2231
- const zoneIds2 = sharedZoneIdsByCardSetId.get(cardSetId) ?? [];
2232
- zoneIds2.push(zoneId);
2233
- sharedZoneIdsByCardSetId.set(cardSetId, zoneIds2);
2234
- }
2235
- }
2236
- for (const [cardSetId, zoneIds2] of sharedZoneIdsByCardSetId.entries()) {
2237
- sharedZoneIdsByCardSetId.set(cardSetId, dedupeSorted2(zoneIds2));
2238
- }
2239
- const sharedZoneIdSet = new Set(sharedZones.map((zone) => zone.id));
2240
- const homeSharedZoneIdsByCardType = new Map(
2241
- cardTypes.map((cardType) => [cardType, []])
2242
- );
2243
- for (const cardSet of cardSets) {
2244
- for (const card of cardSet.cards) {
2245
- if (card.home?.type !== "zone" || !sharedZoneIdSet.has(card.home.zoneId)) {
2246
- continue;
2247
- }
2248
- const zoneIds2 = homeSharedZoneIdsByCardType.get(card.type) ?? [];
2249
- zoneIds2.push(card.home.zoneId);
2250
- homeSharedZoneIdsByCardType.set(card.type, zoneIds2);
2251
- }
2252
- }
2253
- const homeSharedZoneIdByCardType = /* @__PURE__ */ new Map();
2254
- for (const [cardType, zoneIds2] of homeSharedZoneIdsByCardType.entries()) {
2255
- const uniqueZoneIds = dedupeSorted2(zoneIds2);
2256
- homeSharedZoneIdsByCardType.set(cardType, uniqueZoneIds);
2257
- const [homeZoneId] = uniqueZoneIds;
2258
- if (uniqueZoneIds.length === 1 && homeZoneId !== void 0) {
2259
- homeSharedZoneIdByCardType.set(cardType, homeZoneId);
2260
- }
2261
- }
2262
- const zoneVisibilityById = new Map(
2263
- (manifest.zones ?? []).map((zone) => [
2264
- zone.id,
2265
- zone.visibility ?? "public"
2266
- ])
2267
- );
2268
- const resourceIds = dedupeSorted2(
2269
- (manifest.resources ?? []).map((resource) => resource.id)
2270
- );
2271
- const resourcePresentationById = sortedObject(
2272
- (manifest.resources ?? []).map((resource) => [
2273
- resource.id,
2274
- {
2275
- label: resource.name,
2276
- ...resource.icon ? { icon: resource.icon } : {}
2277
- }
2278
- ])
2279
- );
2280
- const setupOptionIds = dedupeSorted2(
2281
- (manifest.setupOptions ?? []).map((option) => option.id)
2282
- );
2283
- const setupProfileIds = dedupeSorted2(
2284
- (manifest.setupProfiles ?? []).map((profile) => profile.id)
2285
- );
2286
- const setupChoiceIdsByOptionId = new Map(
2287
- (manifest.setupOptions ?? []).map((option) => [
2288
- option.id,
2289
- dedupeSorted2((option.choices ?? []).map((choice) => choice.id))
2290
- ])
2291
- );
2292
- const setupOptionsById = Object.fromEntries(
2293
- (manifest.setupOptions ?? []).slice().sort((left, right) => left.id.localeCompare(right.id)).map((option) => [
2294
- option.id,
2295
- {
2296
- id: option.id,
2297
- name: option.name,
2298
- description: option.description ?? null,
2299
- choices: (option.choices ?? []).map((choice) => ({
2300
- id: choice.id,
2301
- label: choice.label,
2302
- description: choice.description ?? null
2303
- }))
2304
- }
2305
- ])
2306
- );
2307
- const setupProfilesById = Object.fromEntries(
2308
- (manifest.setupProfiles ?? []).slice().sort((left, right) => left.id.localeCompare(right.id)).map((profile) => [
2309
- profile.id,
2310
- {
2311
- id: profile.id,
2312
- name: profile.name,
2313
- description: profile.description ?? null,
2314
- optionValues: profile.optionValues ? Object.fromEntries(
2315
- Object.entries(profile.optionValues).filter(
2316
- (entry) => typeof entry[1] === "string"
2317
- )
2318
- ) : null,
2319
- guidance: profile.guidance ? {
2320
- summary: profile.guidance.summary ?? null,
2321
- steps: (profile.guidance.steps ?? []).map((step) => ({
2322
- id: step.id,
2323
- label: step.label,
2324
- description: step.description ?? null
2325
- }))
2326
- } : null
2327
- }
2328
- ])
2329
- );
2330
- const pieceTypeIds = dedupeSorted2(
2331
- (manifest.pieceTypes ?? []).map((pieceType) => pieceType.id)
2332
- );
2333
- const pieceTypeSchemasById = new Map(
2334
- (manifest.pieceTypes ?? []).map((pieceType) => [
2335
- pieceType.id,
2336
- pieceType.fieldsSchema
2337
- ])
2338
- );
2339
- const pieceTypeSlotIdsById = new Map(
2340
- (manifest.pieceTypes ?? []).map((pieceType) => [
2341
- pieceType.id,
2342
- dedupeSorted2((pieceType.slots ?? []).map((slot) => slot.id))
2343
- ])
2344
- );
2345
- const pieceTypeIdByPieceId = /* @__PURE__ */ new Map();
2346
- for (const seed of manifest.pieceSeeds ?? []) {
2347
- for (const pieceId of expandSeedIds2([seed])) {
2348
- pieceTypeIdByPieceId.set(pieceId, seed.typeId);
2349
- }
2350
- }
2351
- const pieceIds = dedupeSorted2(pieceTypeIdByPieceId.keys());
2352
- const dieTypeIds = dedupeSorted2(
2353
- (manifest.dieTypes ?? []).map((dieType) => dieType.id)
2354
- );
2355
- const dieTypeSchemasById = new Map(
2356
- (manifest.dieTypes ?? []).map((dieType) => [
2357
- dieType.id,
2358
- dieType.fieldsSchema
2359
- ])
2360
- );
2361
- const dieTypeSlotIdsById = new Map(
2362
- (manifest.dieTypes ?? []).map((dieType) => [
2363
- dieType.id,
2364
- dedupeSorted2((dieType.slots ?? []).map((slot) => slot.id))
2365
- ])
2366
- );
2367
- const dieTypeIdByDieId = /* @__PURE__ */ new Map();
2368
- for (const seed of manifest.dieSeeds ?? []) {
2369
- for (const dieId of expandSeedIds2([seed])) {
2370
- dieTypeIdByDieId.set(dieId, seed.typeId);
2371
- }
2372
- }
2373
- const dieIds = dedupeSorted2(dieTypeIdByDieId.keys());
2374
- const strictSlotHosts = [
2375
- ...(manifest.pieceSeeds ?? []).flatMap((seed) => {
2376
- if (!isSingletonExplicitSeed(seed)) {
2377
- return [];
2378
- }
2379
- const slotIds = pieceTypeSlotIdsById.get(seed.typeId) ?? [];
2380
- return slotIds.length > 0 ? [
2381
- {
2382
- kind: "piece",
2383
- id: seed.id,
2384
- slotIds
2385
- }
2386
- ] : [];
2387
- }),
2388
- ...(manifest.dieSeeds ?? []).flatMap((seed) => {
2389
- if (!isSingletonExplicitSeed(seed)) {
2390
- return [];
2391
- }
2392
- const slotIds = dieTypeSlotIdsById.get(seed.typeId) ?? [];
2393
- return slotIds.length > 0 ? [
2394
- {
2395
- kind: "die",
2396
- id: seed.id,
2397
- slotIds
2398
- }
2399
- ] : [];
2400
- })
2401
- ].sort(
2402
- (left, right) => left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id)
2403
- );
2404
- const analyzedBoards = analyzeBoards(manifest, playerIds);
2405
- const boardTemplateIds = dedupeSorted2(
2406
- (manifest.boardTemplates ?? []).map((boardTemplate) => boardTemplate.id)
2407
- );
2408
- const boardBaseIds = dedupeSorted2(
2409
- analyzedBoards.map(({ board }) => board.id)
2410
- );
2411
- const boardIds = dedupeSorted2(
2412
- analyzedBoards.flatMap((entry) => entry.runtimeBoardIds)
2413
- );
2414
- const boardTypeIds = dedupeSorted2(
2415
- analyzedBoards.map((board) => board.boardTypeId).filter((typeId) => typeof typeId === "string")
2416
- );
2417
- const boardLayoutById = /* @__PURE__ */ new Map();
2418
- const boardIdsByLayout = /* @__PURE__ */ new Map();
2419
- const boardBaseIdsByLayout = /* @__PURE__ */ new Map();
2420
- const boardIdsByBaseId = /* @__PURE__ */ new Map();
2421
- const boardBaseIdsByTemplateId = /* @__PURE__ */ new Map();
2422
- const boardTemplateLayoutById = /* @__PURE__ */ new Map();
2423
- const boardIdsByTypeId = /* @__PURE__ */ new Map();
2424
- const spaceIdsByBoardId = /* @__PURE__ */ new Map();
2425
- const spaceTypeIdByBoardId = /* @__PURE__ */ new Map();
2426
- const spaceIdsByTypeId = /* @__PURE__ */ new Map();
2427
- const containerIdsByBoardId = /* @__PURE__ */ new Map();
2428
- const containerHostByBoardId = /* @__PURE__ */ new Map();
2429
- const relationTypeIdsByBoardId = /* @__PURE__ */ new Map();
2430
- const edgeIdsByTypeId = /* @__PURE__ */ new Map();
2431
- const edgeIdsByBoardIdAndTypeId = /* @__PURE__ */ new Map();
2432
- const vertexIdsByTypeId = /* @__PURE__ */ new Map();
2433
- const vertexIdsByBoardIdAndTypeId = /* @__PURE__ */ new Map();
2434
- for (const boardTemplate of manifest.boardTemplates ?? []) {
2435
- boardTemplateLayoutById.set(boardTemplate.id, boardTemplate.layout);
2436
- boardBaseIdsByLayout.set(
2437
- boardTemplate.layout,
2438
- dedupeSorted2([...boardBaseIdsByLayout.get(boardTemplate.layout) ?? []])
2439
- );
2440
- }
2441
- for (const analyzedBoard of analyzedBoards) {
2442
- boardIdsByBaseId.set(analyzedBoard.board.id, analyzedBoard.runtimeBoardIds);
2443
- boardIdsByLayout.set(
2444
- analyzedBoard.board.layout,
2445
- dedupeSorted2([
2446
- ...boardIdsByLayout.get(analyzedBoard.board.layout) ?? [],
2447
- ...analyzedBoard.runtimeBoardIds
2448
- ])
2449
- );
2450
- boardBaseIdsByLayout.set(
2451
- analyzedBoard.board.layout,
2452
- dedupeSorted2([
2453
- ...boardBaseIdsByLayout.get(analyzedBoard.board.layout) ?? [],
2454
- analyzedBoard.board.id
2455
- ])
2456
- );
2457
- if (analyzedBoard.board.templateId) {
2458
- boardBaseIdsByTemplateId.set(
2459
- analyzedBoard.board.templateId,
2460
- dedupeSorted2([
2461
- ...boardBaseIdsByTemplateId.get(analyzedBoard.board.templateId) ?? [],
2462
- analyzedBoard.board.id
2463
- ])
2464
- );
2465
- }
2466
- const runtimeSpaceIds = analyzedBoard.spaces.map((space) => space.id);
2467
- const runtimeSpaceTypeIds = sortedObject(
2468
- analyzedBoard.spaces.map(
2469
- (space) => [space.id, space.typeId ?? null]
2470
- )
2471
- );
2472
- const runtimeContainerIds = analyzedBoard.layout === "hex" ? [] : analyzedBoard.containers.map((container) => container.id);
2473
- const runtimeContainerHosts = analyzedBoard.layout === "hex" ? {} : sortedObject(
2474
- analyzedBoard.containers.map((container) => [
2475
- container.id,
2476
- container.host.type === "space" ? { type: "space", spaceId: container.host.spaceId } : { type: "board" }
2477
- ])
2478
- );
2479
- const runtimeRelationTypeIds = analyzedBoard.layout === "hex" ? ["adjacent"] : analyzedBoard.layout === "square" ? dedupeSorted2([
2480
- "adjacent",
2481
- ...analyzedBoard.relations.map((relation) => relation.typeId)
2482
- ]) : dedupeSorted2(
2483
- analyzedBoard.relations.map((relation) => relation.typeId)
2484
- );
2485
- for (const runtimeBoardId of analyzedBoard.runtimeBoardIds) {
2486
- boardLayoutById.set(runtimeBoardId, analyzedBoard.board.layout);
2487
- spaceIdsByBoardId.set(runtimeBoardId, runtimeSpaceIds);
2488
- spaceTypeIdByBoardId.set(runtimeBoardId, runtimeSpaceTypeIds);
2489
- containerIdsByBoardId.set(runtimeBoardId, runtimeContainerIds);
2490
- containerHostByBoardId.set(runtimeBoardId, runtimeContainerHosts);
2491
- relationTypeIdsByBoardId.set(runtimeBoardId, runtimeRelationTypeIds);
2492
- }
2493
- if (analyzedBoard.boardTypeId) {
2494
- boardIdsByTypeId.set(
2495
- analyzedBoard.boardTypeId,
2496
- dedupeSorted2([
2497
- ...boardIdsByTypeId.get(analyzedBoard.boardTypeId) ?? [],
2498
- ...analyzedBoard.runtimeBoardIds
2499
- ])
2500
- );
2501
- }
2502
- for (const space of analyzedBoard.spaces) {
2503
- if (!space.typeId) {
2504
- continue;
2505
- }
2506
- spaceIdsByTypeId.set(
2507
- space.typeId,
2508
- dedupeSorted2([...spaceIdsByTypeId.get(space.typeId) ?? [], space.id])
2509
- );
2510
- }
2511
- if (analyzedBoard.layout !== "generic") {
2512
- const edgeIdsForBoardByType = {};
2513
- for (const edge of analyzedBoard.edges) {
2514
- if (!edge.typeId) {
2515
- continue;
2516
- }
2517
- edgeIdsByTypeId.set(
2518
- edge.typeId,
2519
- dedupeSorted2([...edgeIdsByTypeId.get(edge.typeId) ?? [], edge.id])
2520
- );
2521
- edgeIdsForBoardByType[edge.typeId] = dedupeSorted2([
2522
- ...edgeIdsForBoardByType[edge.typeId] ?? [],
2523
- edge.id
2524
- ]);
2525
- }
2526
- const vertexIdsForBoardByType = {};
2527
- for (const vertex of analyzedBoard.vertices) {
2528
- if (!vertex.typeId) {
2529
- continue;
2530
- }
2531
- vertexIdsByTypeId.set(
2532
- vertex.typeId,
2533
- dedupeSorted2([
2534
- ...vertexIdsByTypeId.get(vertex.typeId) ?? [],
2535
- vertex.id
2536
- ])
2537
- );
2538
- vertexIdsForBoardByType[vertex.typeId] = dedupeSorted2([
2539
- ...vertexIdsForBoardByType[vertex.typeId] ?? [],
2540
- vertex.id
2541
- ]);
2542
- }
2543
- for (const runtimeBoardId of analyzedBoard.runtimeBoardIds) {
2544
- edgeIdsByBoardIdAndTypeId.set(runtimeBoardId, edgeIdsForBoardByType);
2545
- vertexIdsByBoardIdAndTypeId.set(
2546
- runtimeBoardId,
2547
- vertexIdsForBoardByType
2548
- );
2549
- }
2550
- }
2551
- }
2552
- const relationTypeIds = dedupeSorted2(
2553
- Array.from(relationTypeIdsByBoardId.values()).flat()
2554
- );
2555
- const edgeIds = dedupeSorted2(
2556
- analyzedBoards.flatMap(
2557
- (board) => board.layout === "generic" ? [] : board.edges.map((edge) => edge.id)
2558
- )
2559
- );
2560
- const edgeTypeIds = dedupeSorted2(edgeIdsByTypeId.keys());
2561
- const vertexIds = dedupeSorted2(
2562
- analyzedBoards.flatMap(
2563
- (board) => board.layout === "generic" ? [] : board.vertices.map((vertex) => vertex.id)
2564
- )
2565
- );
2566
- const vertexTypeIds = dedupeSorted2(vertexIdsByTypeId.keys());
2567
- const boardContainerIds = dedupeSorted2(
2568
- analyzedBoards.flatMap(
2569
- (board) => board.layout === "hex" ? [] : board.containers.map((container) => container.id)
2570
- )
2571
- );
2572
- const spaceIds = dedupeSorted2(
2573
- analyzedBoards.flatMap((board) => board.spaces.map((space) => space.id))
2574
- );
2575
- const spaceTypeIds = dedupeSorted2(
2576
- analyzedBoards.flatMap(
2577
- (board) => board.spaces.map((space) => space.typeId).filter((typeId) => typeof typeId === "string")
2578
- )
2579
- );
2580
- return {
2581
- manifest,
2582
- playerIds,
2583
- sharedZones,
2584
- playerZones,
2585
- zoneIds,
2586
- cardSets,
2587
- cardSetIds,
2588
- cardTypes,
2589
- cardIds,
2590
- cardSetIdByCardId,
2591
- cardTypeByCardId,
2592
- sharedZoneCardSetIds,
2593
- sharedZoneIdsByCardSetId,
2594
- homeSharedZoneIdsByCardType,
2595
- homeSharedZoneIdByCardType,
2596
- playerZoneCardSetIds,
2597
- zoneCardSetIdsById,
2598
- zoneVisibilityById,
2599
- resourceIds,
2600
- resourcePresentationById,
2601
- setupOptionIds,
2602
- setupProfileIds,
2603
- setupChoiceIdsByOptionId,
2604
- setupOptionsById,
2605
- setupProfilesById,
2606
- pieceTypeIds,
2607
- pieceIds,
2608
- pieceTypeIdByPieceId,
2609
- dieTypeIds,
2610
- dieIds,
2611
- dieTypeIdByDieId,
2612
- strictSlotHosts,
2613
- boardTemplateIds,
2614
- boardBaseIds,
2615
- boardIds,
2616
- boardContainerIds,
2617
- boardTypeIds,
2618
- boardLayoutById,
2619
- boardIdsByLayout,
2620
- boardBaseIdsByLayout,
2621
- boardIdsByBaseId,
2622
- boardBaseIdsByTemplateId,
2623
- boardTemplateLayoutById,
2624
- boardIdsByTypeId,
2625
- spaceIdsByBoardId,
2626
- spaceTypeIdByBoardId,
2627
- spaceIdsByTypeId,
2628
- containerIdsByBoardId,
2629
- containerHostByBoardId,
2630
- relationTypeIds,
2631
- relationTypeIdsByBoardId,
2632
- edgeIds,
2633
- edgeTypeIds,
2634
- edgeIdsByTypeId,
2635
- edgeIdsByBoardIdAndTypeId,
2636
- vertexIds,
2637
- vertexTypeIds,
2638
- vertexIdsByTypeId,
2639
- vertexIdsByBoardIdAndTypeId,
2640
- spaceIds,
2641
- spaceTypeIds,
2642
- analyzedBoards,
2643
- pieceTypeSchemasById,
2644
- dieTypeSchemasById
2645
- };
2646
- }
2647
- function cloneJson(value) {
2648
- return JSON.parse(JSON.stringify(value));
2649
- }
2650
- function boardSpaceRefKey(spaceIds) {
2651
- return [...spaceIds].sort((left, right) => left.localeCompare(right)).join("$$");
2652
- }
2653
- function materializePropertySchemaDefault(property, analysis, runtimeBoardId) {
2654
- if (hasPropertySchemaDefault(property)) {
2655
- return cloneJson(property.default);
2656
- }
2657
- if (property.optional) {
2658
- return void 0;
2659
- }
2660
- if (property.nullable) {
2661
- return null;
2662
- }
2663
- switch (property.type) {
2664
- case "string":
2665
- return "";
2666
- case "integer":
2667
- case "number":
2668
- return 0;
2669
- case "boolean":
2670
- return false;
2671
- case "enum":
2672
- return property.enums?.[0] ?? "";
2673
- case "array":
2674
- return [];
2675
- case "object":
2676
- return materializeObjectSchemaDefaults(
2677
- property.properties ? { properties: property.properties } : void 0,
2678
- analysis,
2679
- runtimeBoardId
2680
- );
2681
- case "record":
2682
- return {};
2683
- case "zoneId":
2684
- return analysis.zoneIds[0] ?? "";
2685
- case "cardId":
2686
- return analysis.cardIds[0] ?? "";
2687
- case "playerId":
2688
- return analysis.playerIds[0] ?? "";
2689
- case "boardId":
2690
- return runtimeBoardId ?? analysis.boardIds[0] ?? "";
2691
- case "edgeId":
2692
- return analysis.edgeIds[0] ?? "";
2693
- case "vertexId":
2694
- return analysis.vertexIds[0] ?? "";
2695
- case "spaceId":
2696
- return (runtimeBoardId ? analysis.spaceIdsByBoardId.get(runtimeBoardId)?.[0] : void 0) ?? analysis.spaceIds[0] ?? "";
2697
- case "pieceId":
2698
- return analysis.pieceIds[0] ?? "";
2699
- case "dieId":
2700
- return analysis.dieIds[0] ?? "";
2701
- case "resourceId":
2702
- return analysis.resourceIds[0] ?? "";
2703
- }
2704
- }
2705
- function materializeObjectSchemaDefaults(schema, analysis, runtimeBoardId) {
2706
- if (!schema?.properties) {
2707
- return {};
2708
- }
2709
- return Object.fromEntries(
2710
- Object.entries(schema.properties).flatMap(([key, property]) => {
2711
- const value = materializePropertySchemaDefault(
2712
- property,
2713
- analysis,
2714
- runtimeBoardId
2715
- );
2716
- return value === void 0 ? [] : [[key, value]];
2717
- })
2718
- );
2719
- }
2720
- function materializeCardPropertiesDefaults(schema, cardType, analysis) {
2721
- const objectSchema = isCardPropertySchemaVariants(schema) ? mergeSharedCardProperties(schema, cardType) : schema;
2722
- return materializeObjectSchemaDefaults(objectSchema, analysis);
2723
- }
2724
- function materializeManifestTable(options) {
2725
- const validation = validateManifestAuthoring(options.manifest);
2726
- if (validation.errors.length > 0) {
2727
- throw new Error(
2728
- `Cannot materialize invalid topology manifest:
2729
- ${validation.errors.join("\n")}`
2730
- );
2731
- }
2732
- const analysis = analyzeManifest(options.manifest, options.playerIds);
2733
- const manifest = analysis.manifest;
2734
- const playerIds = [...options.playerIds];
2735
- const cards = createRecord();
2736
- const pieces = createRecord();
2737
- const dice = createRecord();
2738
- const componentLocations = createRecord();
2739
- const locationOrder = /* @__PURE__ */ new Map();
2740
- const boardAnalysisByBaseId = new Map(
2741
- analysis.analyzedBoards.map((board) => [board.board.id, board])
2742
- );
2743
- const edgeIdByBoardBaseIdAndSpaces = /* @__PURE__ */ new Map();
2744
- const vertexIdByBoardBaseIdAndSpaces = /* @__PURE__ */ new Map();
2745
- for (const analyzedBoard of analysis.analyzedBoards) {
2746
- if (analyzedBoard.layout === "generic") {
2747
- continue;
2748
- }
2749
- edgeIdByBoardBaseIdAndSpaces.set(
2750
- analyzedBoard.board.id,
2751
- new Map(
2752
- analyzedBoard.edges.map((edge) => [
2753
- boardSpaceRefKey(edge.spaceIds),
2754
- edge.id
2755
- ])
2756
- )
2757
- );
2758
- vertexIdByBoardBaseIdAndSpaces.set(
2759
- analyzedBoard.board.id,
2760
- new Map(
2761
- analyzedBoard.vertices.map((vertex) => [
2762
- boardSpaceRefKey(vertex.spaceIds),
2763
- vertex.id
2764
- ])
2765
- )
2766
- );
2767
- }
2768
- const nextLocationPosition = (location) => {
2769
- const key = JSON.stringify(location);
2770
- const position = locationOrder.get(key) ?? 0;
2771
- locationOrder.set(key, position + 1);
2772
- return position;
2773
- };
2774
- const zoneScopeById = new Map([
2775
- ...analysis.sharedZones.map((zone) => [zone.id, "shared"]),
2776
- ...analysis.playerZones.map((zone) => [zone.id, "perPlayer"])
2777
- ]);
2778
- const resolveRuntimeBoardId = (boardBaseId, ownerId, context) => {
2779
- const analyzedBoard = boardAnalysisByBaseId.get(boardBaseId);
2780
- if (!analyzedBoard) {
2781
- throw new Error(
2782
- `${context ?? "Component home"}.boardId: Unknown board '${boardBaseId}'.`
2783
- );
2784
- }
2785
- if (analyzedBoard?.board.scope === "perPlayer") {
2786
- if (!ownerId) {
2787
- throw new Error(
2788
- `${context ?? `Home on board '${boardBaseId}'`} requires ownerId because board '${boardBaseId}' has scope 'perPlayer'. Add ownerId to resolve the player-scoped destination.`
2789
- );
2790
- }
2791
- return `${boardBaseId}:${ownerId}`;
2792
- }
2793
- return boardBaseId;
2794
- };
2795
- const resolveBoardEdgeId = (boardBaseId, spaceIds) => {
2796
- const edgeId = edgeIdByBoardBaseIdAndSpaces.get(boardBaseId)?.get(boardSpaceRefKey(spaceIds));
2797
- if (!edgeId) {
2798
- throw new Error(
2799
- `Unknown edge on board '${boardBaseId}' for spaces [${spaceIds.join(", ")}].`
2800
- );
2801
- }
2802
- return edgeId;
2803
- };
2804
- const resolveBoardVertexId = (boardBaseId, spaceIds) => {
2805
- const vertexId = vertexIdByBoardBaseIdAndSpaces.get(boardBaseId)?.get(boardSpaceRefKey(spaceIds));
2806
- if (!vertexId) {
2807
- throw new Error(
2808
- `Unknown vertex on board '${boardBaseId}' for spaces [${spaceIds.join(", ")}].`
2809
- );
2810
- }
2811
- return vertexId;
2812
- };
2813
- const materializeComponentLocation = (home, context) => {
2814
- if (home.type === "detached") {
2815
- return { type: "Detached" };
2816
- }
2817
- if (home.type === "slot") {
2818
- return {
2819
- type: "InSlot",
2820
- host: home.host,
2821
- slotId: home.slotId,
2822
- position: nextLocationPosition({
2823
- type: "InSlot",
2824
- host: home.host,
2825
- slotId: home.slotId
2826
- })
2827
- };
2828
- }
2829
- if (home.type === "zone") {
2830
- const zoneScope = zoneScopeById.get(home.zoneId);
2831
- if (!zoneScope) {
2832
- throw new Error(
2833
- `${context.path}.zoneId: ${context.label} targets unknown zone '${home.zoneId}'.`
2834
- );
2835
- }
2836
- if (zoneScope === "perPlayer") {
2837
- throw new Error(
2838
- `${context.path}.zoneId: ${context.label} cannot target per-player zone '${home.zoneId}' because card inventory has no ownerId. Place it during reducer setup instead.`
2839
- );
2840
- }
2841
- return {
2842
- type: "InDeck",
2843
- deckId: home.zoneId,
2844
- playedBy: null,
2845
- position: nextLocationPosition({
2846
- type: "InDeck",
2847
- deckId: home.zoneId,
2848
- playedBy: null
2849
- })
2850
- };
2851
- }
2852
- if (home.type === "space") {
2853
- const boardId = resolveRuntimeBoardId(
2854
- home.boardId,
2855
- null,
2856
- `${context.path}.boardId: ${context.label}`
2857
- );
2858
- if (!analysis.spaceIdsByBoardId.get(home.boardId)?.includes(home.spaceId)) {
2859
- throw new Error(
2860
- `${context.path}.spaceId: ${context.label} targets unknown space '${home.spaceId}' on board '${home.boardId}'.`
2861
- );
2862
- }
2863
- return {
2864
- type: "OnSpace",
2865
- boardId,
2866
- spaceId: home.spaceId,
2867
- position: nextLocationPosition({
2868
- type: "OnSpace",
2869
- boardId,
2870
- spaceId: home.spaceId
2871
- })
2872
- };
2873
- }
2874
- if (home.type === "container") {
2875
- const boardId = resolveRuntimeBoardId(
2876
- home.boardId,
2877
- null,
2878
- `${context.path}.boardId: ${context.label}`
2879
- );
2880
- if (!analysis.containerIdsByBoardId.get(home.boardId)?.includes(home.containerId)) {
2881
- throw new Error(
2882
- `${context.path}.containerId: ${context.label} targets unknown container '${home.containerId}' on board '${home.boardId}'.`
2883
- );
2884
- }
2885
- return {
2886
- type: "InContainer",
2887
- boardId,
2888
- containerId: home.containerId,
2889
- position: nextLocationPosition({
2890
- type: "InContainer",
2891
- boardId,
2892
- containerId: home.containerId
2893
- })
2894
- };
2895
- }
2896
- if (home.type === "edge") {
2897
- const boardId = resolveRuntimeBoardId(
2898
- home.boardId,
2899
- null,
2900
- `${context.path}.boardId: ${context.label}`
2901
- );
2902
- const edgeId = resolveBoardEdgeId(home.boardId, home.ref.spaces);
2903
- return {
2904
- type: "OnEdge",
2905
- boardId,
2906
- edgeId,
2907
- position: nextLocationPosition({
2908
- type: "OnEdge",
2909
- boardId,
2910
- edgeId
2911
- })
2912
- };
2913
- }
2914
- if (home.type === "vertex") {
2915
- const boardId = resolveRuntimeBoardId(
2916
- home.boardId,
2917
- null,
2918
- `${context.path}.boardId: ${context.label}`
2919
- );
2920
- const vertexId = resolveBoardVertexId(home.boardId, home.ref.spaces);
2921
- return {
2922
- type: "OnVertex",
2923
- boardId,
2924
- vertexId,
2925
- position: nextLocationPosition({
2926
- type: "OnVertex",
2927
- boardId,
2928
- vertexId
2929
- })
2930
- };
2931
- }
2932
- const exhaustive = home;
2933
- throw new Error(
2934
- `Unsupported component home: ${JSON.stringify(exhaustive)}`
2935
- );
2936
- };
2937
- for (const [cardSetIndex, cardSet] of manifest.cardSets.entries()) {
2938
- const materializedCardSet = materializeCardSet(cardSet);
2939
- for (const [cardIndex, card] of materializedCardSet.cards.entries()) {
2940
- const cardInstanceIds = renderCardInstanceIds2(card);
2941
- for (const [instanceIndex, cardId] of cardInstanceIds.entries()) {
2942
- cards[cardId] = {
2943
- id: cardId,
2944
- cardSetId: materializedCardSet.id,
2945
- cardType: card.type,
2946
- name: card.name,
2947
- text: card.text,
2948
- properties: {
2949
- ...materializeCardPropertiesDefaults(
2950
- materializedCardSet.cardSchema,
2951
- card.type,
2952
- analysis
2953
- ),
2954
- ...card.properties ?? {}
2955
- }
2956
- };
2957
- const resolvedHome = card.home ?? materializedCardSet.defaultHome;
2958
- const path = card.home === void 0 ? `manifest.cardSets[${cardSetIndex}].defaultHome` : `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].home`;
2959
- componentLocations[cardId] = materializeComponentLocation(
2960
- resolvedHome,
2961
- {
2962
- path,
2963
- label: `Card '${card.type}' instance ${instanceIndex + 1}`
2964
- }
2965
- );
2966
- }
2967
- }
2968
- }
2969
- if (Object.keys(cards).length !== analysis.cardIds.length) {
2970
- throw new Error(
2971
- "Materialized card record cardinality drifted from analysis."
2972
- );
2973
- }
2974
- if (Object.keys(componentLocations).length !== analysis.cardIds.length) {
2975
- throw new Error(
2976
- "Materialized card locations drifted from analysis before seed placement."
2977
- );
2978
- }
2979
- const pieceTypesById = new Map(
2980
- (manifest.pieceTypes ?? []).map((pieceType) => [pieceType.id, pieceType])
2981
- );
2982
- const dieTypesById = new Map(
2983
- (manifest.dieTypes ?? []).map((dieType) => [dieType.id, dieType])
2984
- );
2985
- const assignSeedLocation = (componentId, ownerId, home, context) => {
2986
- if (!home) {
2987
- componentLocations[componentId] = { type: "Detached" };
2988
- return;
2989
- }
2990
- if (home.type === "slot") {
2991
- componentLocations[componentId] = {
2992
- type: "InSlot",
2993
- host: home.host,
2994
- slotId: home.slotId,
2995
- position: nextLocationPosition({
2996
- type: "InSlot",
2997
- host: home.host,
2998
- slotId: home.slotId
2999
- })
3000
- };
3001
- return;
3002
- }
3003
- if (home.type === "zone") {
3004
- if (zoneScopeById.get(home.zoneId) === "perPlayer") {
3005
- if (!ownerId) {
3006
- throw new Error(
3007
- `${context.path}.zoneId: ${context.label} requires ownerId because zone '${home.zoneId}' has scope 'perPlayer'. Add ownerId to resolve the player-scoped destination.`
3008
- );
3009
- }
3010
- componentLocations[componentId] = {
3011
- type: "InHand",
3012
- handId: home.zoneId,
3013
- playerId: ownerId,
3014
- position: nextLocationPosition({
3015
- type: "InHand",
3016
- handId: home.zoneId,
3017
- playerId: ownerId
3018
- })
3019
- };
3020
- return;
3021
- }
3022
- componentLocations[componentId] = {
3023
- type: "InZone",
3024
- zoneId: home.zoneId,
3025
- playedBy: null,
3026
- position: nextLocationPosition({
3027
- type: "InZone",
3028
- zoneId: home.zoneId
3029
- })
3030
- };
3031
- return;
3032
- }
3033
- if (home.type === "space") {
3034
- const boardId = resolveRuntimeBoardId(
3035
- home.boardId,
3036
- ownerId,
3037
- `${context.path}.boardId: ${context.label}`
3038
- );
3039
- componentLocations[componentId] = {
3040
- type: "OnSpace",
3041
- boardId,
3042
- spaceId: home.spaceId,
3043
- position: nextLocationPosition({
3044
- type: "OnSpace",
3045
- boardId,
3046
- spaceId: home.spaceId
3047
- })
3048
- };
3049
- return;
3050
- }
3051
- if (home.type === "container") {
3052
- const boardId = resolveRuntimeBoardId(
3053
- home.boardId,
3054
- ownerId,
3055
- `${context.path}.boardId: ${context.label}`
3056
- );
3057
- componentLocations[componentId] = {
3058
- type: "InContainer",
3059
- boardId,
3060
- containerId: home.containerId,
3061
- position: nextLocationPosition({
3062
- type: "InContainer",
3063
- boardId,
3064
- containerId: home.containerId
3065
- })
3066
- };
3067
- return;
3068
- }
3069
- if (home.type === "edge") {
3070
- const boardId = resolveRuntimeBoardId(
3071
- home.boardId,
3072
- ownerId,
3073
- `${context.path}.boardId: ${context.label}`
3074
- );
3075
- const edgeId = resolveBoardEdgeId(home.boardId, home.ref.spaces);
3076
- componentLocations[componentId] = {
3077
- type: "OnEdge",
3078
- boardId,
3079
- edgeId,
3080
- position: nextLocationPosition({
3081
- type: "OnEdge",
3082
- boardId,
3083
- edgeId
3084
- })
3085
- };
3086
- return;
3087
- }
3088
- if (home.type === "vertex") {
3089
- const boardId = resolveRuntimeBoardId(
3090
- home.boardId,
3091
- ownerId,
3092
- `${context.path}.boardId: ${context.label}`
3093
- );
3094
- const vertexId = resolveBoardVertexId(home.boardId, home.ref.spaces);
3095
- componentLocations[componentId] = {
3096
- type: "OnVertex",
3097
- boardId,
3098
- vertexId,
3099
- position: nextLocationPosition({
3100
- type: "OnVertex",
3101
- boardId,
3102
- vertexId
3103
- })
3104
- };
3105
- return;
3106
- }
3107
- componentLocations[componentId] = { type: "Detached" };
3108
- };
3109
- for (const [seedIndex, seed] of (manifest.pieceSeeds ?? []).entries()) {
3110
- const pieceType = pieceTypesById.get(seed.typeId);
3111
- for (const componentId of expandSeedIds2([seed])) {
3112
- pieces[componentId] = {
3113
- componentType: "piece",
3114
- id: componentId,
3115
- pieceTypeId: seed.typeId,
3116
- pieceName: pieceType?.name ?? seed.typeId,
3117
- ownerId: seed.ownerId ?? null,
3118
- properties: {
3119
- ...materializeObjectSchemaDefaults(
3120
- analysis.pieceTypeSchemasById.get(seed.typeId),
3121
- analysis
3122
- ),
3123
- ...seed.fields ?? {}
3124
- }
3125
- };
3126
- assignSeedLocation(componentId, seed.ownerId, seed.home, {
3127
- path: `manifest.pieceSeeds[${seedIndex}].home`,
3128
- label: `Piece seed '${seed.id ?? seed.typeId}'`
3129
- });
3130
- }
3131
- }
3132
- for (const [seedIndex, seed] of (manifest.dieSeeds ?? []).entries()) {
3133
- const dieType = dieTypesById.get(seed.typeId);
3134
- for (const componentId of expandSeedIds2([seed])) {
3135
- dice[componentId] = {
3136
- componentType: "die",
3137
- id: componentId,
3138
- dieTypeId: seed.typeId,
3139
- dieName: dieType?.name ?? seed.typeId,
3140
- ownerId: seed.ownerId ?? null,
3141
- sides: dieType?.sides ?? 6,
3142
- value: null,
3143
- properties: {
3144
- ...materializeObjectSchemaDefaults(
3145
- analysis.dieTypeSchemasById.get(seed.typeId),
3146
- analysis
3147
- ),
3148
- ...seed.fields ?? {}
3149
- }
3150
- };
3151
- assignSeedLocation(componentId, seed.ownerId, seed.home, {
3152
- path: `manifest.dieSeeds[${seedIndex}].home`,
3153
- label: `Die seed '${seed.id ?? seed.typeId}'`
3154
- });
3155
- }
3156
- }
3157
- const boardStatesById = createRecord();
3158
- const hexBoardStatesById = createRecord();
3159
- const squareBoardStatesById = createRecord();
3160
- for (const analyzedBoard of analysis.analyzedBoards) {
3161
- const sharedBoardState = {
3162
- baseId: analyzedBoard.board.id,
3163
- layout: analyzedBoard.layout,
3164
- typeId: analyzedBoard.boardTypeId ?? null,
3165
- scope: analyzedBoard.board.scope,
3166
- templateId: analyzedBoard.board.templateId ?? null,
3167
- fields: {
3168
- ...materializeObjectSchemaDefaults(
3169
- analyzedBoard.boardFieldsSchema,
3170
- analysis
3171
- ),
3172
- ...analyzedBoard.board.fields ?? {}
3173
- }
3174
- };
3175
- const buildSpaces = () => Object.fromEntries(
3176
- analyzedBoard.spaces.map((space) => {
3177
- const spaceId = space.id;
3178
- const baseSpaceState = {
3179
- id: spaceId,
3180
- name: "name" in space ? space.name ?? null : null,
3181
- typeId: space?.typeId ?? null,
3182
- fields: {
3183
- ...materializeObjectSchemaDefaults(
3184
- analyzedBoard.spaceFieldsSchema,
3185
- analysis
3186
- ),
3187
- ...space.fields ?? {}
3188
- },
3189
- zoneId: "zoneId" in space ? space.zoneId ?? null : null
3190
- };
3191
- if (analyzedBoard.layout === "hex") {
3192
- const hexSpace = space;
3193
- return [
3194
- spaceId,
3195
- {
3196
- ...baseSpaceState,
3197
- q: hexSpace.q,
3198
- r: hexSpace.r
3199
- }
3200
- ];
3201
- }
3202
- if (analyzedBoard.layout === "square") {
3203
- const squareSpace = space;
3204
- return [
3205
- spaceId,
3206
- {
3207
- ...baseSpaceState,
3208
- row: squareSpace.row,
3209
- col: squareSpace.col
3210
- }
3211
- ];
3212
- }
3213
- return [spaceId, baseSpaceState];
3214
- })
3215
- );
3216
- const relations = analyzedBoard.layout === "hex" ? [] : analyzedBoard.relations.map((relation) => ({
3217
- id: relation.id ?? null,
3218
- typeId: relation.typeId,
3219
- fromSpaceId: relation.fromSpaceId,
3220
- toSpaceId: relation.toSpaceId,
3221
- directed: relation.directed ?? false,
3222
- fields: {
3223
- ...materializeObjectSchemaDefaults(
3224
- analyzedBoard.relationFieldsSchema,
3225
- analysis
3226
- ),
3227
- ...relation.fields ?? {}
3228
- }
3229
- }));
3230
- const buildContainers = (runtimeBoardId) => analyzedBoard.layout === "hex" ? {} : Object.fromEntries(
3231
- analyzedBoard.containers.map((container) => {
3232
- const containerId = container.id;
3233
- return [
3234
- containerId,
3235
- {
3236
- id: containerId,
3237
- name: container.name ?? containerId,
3238
- host: container.host.type === "space" ? { type: "space", spaceId: container.host.spaceId } : { type: "board" },
3239
- allowedCardSetIds: container.allowedCardSetIds,
3240
- zoneId: `board:${runtimeBoardId}:container:${containerId}`,
3241
- fields: {
3242
- ...materializeObjectSchemaDefaults(
3243
- analyzedBoard.containerFieldsSchema,
3244
- analysis,
3245
- runtimeBoardId
3246
- ),
3247
- ...container.fields ?? {}
3248
- }
3249
- }
3250
- ];
3251
- })
3252
- );
3253
- const edges = analyzedBoard.layout === "generic" ? [] : analyzedBoard.edges.map((edge) => ({
3254
- id: edge.id,
3255
- spaceIds: [...edge.spaceIds],
3256
- typeId: edge.typeId ?? null,
3257
- label: edge.label ?? null,
3258
- ownerId: null,
3259
- fields: {
3260
- ...materializeObjectSchemaDefaults(
3261
- analyzedBoard.edgeFieldsSchema,
3262
- analysis
3263
- ),
3264
- ...edge.fields ?? {}
3265
- }
3266
- }));
3267
- const vertices = analyzedBoard.layout === "generic" ? [] : analyzedBoard.vertices.map((vertex) => ({
3268
- id: vertex.id,
3269
- spaceIds: [...vertex.spaceIds],
3270
- typeId: vertex.typeId ?? null,
3271
- label: vertex.label ?? null,
3272
- ownerId: null,
3273
- fields: {
3274
- ...materializeObjectSchemaDefaults(
3275
- analyzedBoard.vertexFieldsSchema,
3276
- analysis
3277
- ),
3278
- ...vertex.fields ?? {}
3279
- }
3280
- }));
3281
- for (const runtimeBoardId of analyzedBoard.runtimeBoardIds) {
3282
- const playerId = analyzedBoard.board.scope === "perPlayer" ? runtimeBoardId.slice(analyzedBoard.board.id.length + 1) : null;
3283
- const boardState = {
3284
- id: runtimeBoardId,
3285
- ...sharedBoardState,
3286
- playerId,
3287
- spaces: cloneJson(buildSpaces()),
3288
- relations: cloneJson(relations),
3289
- containers: cloneJson(buildContainers(runtimeBoardId)),
3290
- ...analyzedBoard.layout === "hex" ? {
3291
- orientation: analyzedBoard.board.orientation ?? "pointy-top",
3292
- edges: cloneJson(edges),
3293
- vertices: cloneJson(vertices)
3294
- } : analyzedBoard.layout === "square" ? {
3295
- edges: cloneJson(edges),
3296
- vertices: cloneJson(vertices)
3297
- } : {}
3298
- };
3299
- boardStatesById[runtimeBoardId] = boardState;
3300
- if (analyzedBoard.layout === "hex") {
3301
- hexBoardStatesById[runtimeBoardId] = boardState;
3302
- }
3303
- if (analyzedBoard.layout === "square") {
3304
- squareBoardStatesById[runtimeBoardId] = boardState;
3305
- }
3306
- }
3307
- }
3308
- const sharedZones = Object.fromEntries(
3309
- analysis.sharedZones.map((zone) => [zone.id, []])
3310
- );
3311
- const perPlayerZones = Object.fromEntries(
3312
- analysis.playerZones.map((zone) => [
3313
- zone.id,
3314
- Object.fromEntries(
3315
- playerIds.map((playerId) => [playerId, []])
3316
- )
3317
- ])
3318
- );
3319
- const zoneVisibility = Object.fromEntries(
3320
- (manifest.zones ?? []).map((zone) => [
3321
- zone.id,
3322
- zone.visibility ?? "public"
3323
- ])
3324
- );
3325
- const zoneCardSetIdsByZoneId = Object.fromEntries(
3326
- Array.from(analysis.zoneCardSetIdsById.entries()).sort(
3327
- ([left], [right]) => left.localeCompare(right)
3328
- )
3329
- );
3330
- const handVisibility = Object.fromEntries(
3331
- analysis.playerZones.map((zone) => [
3332
- zone.id,
3333
- zone.visibility ?? "ownerOnly"
3334
- ])
3335
- );
3336
- const componentSortPosition = (position) => typeof position === "number" ? position : Number.MAX_SAFE_INTEGER;
3337
- const pushSharedComponent = (zoneId, componentId) => {
3338
- const zone = sharedZones[zoneId];
3339
- if (!zone) {
3340
- return;
3341
- }
3342
- zone.push(componentId);
3343
- zone.sort(
3344
- (left, right) => componentSortPosition(componentLocations[left]?.position) - componentSortPosition(componentLocations[right]?.position)
3345
- );
3346
- };
3347
- const pushPlayerComponent = (zoneId, playerId, componentId) => {
3348
- const zone = perPlayerZones[zoneId]?.[playerId];
3349
- if (!zone) {
3350
- return;
3351
- }
3352
- zone.push(componentId);
3353
- zone.sort(
3354
- (left, right) => componentSortPosition(componentLocations[left]?.position) - componentSortPosition(componentLocations[right]?.position)
3355
- );
3356
- };
3357
- for (const [componentId, location] of Object.entries(componentLocations)) {
3358
- switch (location.type) {
3359
- case "InDeck":
3360
- pushSharedComponent(location.deckId, componentId);
3361
- break;
3362
- case "InHand":
3363
- pushPlayerComponent(
3364
- location.handId,
3365
- location.playerId,
3366
- componentId
3367
- );
3368
- break;
3369
- case "InZone":
3370
- pushSharedComponent(location.zoneId, componentId);
3371
- break;
3372
- default:
3373
- break;
3374
- }
3375
- }
3376
- const ownerOfCard = Object.fromEntries(
3377
- Object.keys(cards).map((cardId) => [cardId, null])
3378
- );
3379
- const visibility = Object.fromEntries(
3380
- Object.keys(cards).map((cardId) => [cardId, { faceUp: true }])
3381
- );
3382
- const resourcesByPlayer = Object.fromEntries(
3383
- playerIds.map((playerId) => [
3384
- playerId,
3385
- Object.fromEntries(
3386
- analysis.resourceIds.map((resourceId) => [resourceId, 0])
3387
- )
3388
- ])
3389
- );
3390
- const toPerPlayerWireFormat = (entries) => ({
3391
- __perPlayer: true,
3392
- entries: playerIds.map(
3393
- (playerId) => [playerId, entries[playerId]]
3394
- )
3395
- });
3396
- const perPlayerZonesWire = Object.fromEntries(
3397
- Object.entries(perPlayerZones).map(([zoneId, entries]) => [
3398
- zoneId,
3399
- toPerPlayerWireFormat(entries)
3400
- ])
3401
- );
3402
- const resourcesWire = toPerPlayerWireFormat(resourcesByPlayer);
3403
- return cloneJson({
3404
- playerOrder: playerIds,
3405
- zones: {
3406
- shared: sharedZones,
3407
- perPlayer: perPlayerZonesWire,
3408
- visibility: zoneVisibility,
3409
- cardSetIdsByZoneId: zoneCardSetIdsByZoneId
3410
- },
3411
- decks: cloneJson(sharedZones),
3412
- hands: cloneJson(perPlayerZonesWire),
3413
- handVisibility,
3414
- cards,
3415
- pieces,
3416
- componentLocations,
3417
- ownerOfCard,
3418
- visibility,
3419
- resources: resourcesWire,
3420
- boards: {
3421
- byId: boardStatesById,
3422
- hex: hexBoardStatesById,
3423
- square: squareBoardStatesById,
3424
- network: {},
3425
- track: {}
3426
- },
3427
- dice
3428
- });
3429
- }
3430
-
3431
- export {
3432
- analyzeManifest,
3433
- materializeManifestTable
3434
- };
3435
- //# sourceMappingURL=chunk-Z7QCREWI.js.map