@dreamboard-games/sdk 0.2.1-alpha.0 → 0.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/dist/{HandView-ncJIVLhN.d.ts → HandView-PYDmehVD.d.ts} +1 -1
  2. package/dist/{ResourceCounter-CTREyF73.d.ts → ResourceCounter-BTXklCEZ.d.ts} +1 -1
  3. package/dist/{ThemeProvider-fy0_QzgO.d.ts → ThemeProvider-DvMl17Ny.d.ts} +1 -1
  4. package/dist/attributes-BeRyboMS.d.ts +279 -0
  5. package/dist/browser-interaction.d.ts +708 -0
  6. package/dist/browser-interaction.js +106 -0
  7. package/dist/{chunk-KAELH4KC.js → chunk-3RQEICD3.js} +2 -2
  8. package/dist/{chunk-TDSWKVZ4.js → chunk-ADYH6PVT.js} +2457 -1265
  9. package/dist/chunk-ADYH6PVT.js.map +1 -0
  10. package/dist/{chunk-YFY3RL52.js → chunk-P7VMTJ5D.js} +2 -2
  11. package/dist/{chunk-YFY3RL52.js.map → chunk-P7VMTJ5D.js.map} +1 -1
  12. package/dist/chunk-QLG6VEMW.js +1691 -0
  13. package/dist/chunk-QLG6VEMW.js.map +1 -0
  14. package/dist/{chunk-WYPQ3GG5.js → chunk-QZ6X4B5P.js} +1177 -1106
  15. package/dist/chunk-QZ6X4B5P.js.map +1 -0
  16. package/dist/{chunk-GKKBPPSW.js → chunk-TXXLS3OI.js} +405 -309
  17. package/dist/chunk-TXXLS3OI.js.map +1 -0
  18. package/dist/{chunk-FDNZTDD6.js → chunk-W6SGFWXH.js} +48 -44
  19. package/dist/chunk-W6SGFWXH.js.map +1 -0
  20. package/dist/{chunk-MKXPVOUT.js → chunk-Y2AFYBMB.js} +48 -4
  21. package/dist/chunk-Y2AFYBMB.js.map +1 -0
  22. package/dist/{index-BwqPQtBu.d.ts → codegen.d.ts} +57 -565
  23. package/dist/{infrastructure/workspace-codegen.js → codegen.js} +3 -3
  24. package/dist/{components-D5ZRE2Hl.d.ts → components-DaIqi9QM.d.ts} +48 -37
  25. package/dist/{hex-board-view-D_07hO6O.d.ts → hex-board-view-UCUjMALd.d.ts} +3 -2
  26. package/dist/{infrastructure/reducer-bundle-abi.d.ts → index.d-BGqVifr_.d.ts} +289 -10
  27. package/dist/index.js +1 -1
  28. package/dist/package-set.d.ts +2 -2
  29. package/dist/package-set.js +1 -1
  30. package/dist/primitive-props-BNHDkgd7.d.ts +16 -0
  31. package/dist/reducer-contract.d.ts +3 -0
  32. package/dist/{infrastructure/reducer-bundle-abi.js → reducer-contract.js} +4 -4
  33. package/dist/reducer.d.ts +185 -179
  34. package/dist/reducer.js +1637 -1353
  35. package/dist/reducer.js.map +1 -1
  36. package/dist/runtime/primitives.d.ts +10 -9
  37. package/dist/runtime/primitives.js +8 -7
  38. package/dist/runtime/runtime-api.d.ts +2 -0
  39. package/dist/runtime/runtime-api.js +1 -0
  40. package/dist/runtime/workspace-contract.d.ts +63 -17
  41. package/dist/runtime/workspace-contract.js +5 -5
  42. package/dist/{runtime-api-DWxvTr-O.d.ts → runtime-api-tGL3ArsB.d.ts} +109 -4
  43. package/dist/runtime.d.ts +12 -57
  44. package/dist/runtime.js +487 -7
  45. package/dist/runtime.js.map +1 -1
  46. package/dist/testing.d.ts +4 -4
  47. package/dist/testing.js.map +1 -1
  48. package/dist/types.d.ts +1531 -85
  49. package/dist/types.js +1 -3
  50. package/dist/types.js.map +1 -1
  51. package/dist/ui/components.d.ts +7 -8
  52. package/dist/ui/components.js +1 -1
  53. package/dist/ui/defaults.d.ts +1 -1
  54. package/dist/ui/{types/player-state.d.ts → player-state.d.ts} +2 -2
  55. package/dist/{ui-contract-iQfTtUSL.d.ts → ui-contract-SctM4ibF.d.ts} +157 -150
  56. package/dist/ui.d.ts +10 -11
  57. package/dist/ui.js +2 -2
  58. package/package.json +35 -74
  59. package/dist/bundle-CDd5FKeD.d.ts +0 -283
  60. package/dist/cards-Sl3b40Mv.d.ts +0 -13
  61. package/dist/chunk-7YAHLYBR.js +0 -481
  62. package/dist/chunk-7YAHLYBR.js.map +0 -1
  63. package/dist/chunk-FDNZTDD6.js.map +0 -1
  64. package/dist/chunk-GKKBPPSW.js.map +0 -1
  65. package/dist/chunk-I46YJSOD.js +0 -1
  66. package/dist/chunk-MKXPVOUT.js.map +0 -1
  67. package/dist/chunk-T52J5RMF.js +0 -1
  68. package/dist/chunk-TDSWKVZ4.js.map +0 -1
  69. package/dist/chunk-U5C6BONG.js +0 -34
  70. package/dist/chunk-U5C6BONG.js.map +0 -1
  71. package/dist/chunk-WYPQ3GG5.js.map +0 -1
  72. package/dist/generated/runtime/primitives.d.ts +0 -12
  73. package/dist/generated/runtime/primitives.js +0 -180
  74. package/dist/generated/runtime/primitives.js.map +0 -1
  75. package/dist/generated/runtime-api.d.ts +0 -3
  76. package/dist/generated/runtime-api.js +0 -2
  77. package/dist/generated/runtime.d.ts +0 -14
  78. package/dist/generated/runtime.js +0 -18
  79. package/dist/generated/runtime.js.map +0 -1
  80. package/dist/generated/workspace-contract.d.ts +0 -14
  81. package/dist/generated/workspace-contract.js +0 -14
  82. package/dist/generated/workspace-contract.js.map +0 -1
  83. package/dist/infrastructure/reducer-bundle-abi.js.map +0 -1
  84. package/dist/infrastructure/workspace-codegen.d.ts +0 -53
  85. package/dist/infrastructure/workspace-codegen.js.map +0 -1
  86. package/dist/manifest-contract-BNHVGFtU.d.ts +0 -9
  87. package/dist/primitive-props-DpKs-GCr.d.ts +0 -11
  88. package/dist/runtime/types/runtime-api.d.ts +0 -1
  89. package/dist/runtime/types/runtime-api.js +0 -2
  90. package/dist/runtime-api-3dshj6kK.d.ts +0 -101
  91. package/dist/slots-1GPGihk8.d.ts +0 -8
  92. package/src/generated/reducer-contract/builders.ts +0 -90
  93. package/src/generated/reducer-contract/version.ts +0 -9
  94. package/src/generated/reducer-contract/wire.ts +0 -100
  95. package/src/generated/reducer-contract/zod.ts +0 -103
  96. package/src/generated/runtime/primitives.ts +0 -2
  97. package/src/generated/runtime-api.ts +0 -5
  98. package/src/generated/runtime.ts +0 -35
  99. package/src/generated/workspace-contract.ts +0 -2
  100. package/src/index.ts +0 -7
  101. package/src/infrastructure/reducer-bundle-abi.ts +0 -8
  102. package/src/infrastructure/reducer-contract/bundle.ts +0 -37
  103. package/src/infrastructure/workspace-codegen/hex-geometry.ts +0 -69
  104. package/src/infrastructure/workspace-codegen/index.ts +0 -64
  105. package/src/infrastructure/workspace-codegen/manifest-contract.ts +0 -6632
  106. package/src/infrastructure/workspace-codegen/manifest-validation.ts +0 -795
  107. package/src/infrastructure/workspace-codegen/ownership.ts +0 -131
  108. package/src/infrastructure/workspace-codegen/preset-card-sets.ts +0 -169
  109. package/src/infrastructure/workspace-codegen/seeds.ts +0 -1705
  110. package/src/infrastructure/workspace-codegen.ts +0 -1
  111. package/src/package-set.ts +0 -19
  112. package/src/reducer/authoring/contract.ts +0 -157
  113. package/src/reducer/authoring/effect.ts +0 -224
  114. package/src/reducer/authoring/game.ts +0 -23
  115. package/src/reducer/authoring/interaction.ts +0 -98
  116. package/src/reducer/authoring/phase.ts +0 -300
  117. package/src/reducer/authoring/types.ts +0 -70
  118. package/src/reducer/authoring/validation.ts +0 -382
  119. package/src/reducer/authoring/view-stage.ts +0 -68
  120. package/src/reducer/authoring.ts +0 -29
  121. package/src/reducer/bundle/ingress-bundle.ts +0 -491
  122. package/src/reducer/bundle/trusted/engine-instruction-resolver.ts +0 -254
  123. package/src/reducer/bundle/trusted/flow-instruction-resolver.ts +0 -73
  124. package/src/reducer/bundle/trusted/instruction-runner.ts +0 -414
  125. package/src/reducer/bundle/trusted/interaction-authorization.ts +0 -137
  126. package/src/reducer/bundle/trusted/interaction-collectors.ts +0 -859
  127. package/src/reducer/bundle/trusted/interaction-decision.ts +0 -747
  128. package/src/reducer/bundle/trusted/interaction-resolver.ts +0 -95
  129. package/src/reducer/bundle/trusted/interaction-types.ts +0 -171
  130. package/src/reducer/bundle/trusted/lifecycle-runner.ts +0 -427
  131. package/src/reducer/bundle/trusted/projection-builder.ts +0 -356
  132. package/src/reducer/bundle/trusted/projection-context.ts +0 -39
  133. package/src/reducer/bundle/trusted/rng-sampler.ts +0 -150
  134. package/src/reducer/bundle/trusted/runtime-registry.ts +0 -120
  135. package/src/reducer/bundle/trusted/runtime-scope.ts +0 -336
  136. package/src/reducer/bundle/trusted/simultaneous-player.ts +0 -97
  137. package/src/reducer/bundle/trusted/stage-resolver.ts +0 -87
  138. package/src/reducer/bundle/trusted/static-projection.ts +0 -116
  139. package/src/reducer/bundle/trusted/trusted-runtime-args.ts +0 -97
  140. package/src/reducer/bundle/trusted/trusted-runtime-result.ts +0 -39
  141. package/src/reducer/bundle/trusted/trusted-setup-profiles.ts +0 -43
  142. package/src/reducer/bundle/trusted/trusted-state-codec.ts +0 -48
  143. package/src/reducer/bundle/trusted-bundle.ts +0 -97
  144. package/src/reducer/bundle/types.ts +0 -171
  145. package/src/reducer/bundle.ts +0 -2
  146. package/src/reducer/client-param-schemas.ts +0 -57
  147. package/src/reducer/compose.ts +0 -34
  148. package/src/reducer/core/runtime-input.ts +0 -30
  149. package/src/reducer/core/runtime-instruction.ts +0 -59
  150. package/src/reducer/core/types.ts +0 -62
  151. package/src/reducer/definition-index.ts +0 -277
  152. package/src/reducer/derived.ts +0 -106
  153. package/src/reducer/effects.ts +0 -92
  154. package/src/reducer/engine/runtime-instruction-engine.ts +0 -155
  155. package/src/reducer/ingress/decode-runtime-input.ts +0 -7
  156. package/src/reducer/ingress/decode-session-state.ts +0 -9
  157. package/src/reducer/ingress/encode-session-state.ts +0 -6
  158. package/src/reducer/ingress/input-codec.ts +0 -18
  159. package/src/reducer/ingress/phase-schemas.ts +0 -62
  160. package/src/reducer/ingress/raw-types.ts +0 -107
  161. package/src/reducer/ingress/runtime-codec.ts +0 -14
  162. package/src/reducer/ingress/runtime-payload.ts +0 -13
  163. package/src/reducer/ingress/session-codec.ts +0 -392
  164. package/src/reducer/ingress/types.ts +0 -6
  165. package/src/reducer/inputs/boardInput.ts +0 -217
  166. package/src/reducer/inputs/boardTarget.ts +0 -190
  167. package/src/reducer/inputs/cardInput.ts +0 -86
  168. package/src/reducer/inputs/cardTarget.ts +0 -101
  169. package/src/reducer/inputs/choiceTarget.ts +0 -104
  170. package/src/reducer/inputs/defineInputs.ts +0 -71
  171. package/src/reducer/inputs/formInput.ts +0 -809
  172. package/src/reducer/inputs/many.ts +0 -120
  173. package/src/reducer/inputs/promptInput.ts +0 -87
  174. package/src/reducer/inputs/rngInput.ts +0 -58
  175. package/src/reducer/inputs/targetRule.ts +0 -123
  176. package/src/reducer/inputs.ts +0 -41
  177. package/src/reducer/model/definition.ts +0 -1072
  178. package/src/reducer/model/extract.ts +0 -745
  179. package/src/reducer/model/manifest.ts +0 -570
  180. package/src/reducer/model/queries.ts +0 -641
  181. package/src/reducer/model/runtime.ts +0 -264
  182. package/src/reducer/model/spec.ts +0 -1386
  183. package/src/reducer/model/table.ts +0 -260
  184. package/src/reducer/model.ts +0 -7
  185. package/src/reducer/ops.ts +0 -1034
  186. package/src/reducer/parse-utils.ts +0 -28
  187. package/src/reducer/per-player.ts +0 -422
  188. package/src/reducer/rng.ts +0 -69
  189. package/src/reducer/schema-helpers.ts +0 -185
  190. package/src/reducer/setup-bootstrap-helpers.ts +0 -171
  191. package/src/reducer/setup-bootstrap.ts +0 -481
  192. package/src/reducer/table-ops.ts +0 -2671
  193. package/src/reducer/table-queries.ts +0 -372
  194. package/src/reducer/transaction.ts +0 -120
  195. package/src/reducer.ts +0 -314
  196. package/src/runtime/primitives.ts +0 -1
  197. package/src/runtime/types/runtime-api.ts +0 -1
  198. package/src/runtime/workspace-contract.ts +0 -32
  199. package/src/runtime-internal/components/InteractionForm.tsx +0 -1309
  200. package/src/runtime-internal/components/PluginRuntime.tsx +0 -103
  201. package/src/runtime-internal/components/board/target-layer.ts +0 -70
  202. package/src/runtime-internal/context/ClientParamSchemaContext.tsx +0 -44
  203. package/src/runtime-internal/context/InteractionDraftContext.tsx +0 -279
  204. package/src/runtime-internal/context/PluginSessionContext.tsx +0 -47
  205. package/src/runtime-internal/context/PluginStateContext.tsx +0 -262
  206. package/src/runtime-internal/context/RuntimeContext.tsx +0 -96
  207. package/src/runtime-internal/defaults/components.tsx +0 -409
  208. package/src/runtime-internal/defaults/index.ts +0 -11
  209. package/src/runtime-internal/errors/ValidationError.ts +0 -29
  210. package/src/runtime-internal/hooks/useActivePlayers.ts +0 -33
  211. package/src/runtime-internal/hooks/useBoardInteractions.ts +0 -665
  212. package/src/runtime-internal/hooks/useGameSelector.ts +0 -105
  213. package/src/runtime-internal/hooks/useGameView.ts +0 -9
  214. package/src/runtime-internal/hooks/useInteractionByKey.ts +0 -354
  215. package/src/runtime-internal/hooks/useInteractionHandle.ts +0 -438
  216. package/src/runtime-internal/hooks/useIsMyTurn.ts +0 -20
  217. package/src/runtime-internal/hooks/useLobby.ts +0 -76
  218. package/src/runtime-internal/hooks/useMe.ts +0 -48
  219. package/src/runtime-internal/hooks/usePlayerInfo.ts +0 -28
  220. package/src/runtime-internal/hooks/usePlayerTurnOrder.ts +0 -23
  221. package/src/runtime-internal/hooks/usePluginRuntime.ts +0 -147
  222. package/src/runtime-internal/hooks/useSeatInbox.ts +0 -61
  223. package/src/runtime-internal/hooks/useSimultaneousPhase.ts +0 -10
  224. package/src/runtime-internal/index.ts +0 -42
  225. package/src/runtime-internal/internal.ts +0 -43
  226. package/src/runtime-internal/plugin-styles.css +0 -250
  227. package/src/runtime-internal/primitives/board.tsx +0 -459
  228. package/src/runtime-internal/primitives/dialog-lifecycle.ts +0 -58
  229. package/src/runtime-internal/primitives/dice.tsx +0 -79
  230. package/src/runtime-internal/primitives/game-ui-provider.tsx +0 -35
  231. package/src/runtime-internal/primitives/game.tsx +0 -387
  232. package/src/runtime-internal/primitives/hand-intent-adapter.ts +0 -147
  233. package/src/runtime-internal/primitives/hand-surface.tsx +0 -594
  234. package/src/runtime-internal/primitives/index.ts +0 -196
  235. package/src/runtime-internal/primitives/interaction-form-binding.tsx +0 -56
  236. package/src/runtime-internal/primitives/interaction-submit.ts +0 -90
  237. package/src/runtime-internal/primitives/interaction.tsx +0 -987
  238. package/src/runtime-internal/primitives/phase.tsx +0 -43
  239. package/src/runtime-internal/primitives/player-roster.tsx +0 -302
  240. package/src/runtime-internal/primitives/primitive-props.tsx +0 -101
  241. package/src/runtime-internal/primitives/prompt.tsx +0 -255
  242. package/src/runtime-internal/primitives/ui.tsx +0 -60
  243. package/src/runtime-internal/primitives/zone.tsx +0 -791
  244. package/src/runtime-internal/reducer.ts +0 -30
  245. package/src/runtime-internal/runtime/createPluginRuntimeAPI.ts +0 -605
  246. package/src/runtime-internal/types/plugin-state.ts +0 -508
  247. package/src/runtime-internal/types/reducer-state.ts +0 -24
  248. package/src/runtime-internal/types/runtime-api.ts +0 -114
  249. package/src/runtime-internal/ui-contract.ts +0 -519
  250. package/src/runtime-internal/utils/card-intent-adapter.ts +0 -546
  251. package/src/runtime-internal/utils/interaction-inputs.ts +0 -492
  252. package/src/runtime-internal/utils/interaction-labels.ts +0 -23
  253. package/src/runtime-internal/utils/interaction-router.ts +0 -273
  254. package/src/runtime-internal/utils/interaction-status.ts +0 -74
  255. package/src/runtime-internal/workspace-contract.ts +0 -1170
  256. package/src/runtime.ts +0 -34
  257. package/src/testing/create-expect-api.ts +0 -352
  258. package/src/testing/create-test-runtime.ts +0 -381
  259. package/src/testing/definitions.ts +0 -127
  260. package/src/testing/index.ts +0 -3
  261. package/src/testing.ts +0 -1
  262. package/src/type-stubs/manifest-contract.d.ts +0 -42
  263. package/src/type-stubs/manifest-contract.js +0 -72
  264. package/src/type-stubs/ui-contract.d.ts +0 -5
  265. package/src/type-stubs/ui-contract.js +0 -1
  266. package/src/types/authoring-card-properties.type-test.ts +0 -266
  267. package/src/types/authoring.ts +0 -1282
  268. package/src/types/cards.ts +0 -19
  269. package/src/types/contracts.ts +0 -1550
  270. package/src/types/generated-helpers.ts +0 -35
  271. package/src/types/index.ts +0 -147
  272. package/src/types/slots.ts +0 -11
  273. package/src/types.ts +0 -1
  274. package/src/ui/components/ActionButton.tsx +0 -97
  275. package/src/ui/components/ActionPanel.tsx +0 -315
  276. package/src/ui/components/Card.tsx +0 -378
  277. package/src/ui/components/CardDragSurface.tsx +0 -1076
  278. package/src/ui/components/ChromeSuppressionContext.tsx +0 -70
  279. package/src/ui/components/CostDisplay.tsx +0 -145
  280. package/src/ui/components/DiceRoller.tsx +0 -581
  281. package/src/ui/components/Drawer.tsx +0 -180
  282. package/src/ui/components/ErrorBoundary.tsx +0 -275
  283. package/src/ui/components/GameEndDisplay.tsx +0 -398
  284. package/src/ui/components/GameSkeleton.tsx +0 -260
  285. package/src/ui/components/Hand.tsx +0 -468
  286. package/src/ui/components/HandDock.tsx +0 -299
  287. package/src/ui/components/HandView.tsx +0 -441
  288. package/src/ui/components/MobileHandTray.tsx +0 -381
  289. package/src/ui/components/MoreActions.tsx +0 -143
  290. package/src/ui/components/PhaseIndicator.tsx +0 -341
  291. package/src/ui/components/PlayArea.tsx +0 -146
  292. package/src/ui/components/PrimaryActionButton.tsx +0 -336
  293. package/src/ui/components/PrimaryButton.tsx +0 -45
  294. package/src/ui/components/ResourceCounter.tsx +0 -270
  295. package/src/ui/components/StagingZone.tsx +0 -134
  296. package/src/ui/components/ThemedButton.tsx +0 -113
  297. package/src/ui/components/Toast.tsx +0 -264
  298. package/src/ui/components/board/HexGrid.tsx +0 -1294
  299. package/src/ui/components/board/NetworkGraph.tsx +0 -476
  300. package/src/ui/components/board/SlotSystem.tsx +0 -388
  301. package/src/ui/components/board/SquareGrid.tsx +0 -1165
  302. package/src/ui/components/board/TrackBoard.tsx +0 -496
  303. package/src/ui/components/board/ZoneMap.tsx +0 -448
  304. package/src/ui/components/board/hex-board-view.ts +0 -123
  305. package/src/ui/components/board/index.ts +0 -142
  306. package/src/ui/components/board/interaction-accessibility.ts +0 -21
  307. package/src/ui/components/board/target-layer.ts +0 -66
  308. package/src/ui/components/card-render-content.type-test.ts +0 -27
  309. package/src/ui/components/hand-layout-math.ts +0 -163
  310. package/src/ui/components/hand-pointer-engine.ts +0 -413
  311. package/src/ui/components/index.ts +0 -245
  312. package/src/ui/components.ts +0 -1
  313. package/src/ui/defaults/components.tsx +0 -106
  314. package/src/ui/defaults/index.ts +0 -8
  315. package/src/ui/defaults.ts +0 -1
  316. package/src/ui/errors/ValidationError.ts +0 -29
  317. package/src/ui/helpers/cards.ts +0 -19
  318. package/src/ui/helpers/track-board.ts +0 -211
  319. package/src/ui/hooks/useBoardTopology.ts +0 -316
  320. package/src/ui/hooks/useCards.ts +0 -10
  321. package/src/ui/hooks/useHandCardPointer.ts +0 -381
  322. package/src/ui/hooks/useHandLayout.ts +0 -378
  323. package/src/ui/hooks/useHandPresentation.ts +0 -121
  324. package/src/ui/hooks/useHexBoard.ts +0 -74
  325. package/src/ui/hooks/useHexGrid.ts +0 -185
  326. package/src/ui/hooks/useIsMobile.ts +0 -35
  327. package/src/ui/hooks/usePanZoom.ts +0 -278
  328. package/src/ui/hooks/useSquareBoard.ts +0 -124
  329. package/src/ui/hooks/useSquareGrid.ts +0 -328
  330. package/src/ui/index.ts +0 -98
  331. package/src/ui/internal/ui/alert.tsx +0 -51
  332. package/src/ui/internal/ui/button.tsx +0 -58
  333. package/src/ui/internal/ui/dialog.tsx +0 -134
  334. package/src/ui/internal/ui/input.tsx +0 -21
  335. package/src/ui/internal/ui/label.tsx +0 -21
  336. package/src/ui/internal/ui/select.tsx +0 -129
  337. package/src/ui/internal/ui/tooltip.tsx +0 -54
  338. package/src/ui/internal/ui/utils.ts +0 -5
  339. package/src/ui/plugin-styles.css +0 -250
  340. package/src/ui/primitives/dialog-lifecycle.ts +0 -58
  341. package/src/ui/primitives/dice.tsx +0 -79
  342. package/src/ui/primitives/primitive-props.tsx +0 -101
  343. package/src/ui/theme/ThemeProvider.tsx +0 -252
  344. package/src/ui/theme/board.ts +0 -61
  345. package/src/ui/theme/css-vars.ts +0 -105
  346. package/src/ui/theme/derive.ts +0 -240
  347. package/src/ui/theme/index.ts +0 -61
  348. package/src/ui/theme/presets/arcade.ts +0 -261
  349. package/src/ui/theme/presets/studio.ts +0 -261
  350. package/src/ui/theme/presets/tabletop.ts +0 -266
  351. package/src/ui/theme/tokens.ts +0 -392
  352. package/src/ui/types/hex-color.ts +0 -20
  353. package/src/ui/types/player-state.ts +0 -463
  354. package/src/ui/types/tiled-board.ts +0 -785
  355. package/src/ui/types/visual-state.ts +0 -137
  356. package/src/ui.ts +0 -1
  357. /package/dist/{chunk-I46YJSOD.js.map → browser-interaction.js.map} +0 -0
  358. /package/dist/{chunk-KAELH4KC.js.map → chunk-3RQEICD3.js.map} +0 -0
  359. /package/dist/{chunk-T52J5RMF.js.map → codegen.js.map} +0 -0
  360. /package/dist/{generated/runtime-api.js.map → reducer-contract.js.map} +0 -0
  361. /package/dist/runtime/{types/runtime-api.js.map → runtime-api.js.map} +0 -0
  362. /package/dist/ui/{types/player-state.js → player-state.js} +0 -0
  363. /package/dist/ui/{types/player-state.js.map → player-state.js.map} +0 -0
package/dist/reducer.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  REDUCER_CONTRACT_VERSION,
3
- ReducerSessionStateSchema
4
- } from "./chunk-MKXPVOUT.js";
3
+ builders_exports,
4
+ zod_exports
5
+ } from "./chunk-Y2AFYBMB.js";
5
6
  import "./chunk-PZ5AY32C.js";
6
7
 
7
8
  // src/reducer/model/manifest.ts
@@ -1748,7 +1749,7 @@ function createRuntimeInstructionEngine({
1748
1749
  };
1749
1750
  }
1750
1751
 
1751
- // src/reducer/table-ops.ts
1752
+ // src/reducer/table/internal.ts
1752
1753
  function ppRead(value, playerId) {
1753
1754
  if (value === void 0) return void 0;
1754
1755
  return perPlayerGet(value, playerId);
@@ -1768,9 +1769,6 @@ function orderedComponentIdsForLocation(table, predicate) {
1768
1769
  (left, right) => locationPosition(left[1]) - locationPosition(right[1])
1769
1770
  ).map(([componentId]) => componentId);
1770
1771
  }
1771
- function allowedCardSetIdsForZone(table, zoneId) {
1772
- return table.zones.cardSetIdsByZoneId?.[zoneId] ?? [];
1773
- }
1774
1772
  function hasOwnKey(record, key) {
1775
1773
  return Object.prototype.hasOwnProperty.call(record, key);
1776
1774
  }
@@ -1803,51 +1801,24 @@ function assertZoneScope(table, zoneId, expectedScope, operation, argumentName)
1803
1801
  `Zone '${zoneId}' has scope '${actualScope}', but ${operation} requires ${argumentName} to be a ${expectedScope === "shared" ? "shared" : "perPlayer"} zone.`
1804
1802
  );
1805
1803
  }
1806
- function matchesSlotHost(location, host, slotId) {
1807
- return location.type === "InSlot" && location.host.kind === host.kind && location.host.id === host.id && (slotId === void 0 || location.slotId === slotId);
1808
- }
1809
- function componentPlayerId(table, componentId) {
1810
- const piece = table.pieces[componentId];
1811
- if (piece) {
1812
- return piece.ownerId ?? null;
1813
- }
1814
- const die = table.dice[componentId];
1815
- if (die) {
1816
- return die.ownerId ?? null;
1817
- }
1818
- const owner = table.ownerOfCard[componentId];
1819
- if (owner !== void 0) {
1820
- return owner ?? null;
1821
- }
1822
- return null;
1823
- }
1824
- function componentData(table, componentId) {
1825
- const piece = table.pieces[componentId];
1826
- if (piece) {
1827
- return piece.properties;
1828
- }
1829
- const die = table.dice[componentId];
1830
- if (die) {
1831
- return die.properties;
1832
- }
1833
- const card = table.cards[componentId];
1834
- if (card) {
1835
- return card.properties;
1836
- }
1837
- return void 0;
1804
+ function syncSharedZoneWithDeck(table, zoneId, nextCards) {
1805
+ table.decks[zoneId] = [...nextCards];
1806
+ table.zones.shared[zoneId] = [
1807
+ ...nextCards
1808
+ ];
1838
1809
  }
1839
- function assertCardAllowedInZone(table, zoneId, componentId) {
1840
- const card = table.cards[componentId];
1841
- if (!card) {
1842
- return;
1843
- }
1844
- const allowedCardSetIds = allowedCardSetIdsForZone(table, zoneId);
1845
- if (allowedCardSetIds.length > 0 && !allowedCardSetIds.includes(card.cardSetId)) {
1846
- throw new Error(
1847
- `Card '${componentId}' from card set '${card.cardSetId}' cannot enter zone '${zoneId}'.`
1848
- );
1849
- }
1810
+ function syncPlayerZoneWithHand(table, zoneId, playerId, nextCards) {
1811
+ table.hands[zoneId] = ppWrite(table.hands[zoneId], playerId, [
1812
+ ...nextCards
1813
+ ]);
1814
+ table.zones.perPlayer[zoneId] = ppWrite(
1815
+ table.zones.perPlayer[zoneId],
1816
+ playerId,
1817
+ [...nextCards]
1818
+ );
1850
1819
  }
1820
+
1821
+ // src/reducer/table/clone.ts
1851
1822
  function cloneRuntimeBoardState(board) {
1852
1823
  if (board.layout !== "generic") {
1853
1824
  return {
@@ -1991,623 +1962,251 @@ function cloneRuntimeTable(table) {
1991
1962
  )
1992
1963
  };
1993
1964
  }
1994
- function syncSharedZoneWithDeck(table, zoneId, nextCards) {
1995
- table.decks[zoneId] = [...nextCards];
1996
- table.zones.shared[zoneId] = [
1997
- ...nextCards
1998
- ];
1965
+
1966
+ // src/reducer/table/board-queries.ts
1967
+ function getBoard(table, boardId) {
1968
+ return table.boards.byId[boardId];
1999
1969
  }
2000
- function shufflePlayerZoneCards(table, zoneId, playerId, nextCards) {
2001
- if (nextCards === void 0) {
2002
- const fromZone = ppRead(
2003
- table.zones.perPlayer[zoneId] ?? table.hands[zoneId],
2004
- playerId
2005
- );
2006
- return [...ensureArray(fromZone)];
1970
+ function getHexBoard(table, boardId) {
1971
+ return getBoard(table, boardId);
1972
+ }
1973
+ function getTiledBoard(table, boardId) {
1974
+ return getBoard(table, boardId);
1975
+ }
1976
+ function getSquareBoard(table, boardId) {
1977
+ return getBoard(table, boardId);
1978
+ }
1979
+ function getSpace(table, boardId, spaceId) {
1980
+ return getBoard(table, boardId).spaces[spaceId];
1981
+ }
1982
+ function getHexSpace(table, boardId, spaceId) {
1983
+ return getHexBoard(table, boardId).spaces[spaceId];
1984
+ }
1985
+ function getSquareSpace(table, boardId, spaceId) {
1986
+ return getSquareBoard(table, boardId).spaces[spaceId];
1987
+ }
1988
+ function getContainer(table, boardId, containerId) {
1989
+ return getBoard(table, boardId).containers[containerId];
1990
+ }
1991
+ function getEdge(table, boardId, edgeId) {
1992
+ const edge = getTiledBoard(table, boardId).edges.find(
1993
+ (candidate) => candidate.id === edgeId
1994
+ );
1995
+ if (!edge) {
1996
+ throw new Error(`Unknown edge '${edgeId}' on board '${boardId}'.`);
2007
1997
  }
2008
- table.hands[zoneId] = ppWrite(table.hands[zoneId], playerId, [
2009
- ...nextCards
2010
- ]);
2011
- table.zones.perPlayer[zoneId] = ppWrite(
2012
- table.zones.perPlayer[zoneId],
2013
- playerId,
2014
- [...nextCards]
1998
+ return edge;
1999
+ }
2000
+ function getVertex(table, boardId, vertexId) {
2001
+ const vertex = getTiledBoard(table, boardId).vertices.find(
2002
+ (candidate) => candidate.id === vertexId
2015
2003
  );
2016
- return [...nextCards];
2004
+ if (!vertex) {
2005
+ throw new Error(`Unknown vertex '${vertexId}' on board '${boardId}'.`);
2006
+ }
2007
+ return vertex;
2017
2008
  }
2018
- function syncPlayerZoneWithHand(table, zoneId, playerId, nextCards) {
2019
- table.hands[zoneId] = ppWrite(table.hands[zoneId], playerId, [
2020
- ...nextCards
2021
- ]);
2022
- table.zones.perPlayer[zoneId] = ppWrite(
2023
- table.zones.perPlayer[zoneId],
2024
- playerId,
2025
- [...nextCards]
2009
+ function getHexSpaceAt(table, boardId, q, r) {
2010
+ return Object.values(getHexBoard(table, boardId).spaces).find(
2011
+ (space) => space.q === q && space.r === r
2026
2012
  );
2027
2013
  }
2028
- function reindexSpaceOccupants(table, boardId, spaceId) {
2029
- getComponentsOnSpace(table, boardId, spaceId).forEach(
2030
- (componentId, index) => {
2031
- const location = table.componentLocations[componentId];
2032
- if (location?.type === "OnSpace") {
2033
- table.componentLocations[componentId] = {
2034
- ...location,
2035
- position: index
2036
- };
2037
- }
2038
- }
2014
+ function getSquareSpaceAt(table, boardId, row, col) {
2015
+ return Object.values(getSquareBoard(table, boardId).spaces).find(
2016
+ (space) => space.row === row && space.col === col
2039
2017
  );
2040
2018
  }
2041
- function reindexContainerOccupants(table, boardId, containerId) {
2042
- getComponentsInContainer(table, boardId, containerId).forEach(
2043
- (componentId, index) => {
2044
- const location = table.componentLocations[componentId];
2045
- if (location?.type === "InContainer") {
2046
- table.componentLocations[componentId] = {
2047
- ...location,
2048
- position: index
2049
- };
2050
- }
2051
- }
2019
+ function getSpaceEdges(table, boardId, spaceId) {
2020
+ return getTiledBoard(table, boardId).edges.filter((edge) => edge.spaceIds.includes(spaceId)).map((edge) => edge.id);
2021
+ }
2022
+ function getSpaceVertices(table, boardId, spaceId) {
2023
+ return getTiledBoard(table, boardId).vertices.filter((vertex) => vertex.spaceIds.includes(spaceId)).map((vertex) => vertex.id);
2024
+ }
2025
+ function getIncidentEdges(table, boardId, vertexId) {
2026
+ const tiledBoard = getTiledBoard(table, boardId);
2027
+ const vertex = tiledBoard.vertices.find(
2028
+ (candidate) => candidate.id === vertexId
2052
2029
  );
2030
+ if (!vertex) {
2031
+ throw new Error(`Unknown vertex '${vertexId}' on board '${boardId}'.`);
2032
+ }
2033
+ const vertexSpaceIds = new Set(vertex.spaceIds);
2034
+ return tiledBoard.edges.filter(
2035
+ (edge) => edge.spaceIds.every((spaceId) => vertexSpaceIds.has(spaceId))
2036
+ ).map((edge) => edge.id);
2053
2037
  }
2054
- function reindexEdgeOccupants(table, boardId, edgeId) {
2055
- orderedComponentIdsForLocation(
2056
- table,
2057
- (location) => location.type === "OnEdge" && location.boardId === boardId && location.edgeId === edgeId
2058
- ).forEach((componentId, index) => {
2059
- const location = table.componentLocations[componentId];
2060
- if (location?.type === "OnEdge") {
2061
- table.componentLocations[componentId] = {
2062
- ...location,
2063
- position: index
2064
- };
2065
- }
2066
- });
2038
+ function getIncidentVertices(table, boardId, edgeId) {
2039
+ const tiledBoard = getTiledBoard(table, boardId);
2040
+ const edge = tiledBoard.edges.find((candidate) => candidate.id === edgeId);
2041
+ if (!edge) {
2042
+ throw new Error(`Unknown edge '${edgeId}' on board '${boardId}'.`);
2043
+ }
2044
+ const edgeSpaceIds = new Set(edge.spaceIds);
2045
+ return tiledBoard.vertices.filter(
2046
+ (vertex) => Array.from(edgeSpaceIds).every(
2047
+ (spaceId) => vertex.spaceIds.includes(spaceId)
2048
+ )
2049
+ ).map((vertex) => vertex.id);
2067
2050
  }
2068
- function reindexVertexOccupants(table, boardId, vertexId) {
2069
- orderedComponentIdsForLocation(
2070
- table,
2071
- (location) => location.type === "OnVertex" && location.boardId === boardId && location.vertexId === vertexId
2072
- ).forEach((componentId, index) => {
2073
- const location = table.componentLocations[componentId];
2074
- if (location?.type === "OnVertex") {
2075
- table.componentLocations[componentId] = {
2076
- ...location,
2077
- position: index
2078
- };
2051
+ function getRelatedSpaces(table, boardId, spaceId, relationTypeId) {
2052
+ const board = getBoard(table, boardId);
2053
+ const related = /* @__PURE__ */ new Set();
2054
+ for (const relation of board.relations) {
2055
+ if (relation.typeId !== relationTypeId) {
2056
+ continue;
2079
2057
  }
2080
- });
2058
+ if (relation.fromSpaceId === spaceId) {
2059
+ related.add(relation.toSpaceId);
2060
+ continue;
2061
+ }
2062
+ if (!relation.directed && relation.toSpaceId === spaceId) {
2063
+ related.add(relation.fromSpaceId);
2064
+ }
2065
+ }
2066
+ return [...related];
2081
2067
  }
2082
- function reindexSlotOccupants(table, host, slotId) {
2083
- orderedComponentIdsForLocation(
2068
+ function getAdjacentSpaces(table, boardId, spaceId) {
2069
+ return getRelatedSpaces(
2084
2070
  table,
2085
- (location) => location.type === "InSlot" && location.host.kind === host.kind && location.host.id === host.id && location.slotId === slotId
2086
- ).forEach((componentId, index) => {
2087
- const location = table.componentLocations[componentId];
2088
- if (location?.type === "InSlot") {
2089
- table.componentLocations[componentId] = {
2090
- ...location,
2091
- position: index
2092
- };
2093
- }
2094
- });
2071
+ boardId,
2072
+ spaceId,
2073
+ "adjacent"
2074
+ );
2095
2075
  }
2096
- function removeComponentFromCurrentLocation(table, componentId) {
2097
- const currentLocation = table.componentLocations[componentId];
2098
- if (!currentLocation) {
2099
- return;
2076
+ function getSpaceDistance(table, boardId, fromSpaceId, toSpaceId) {
2077
+ if (fromSpaceId === toSpaceId) {
2078
+ return 0;
2100
2079
  }
2101
- if (currentLocation.type === "InDeck") {
2102
- const nextCards = ensureArray(table.decks[currentLocation.deckId]).filter(
2103
- (candidate) => candidate !== componentId
2104
- );
2105
- syncSharedZoneWithDeck(
2106
- table,
2107
- currentLocation.deckId,
2108
- nextCards
2109
- );
2110
- nextCards.forEach((cardId, index) => {
2111
- const location = table.componentLocations[cardId];
2112
- if (location?.type === "InDeck") {
2113
- table.componentLocations[cardId] = {
2114
- ...location,
2115
- position: index
2116
- };
2117
- }
2118
- });
2119
- return;
2120
- }
2121
- if (currentLocation.type === "InHand") {
2122
- const nextCards = ensureArray(
2123
- ppRead(table.hands[currentLocation.handId], currentLocation.playerId)
2124
- ).filter((candidate) => candidate !== componentId);
2125
- syncPlayerZoneWithHand(
2126
- table,
2127
- currentLocation.handId,
2128
- currentLocation.playerId,
2129
- nextCards
2130
- );
2131
- nextCards.forEach((cardId, index) => {
2132
- const location = table.componentLocations[cardId];
2133
- if (location?.type === "InHand") {
2134
- table.componentLocations[cardId] = {
2135
- ...location,
2136
- position: index
2137
- };
2138
- }
2139
- });
2140
- return;
2141
- }
2142
- if (currentLocation.type === "OnSpace") {
2143
- delete table.componentLocations[componentId];
2144
- reindexSpaceOccupants(
2145
- table,
2146
- currentLocation.boardId,
2147
- currentLocation.spaceId
2148
- );
2149
- return;
2150
- }
2151
- if (currentLocation.type === "InZone") {
2152
- if (currentLocation.zoneId in table.zones.shared) {
2153
- const nextComponents = ensureArray(
2154
- table.zones.shared[currentLocation.zoneId]
2155
- ).filter((candidate) => candidate !== componentId);
2156
- syncSharedZoneWithDeck(
2157
- table,
2158
- currentLocation.zoneId,
2159
- nextComponents
2160
- );
2161
- nextComponents.forEach((currentComponentId, index) => {
2162
- const location = table.componentLocations[currentComponentId];
2163
- if (location?.type === "InZone") {
2164
- table.componentLocations[currentComponentId] = {
2165
- ...location,
2166
- position: index
2167
- };
2168
- }
2169
- });
2170
- }
2171
- delete table.componentLocations[componentId];
2172
- return;
2173
- }
2174
- if (currentLocation.type === "InContainer") {
2175
- delete table.componentLocations[componentId];
2176
- reindexContainerOccupants(
2177
- table,
2178
- currentLocation.boardId,
2179
- currentLocation.containerId
2180
- );
2181
- return;
2182
- }
2183
- if (currentLocation.type === "OnEdge") {
2184
- delete table.componentLocations[componentId];
2185
- reindexEdgeOccupants(
2186
- table,
2187
- currentLocation.boardId,
2188
- currentLocation.edgeId
2189
- );
2190
- return;
2191
- }
2192
- if (currentLocation.type === "OnVertex") {
2193
- delete table.componentLocations[componentId];
2194
- reindexVertexOccupants(
2195
- table,
2196
- currentLocation.boardId,
2197
- currentLocation.vertexId
2198
- );
2199
- return;
2200
- }
2201
- if (currentLocation.type === "InSlot") {
2202
- delete table.componentLocations[componentId];
2203
- reindexSlotOccupants(table, currentLocation.host, currentLocation.slotId);
2204
- return;
2205
- }
2206
- delete table.componentLocations[componentId];
2207
- }
2208
- function appendToDeck(table, deckId, cardId, playedBy = null, position = "bottom") {
2209
- const nextTable = cloneRuntimeTable(table);
2210
- assertZoneScope(
2211
- nextTable,
2212
- deckId,
2213
- "shared",
2214
- "addCardToSharedZone",
2215
- "zoneId"
2216
- );
2217
- const deckCards = [
2218
- ...ensureArray(nextTable.decks[deckId])
2219
- ];
2220
- assertCardAllowedInZone(nextTable, deckId, cardId);
2221
- if (position === "top") {
2222
- deckCards.unshift(cardId);
2223
- } else {
2224
- deckCards.push(cardId);
2225
- }
2226
- syncSharedZoneWithDeck(nextTable, deckId, deckCards);
2227
- for (const [index, currentCardId] of deckCards.entries()) {
2228
- if (currentCardId === cardId) {
2229
- nextTable.componentLocations[currentCardId] = {
2230
- type: "InDeck",
2231
- deckId,
2232
- playedBy,
2233
- position: index
2234
- };
2235
- continue;
2236
- }
2237
- const existing = nextTable.componentLocations[currentCardId];
2238
- nextTable.componentLocations[currentCardId] = {
2239
- type: "InDeck",
2240
- deckId,
2241
- playedBy: existing?.type === "InDeck" ? existing.playedBy : null,
2242
- position: index
2243
- };
2244
- }
2245
- nextTable.ownerOfCard[cardId] = playedBy;
2246
- nextTable.visibility[cardId] = {
2247
- faceUp: true
2248
- };
2249
- return nextTable;
2250
- }
2251
- function removeFromDeck(table, deckId, cardId) {
2252
- const nextTable = cloneRuntimeTable(table);
2253
- assertZoneScope(
2254
- nextTable,
2255
- deckId,
2256
- "shared",
2257
- "removeCardFromSharedZone",
2258
- "zoneId"
2259
- );
2260
- const remaining = ensureArray(nextTable.decks[deckId]).filter(
2261
- (candidate) => candidate !== cardId
2262
- );
2263
- syncSharedZoneWithDeck(nextTable, deckId, remaining);
2264
- for (const [index, currentCardId] of remaining.entries()) {
2265
- const currentLocation = nextTable.componentLocations[currentCardId];
2266
- if (currentLocation?.type === "InDeck") {
2267
- nextTable.componentLocations[currentCardId] = {
2268
- ...currentLocation,
2269
- position: index
2270
- };
2271
- }
2272
- }
2273
- return nextTable;
2274
- }
2275
- function computeVisibilityForPlayerZone(table, zoneId, playerId) {
2276
- const mode = table.handVisibility[zoneId];
2277
- if (mode === "all" || mode === "public") {
2278
- return { faceUp: true };
2279
- }
2280
- return { faceUp: false, visibleTo: [playerId] };
2281
- }
2282
- function moveFromHandToDeck(options) {
2283
- const nextTable = cloneRuntimeTable(options.table);
2284
- assertZoneScope(
2285
- nextTable,
2286
- options.handId,
2287
- "perPlayer",
2288
- "moveCardFromPlayerZoneToSharedZone",
2289
- "fromZoneId"
2290
- );
2291
- assertZoneScope(
2292
- nextTable,
2293
- options.deckId,
2294
- "shared",
2295
- "moveCardFromPlayerZoneToSharedZone",
2296
- "toZoneId"
2297
- );
2298
- const currentHand = ensureArray(
2299
- ppRead(nextTable.hands[options.handId], options.playerId)
2300
- ).filter((candidate) => candidate !== options.cardId);
2301
- syncPlayerZoneWithHand(
2302
- nextTable,
2303
- options.handId,
2304
- options.playerId,
2305
- currentHand
2306
- );
2307
- for (const [index, currentCardId] of currentHand.entries()) {
2308
- nextTable.componentLocations[currentCardId] = {
2309
- type: "InHand",
2310
- handId: options.handId,
2311
- playerId: options.playerId,
2312
- position: index
2313
- };
2314
- }
2315
- nextTable.ownerOfCard[options.cardId] = options.playedBy ?? options.playerId;
2316
- nextTable.visibility[options.cardId] = {
2317
- faceUp: true
2318
- };
2319
- return appendToDeck(
2320
- nextTable,
2321
- options.deckId,
2322
- options.cardId,
2323
- options.playedBy ?? options.playerId,
2324
- options.position ?? "bottom"
2325
- );
2326
- }
2327
- function setActivePlayers(state, activePlayers) {
2328
- return {
2329
- ...state,
2330
- flow: {
2331
- ...state.flow,
2332
- activePlayers
2333
- }
2334
- };
2335
- }
2336
- function setPhaseState(state, phaseState) {
2337
- return {
2338
- ...state,
2339
- phase: phaseState
2340
- };
2341
- }
2342
- function getSharedZoneCards(table, zoneId) {
2343
- assertZoneScope(
2344
- table,
2345
- zoneId,
2346
- "shared",
2347
- "getSharedZoneCards",
2348
- "zoneId"
2349
- );
2350
- return [
2351
- ...ensureArray(table.zones.shared[zoneId] ?? table.decks[zoneId])
2352
- ];
2353
- }
2354
- function getPlayerZoneCards(table, playerId, zoneId) {
2355
- assertZoneScope(
2356
- table,
2357
- zoneId,
2358
- "perPlayer",
2359
- "getPlayerZoneCards",
2360
- "zoneId"
2361
- );
2362
- const cards = ppRead(table.zones.perPlayer[zoneId], playerId) ?? ppRead(table.hands[zoneId], playerId);
2363
- return [
2364
- ...ensureArray(cards)
2365
- ];
2366
- }
2367
- function collectZoneIds(...sources) {
2368
- const seen = /* @__PURE__ */ new Set();
2369
- for (const source of sources) {
2370
- if (!source) continue;
2371
- for (const key of Object.keys(source)) {
2372
- seen.add(key);
2080
+ const visited = /* @__PURE__ */ new Set([fromSpaceId]);
2081
+ let frontier = [fromSpaceId];
2082
+ let distance = 0;
2083
+ while (frontier.length > 0) {
2084
+ distance += 1;
2085
+ const nextFrontier = [];
2086
+ for (const currentSpaceId of frontier) {
2087
+ for (const neighborId of getAdjacentSpaces(
2088
+ table,
2089
+ boardId,
2090
+ currentSpaceId
2091
+ )) {
2092
+ if (neighborId === toSpaceId) {
2093
+ return distance;
2094
+ }
2095
+ if (!visited.has(neighborId)) {
2096
+ visited.add(neighborId);
2097
+ nextFrontier.push(neighborId);
2098
+ }
2099
+ }
2373
2100
  }
2101
+ frontier = nextFrontier;
2374
2102
  }
2375
- return [...seen];
2103
+ return Number.POSITIVE_INFINITY;
2376
2104
  }
2377
- function getAllSharedZoneCards(table) {
2378
- const zoneIds = collectZoneIds(
2379
- table.zones.shared,
2380
- table.decks
2381
- );
2382
- const result = {};
2383
- for (const zoneId of zoneIds) {
2384
- result[zoneId] = getSharedZoneCards(
2385
- table,
2386
- zoneId
2387
- );
2388
- }
2389
- return result;
2105
+ function getSquareNeighbors(table, boardId, spaceId, options = {}) {
2106
+ const board = getSquareBoard(table, boardId);
2107
+ const space = getSquareSpace(table, boardId, spaceId);
2108
+ const offsets = options.mode === "diagonal" ? [
2109
+ [-1, -1],
2110
+ [-1, 1],
2111
+ [1, -1],
2112
+ [1, 1]
2113
+ ] : options.mode === "all" ? [
2114
+ [-1, 0],
2115
+ [0, 1],
2116
+ [1, 0],
2117
+ [0, -1],
2118
+ [-1, -1],
2119
+ [-1, 1],
2120
+ [1, -1],
2121
+ [1, 1]
2122
+ ] : [
2123
+ [-1, 0],
2124
+ [0, 1],
2125
+ [1, 0],
2126
+ [0, -1]
2127
+ ];
2128
+ return offsets.map(
2129
+ ([rowOffset, colOffset]) => Object.values(board.spaces).find(
2130
+ (candidate) => candidate.row === space.row + rowOffset && candidate.col === space.col + colOffset
2131
+ )
2132
+ ).filter((candidate) => candidate !== void 0).map((candidate) => candidate.id);
2390
2133
  }
2391
- function getAllPlayerZoneCards(table, zoneId) {
2392
- const result = {};
2393
- for (const playerId of table.playerOrder) {
2394
- result[playerId] = getPlayerZoneCards(
2395
- table,
2396
- playerId,
2397
- zoneId
2398
- );
2399
- }
2400
- return result;
2134
+ function getSquareDistance(table, boardId, fromSpaceId, toSpaceId, options = {}) {
2135
+ const from = getSquareSpace(table, boardId, fromSpaceId);
2136
+ const to = getSquareSpace(table, boardId, toSpaceId);
2137
+ const rowDistance = Math.abs(from.row - to.row);
2138
+ const colDistance = Math.abs(from.col - to.col);
2139
+ return options.metric === "chebyshev" ? Math.max(rowDistance, colDistance) : rowDistance + colDistance;
2401
2140
  }
2402
- function getCard(table, cardId) {
2403
- const card = table.cards[cardId];
2404
- return {
2405
- id: card.id,
2406
- cardType: card.cardType,
2407
- name: card.name,
2408
- text: card.text,
2409
- properties: card.properties
2410
- };
2141
+ function getBoardsByTypeId(table, typeId) {
2142
+ return Object.entries(table.boards.byId).filter(([, board]) => board.typeId === typeId).map(([boardId]) => boardId);
2411
2143
  }
2412
- function getCardsById(table, cardIds) {
2413
- return Object.fromEntries(
2414
- cardIds.map((cardId) => [
2415
- cardId,
2416
- table.cards[cardId] ? getCard(table, cardId) : void 0
2417
- ])
2418
- );
2144
+ function getSpacesByTypeId(table, boardId, typeId) {
2145
+ return Object.entries(getBoard(table, boardId).spaces).filter(([, space]) => space.typeId === typeId).map(([spaceId]) => spaceId);
2419
2146
  }
2420
- function getSharedZoneCardCollection(table, zoneId) {
2421
- const cardIds = getSharedZoneCards(table, zoneId);
2422
- return {
2423
- cardIds,
2424
- cardsById: getCardsById(table, cardIds)
2425
- };
2147
+ function getEdgesByTypeId(table, boardId, typeId) {
2148
+ return getTiledBoard(table, boardId).edges.filter((edge) => edge.typeId === typeId).map((edge) => edge.id);
2426
2149
  }
2427
- function getPlayerZoneCardCollection(table, playerId, zoneId) {
2428
- const cardIds = getPlayerZoneCards(table, playerId, zoneId);
2429
- return {
2430
- cardIds,
2431
- cardsById: getCardsById(table, cardIds)
2432
- };
2150
+ function getVerticesByTypeId(table, boardId, typeId) {
2151
+ return getTiledBoard(table, boardId).vertices.filter((vertex) => vertex.typeId === typeId).map((vertex) => vertex.id);
2433
2152
  }
2434
- function getSlotOccupants(table, host, slotId) {
2153
+ function getComponentsOnSpace(table, boardId, spaceId) {
2154
+ const zoneId = getSpace(table, boardId, spaceId).zoneId;
2435
2155
  return orderedComponentIdsForLocation(
2436
2156
  table,
2437
- (location) => matchesSlotHost(location, host, slotId)
2438
- ).map((componentId) => ({
2439
- pieceId: componentId,
2440
- playerId: componentPlayerId(
2441
- table,
2442
- componentId
2443
- ),
2444
- slotId,
2445
- data: componentData(table, componentId)
2446
- }));
2157
+ (location) => location.type === "OnSpace" && location.boardId === boardId && location.spaceId === spaceId || location.type === "InZone" && typeof zoneId === "string" && zoneId.length > 0 && location.zoneId === zoneId
2158
+ );
2447
2159
  }
2448
- function getSlotOccupantsByHost(table, host) {
2449
- const occupantsBySlot = {};
2450
- orderedComponentIdsForLocation(
2160
+ function getComponentsInContainer(table, boardId, containerId) {
2161
+ const zoneId = getContainer(table, boardId, containerId).zoneId;
2162
+ return orderedComponentIdsForLocation(
2451
2163
  table,
2452
- (location) => matchesSlotHost(location, host)
2453
- ).forEach((componentId) => {
2454
- const location = table.componentLocations[componentId];
2455
- if (!location || !matchesSlotHost(location, host)) {
2456
- return;
2457
- }
2458
- const slotOccupant = {
2459
- pieceId: componentId,
2460
- playerId: componentPlayerId(
2461
- table,
2462
- componentId
2463
- ),
2464
- slotId: location.slotId,
2465
- data: componentData(table, componentId)
2466
- };
2467
- (occupantsBySlot[location.slotId] ??= []).push(slotOccupant);
2468
- });
2469
- return occupantsBySlot;
2470
- }
2471
- function getCardOwner(table, cardId) {
2472
- return table.ownerOfCard[cardId];
2473
- }
2474
- function getCardVisibility(table, cardId) {
2475
- return table.visibility[cardId];
2476
- }
2477
- function getPlayerOrder(table) {
2478
- return table.playerOrder;
2479
- }
2480
- function getPlayerResources(table, playerId) {
2481
- return perPlayerGet(
2482
- table.resources,
2483
- playerId
2164
+ (location) => location.type === "InContainer" && location.boardId === boardId && location.containerId === containerId || location.type === "InZone" && typeof zoneId === "string" && zoneId.length > 0 && location.zoneId === zoneId
2484
2165
  );
2485
2166
  }
2486
- function getPlayerResourceAmount(table, playerId, resourceId) {
2487
- const playerResources = perPlayerGet(
2488
- table.resources,
2489
- playerId
2167
+ function getComponentsOnEdge(table, boardId, edgeId) {
2168
+ return orderedComponentIdsForLocation(
2169
+ table,
2170
+ (location) => location.type === "OnEdge" && location.boardId === boardId && location.edgeId === edgeId
2490
2171
  );
2491
- if (!playerResources) return 0;
2492
- const value = playerResources[resourceId];
2493
- return typeof value === "number" ? value : 0;
2494
2172
  }
2495
- function getPlayerResourceTotal(table, playerId) {
2496
- const playerResources = perPlayerGet(
2497
- table.resources,
2498
- playerId
2173
+ function getComponentsOnVertex(table, boardId, vertexId) {
2174
+ return orderedComponentIdsForLocation(
2175
+ table,
2176
+ (location) => location.type === "OnVertex" && location.boardId === boardId && location.vertexId === vertexId
2499
2177
  );
2500
- if (!playerResources) return 0;
2501
- let total = 0;
2502
- for (const key of Object.keys(playerResources)) {
2503
- const value = playerResources[key];
2504
- if (typeof value === "number") total += value;
2505
- }
2506
- return total;
2507
- }
2508
- function getNextPlayerInOrder(table, playerId) {
2509
- const order = table.playerOrder;
2510
- if (order.length === 0) return null;
2511
- const idx = order.indexOf(playerId);
2512
- if (idx < 0) return null;
2513
- return order[(idx + 1) % order.length] ?? null;
2514
- }
2515
- function forEachResourceEntry(amounts, visit) {
2516
- for (const resourceId of Object.keys(amounts)) {
2517
- const amount = amounts[resourceId];
2518
- if (typeof amount !== "number" || amount === 0) continue;
2519
- visit(resourceId, amount);
2520
- }
2521
- }
2522
- function canAffordResources(table, playerId, amounts) {
2523
- for (const resourceId of Object.keys(amounts)) {
2524
- const required = amounts[resourceId];
2525
- if (typeof required !== "number" || required <= 0) continue;
2526
- if (getPlayerResourceAmount(table, playerId, resourceId) < required) {
2527
- return false;
2528
- }
2529
- }
2530
- return true;
2531
- }
2532
- function getMissingResources(table, playerId, amounts) {
2533
- const missing = {};
2534
- for (const resourceId of Object.keys(amounts)) {
2535
- const required = amounts[resourceId];
2536
- if (typeof required !== "number" || required <= 0) continue;
2537
- const have = getPlayerResourceAmount(table, playerId, resourceId);
2538
- if (have < required) missing[resourceId] = required - have;
2539
- }
2540
- return missing;
2541
- }
2542
- function withPlayerResources(table, playerId, nextForPlayer) {
2543
- return {
2544
- ...table,
2545
- resources: perPlayerSet(
2546
- table.resources,
2547
- playerId,
2548
- nextForPlayer
2549
- )
2550
- };
2551
2178
  }
2552
- function addPlayerResources(table, playerId, amounts) {
2553
- const prev = perPlayerGet(
2554
- table.resources,
2555
- playerId
2556
- ) ?? {};
2557
- const next = { ...prev };
2558
- forEachResourceEntry(amounts, (resourceId, amount) => {
2559
- if (amount < 0) {
2560
- throw new Error(
2561
- `addPlayerResources: negative amount for resource '${resourceId}'. Use spendPlayerResources or transferPlayerResources instead.`
2562
- );
2563
- }
2564
- next[resourceId] = (next[resourceId] ?? 0) + amount;
2565
- });
2566
- return withPlayerResources(table, playerId, next);
2179
+
2180
+ // src/reducer/table/card-validation.ts
2181
+ function allowedCardSetIdsForZone(table, zoneId) {
2182
+ return table.zones.cardSetIdsByZoneId?.[zoneId] ?? [];
2567
2183
  }
2568
- function spendPlayerResources(table, playerId, amounts) {
2569
- if (!canAffordResources(table, playerId, amounts)) {
2570
- const missing = getMissingResources(table, playerId, amounts);
2184
+ function assertCardAllowedInZone(table, zoneId, componentId) {
2185
+ const card = table.cards[componentId];
2186
+ if (!card) {
2187
+ return;
2188
+ }
2189
+ const allowedCardSetIds = allowedCardSetIdsForZone(table, zoneId);
2190
+ if (allowedCardSetIds.length > 0 && !allowedCardSetIds.includes(card.cardSetId)) {
2571
2191
  throw new Error(
2572
- `spendPlayerResources: player '${playerId}' cannot afford ${JSON.stringify(
2573
- missing
2574
- )}. Check canAfford in your validate step first.`
2192
+ `Card '${componentId}' from card set '${card.cardSetId}' cannot enter zone '${zoneId}'.`
2575
2193
  );
2576
2194
  }
2577
- const prev = perPlayerGet(
2578
- table.resources,
2579
- playerId
2580
- ) ?? {};
2581
- const next = { ...prev };
2582
- forEachResourceEntry(amounts, (resourceId, amount) => {
2583
- if (amount < 0) {
2584
- throw new Error(
2585
- `spendPlayerResources: negative amount for resource '${resourceId}'. Pass positive amounts \u2014 the op deducts them from the player.`
2586
- );
2587
- }
2588
- next[resourceId] = Math.max(0, (next[resourceId] ?? 0) - amount);
2589
- });
2590
- return withPlayerResources(table, playerId, next);
2591
- }
2592
- function transferPlayerResources(table, fromPlayerId, toPlayerId, amounts) {
2593
- const afterSpend = spendPlayerResources(table, fromPlayerId, amounts);
2594
- return addPlayerResources(afterSpend, toPlayerId, amounts);
2595
2195
  }
2596
- function setPlayerResource(table, playerId, resourceId, amount) {
2597
- if (!Number.isFinite(amount) || amount < 0) {
2196
+ function assertCardAllowedInContainer(table, boardId, containerId, componentId) {
2197
+ const card = table.cards[componentId];
2198
+ if (!card) {
2199
+ return;
2200
+ }
2201
+ const allowedCardSetIds = getContainer(table, boardId, containerId).allowedCardSetIds ?? [];
2202
+ if (allowedCardSetIds.length > 0 && !allowedCardSetIds.includes(card.cardSetId)) {
2598
2203
  throw new Error(
2599
- `setPlayerResource: amount must be a non-negative finite number, got ${amount}.`
2204
+ `Card '${componentId}' from card set '${card.cardSetId}' cannot enter container '${containerId}' on board '${boardId}'.`
2600
2205
  );
2601
2206
  }
2602
- const prev = perPlayerGet(
2603
- table.resources,
2604
- playerId
2605
- ) ?? {};
2606
- return withPlayerResources(table, playerId, {
2607
- ...prev,
2608
- [resourceId]: amount
2609
- });
2610
2207
  }
2208
+
2209
+ // src/reducer/table/component-locations.ts
2611
2210
  function getComponentLocation(table, componentId) {
2612
2211
  return table.componentLocations[componentId];
2613
2212
  }
@@ -2726,229 +2325,493 @@ function getComponentSlotLocation(table, componentId) {
2726
2325
  location
2727
2326
  };
2728
2327
  }
2729
- function getBoard(table, boardId) {
2730
- return table.boards.byId[boardId];
2731
- }
2732
- function getHexBoard(table, boardId) {
2733
- return getBoard(table, boardId);
2734
- }
2735
- function getTiledBoard(table, boardId) {
2736
- return getBoard(table, boardId);
2737
- }
2738
- function getSquareBoard(table, boardId) {
2739
- return getBoard(table, boardId);
2328
+
2329
+ // src/reducer/table/zone-queries.ts
2330
+ function matchesSlotHost(location, host, slotId) {
2331
+ return location.type === "InSlot" && location.host.kind === host.kind && location.host.id === host.id && (slotId === void 0 || location.slotId === slotId);
2740
2332
  }
2741
- function getSpace(table, boardId, spaceId) {
2742
- return getBoard(table, boardId).spaces[spaceId];
2333
+ function componentPlayerId(table, componentId) {
2334
+ const piece = table.pieces[componentId];
2335
+ if (piece) {
2336
+ return piece.ownerId ?? null;
2337
+ }
2338
+ const die = table.dice[componentId];
2339
+ if (die) {
2340
+ return die.ownerId ?? null;
2341
+ }
2342
+ const owner = table.ownerOfCard[componentId];
2343
+ if (owner !== void 0) {
2344
+ return owner ?? null;
2345
+ }
2346
+ return null;
2743
2347
  }
2744
- function getHexSpace(table, boardId, spaceId) {
2745
- return getHexBoard(table, boardId).spaces[spaceId];
2348
+ function componentData(table, componentId) {
2349
+ const piece = table.pieces[componentId];
2350
+ if (piece) {
2351
+ return piece.properties;
2352
+ }
2353
+ const die = table.dice[componentId];
2354
+ if (die) {
2355
+ return die.properties;
2356
+ }
2357
+ const card = table.cards[componentId];
2358
+ if (card) {
2359
+ return card.properties;
2360
+ }
2361
+ return void 0;
2746
2362
  }
2747
- function getSquareSpace(table, boardId, spaceId) {
2748
- return getSquareBoard(table, boardId).spaces[spaceId];
2363
+ function getSharedZoneCards(table, zoneId) {
2364
+ assertZoneScope(
2365
+ table,
2366
+ zoneId,
2367
+ "shared",
2368
+ "getSharedZoneCards",
2369
+ "zoneId"
2370
+ );
2371
+ return [
2372
+ ...ensureArray(table.zones.shared[zoneId] ?? table.decks[zoneId])
2373
+ ];
2749
2374
  }
2750
- function getContainer(table, boardId, containerId) {
2751
- return getBoard(table, boardId).containers[containerId];
2375
+ function getPlayerZoneCards(table, playerId, zoneId) {
2376
+ assertZoneScope(
2377
+ table,
2378
+ zoneId,
2379
+ "perPlayer",
2380
+ "getPlayerZoneCards",
2381
+ "zoneId"
2382
+ );
2383
+ const cards = ppRead(table.zones.perPlayer[zoneId], playerId) ?? ppRead(table.hands[zoneId], playerId);
2384
+ return [
2385
+ ...ensureArray(cards)
2386
+ ];
2752
2387
  }
2753
- function assertCardAllowedInContainer(table, boardId, containerId, componentId) {
2754
- const card = table.cards[componentId];
2755
- if (!card) {
2756
- return;
2757
- }
2758
- const allowedCardSetIds = getContainer(table, boardId, containerId).allowedCardSetIds ?? [];
2759
- if (allowedCardSetIds.length > 0 && !allowedCardSetIds.includes(card.cardSetId)) {
2760
- throw new Error(
2761
- `Card '${componentId}' from card set '${card.cardSetId}' cannot enter container '${containerId}' on board '${boardId}'.`
2762
- );
2388
+ function collectZoneIds(...sources) {
2389
+ const seen = /* @__PURE__ */ new Set();
2390
+ for (const source of sources) {
2391
+ if (!source) continue;
2392
+ for (const key of Object.keys(source)) {
2393
+ seen.add(key);
2394
+ }
2763
2395
  }
2396
+ return [...seen];
2764
2397
  }
2765
- function getEdge(table, boardId, edgeId) {
2766
- const edge = getTiledBoard(table, boardId).edges.find(
2767
- (candidate) => candidate.id === edgeId
2398
+ function getAllSharedZoneCards(table) {
2399
+ const zoneIds = collectZoneIds(
2400
+ table.zones.shared,
2401
+ table.decks
2768
2402
  );
2769
- if (!edge) {
2770
- throw new Error(`Unknown edge '${edgeId}' on board '${boardId}'.`);
2403
+ const result = {};
2404
+ for (const zoneId of zoneIds) {
2405
+ result[zoneId] = getSharedZoneCards(
2406
+ table,
2407
+ zoneId
2408
+ );
2771
2409
  }
2772
- return edge;
2410
+ return result;
2773
2411
  }
2774
- function getVertex(table, boardId, vertexId) {
2775
- const vertex = getTiledBoard(table, boardId).vertices.find(
2776
- (candidate) => candidate.id === vertexId
2777
- );
2778
- if (!vertex) {
2779
- throw new Error(`Unknown vertex '${vertexId}' on board '${boardId}'.`);
2412
+ function getAllPlayerZoneCards(table, zoneId) {
2413
+ const result = {};
2414
+ for (const playerId of table.playerOrder) {
2415
+ result[playerId] = getPlayerZoneCards(
2416
+ table,
2417
+ playerId,
2418
+ zoneId
2419
+ );
2780
2420
  }
2781
- return vertex;
2421
+ return result;
2782
2422
  }
2783
- function getHexSpaceAt(table, boardId, q, r) {
2784
- return Object.values(getHexBoard(table, boardId).spaces).find(
2785
- (space) => space.q === q && space.r === r
2423
+ function getCard(table, cardId) {
2424
+ const card = table.cards[cardId];
2425
+ return {
2426
+ id: card.id,
2427
+ cardType: card.cardType,
2428
+ name: card.name,
2429
+ text: card.text,
2430
+ properties: card.properties
2431
+ };
2432
+ }
2433
+ function getCardsById(table, cardIds) {
2434
+ return Object.fromEntries(
2435
+ cardIds.map((cardId) => [
2436
+ cardId,
2437
+ table.cards[cardId] ? getCard(table, cardId) : void 0
2438
+ ])
2786
2439
  );
2787
2440
  }
2788
- function getSquareSpaceAt(table, boardId, row, col) {
2789
- return Object.values(getSquareBoard(table, boardId).spaces).find(
2790
- (space) => space.row === row && space.col === col
2441
+ function getSharedZoneCardCollection(table, zoneId) {
2442
+ const cardIds = getSharedZoneCards(table, zoneId);
2443
+ return {
2444
+ cardIds,
2445
+ cardsById: getCardsById(table, cardIds)
2446
+ };
2447
+ }
2448
+ function getPlayerZoneCardCollection(table, playerId, zoneId) {
2449
+ const cardIds = getPlayerZoneCards(table, playerId, zoneId);
2450
+ return {
2451
+ cardIds,
2452
+ cardsById: getCardsById(table, cardIds)
2453
+ };
2454
+ }
2455
+ function getSlotOccupants(table, host, slotId) {
2456
+ return orderedComponentIdsForLocation(
2457
+ table,
2458
+ (location) => matchesSlotHost(location, host, slotId)
2459
+ ).map((componentId) => ({
2460
+ pieceId: componentId,
2461
+ playerId: componentPlayerId(
2462
+ table,
2463
+ componentId
2464
+ ),
2465
+ slotId,
2466
+ data: componentData(table, componentId)
2467
+ }));
2468
+ }
2469
+ function getSlotOccupantsByHost(table, host) {
2470
+ const occupantsBySlot = {};
2471
+ orderedComponentIdsForLocation(
2472
+ table,
2473
+ (location) => matchesSlotHost(location, host)
2474
+ ).forEach((componentId) => {
2475
+ const location = table.componentLocations[componentId];
2476
+ if (!location || !matchesSlotHost(location, host)) {
2477
+ return;
2478
+ }
2479
+ const slotOccupant = {
2480
+ pieceId: componentId,
2481
+ playerId: componentPlayerId(
2482
+ table,
2483
+ componentId
2484
+ ),
2485
+ slotId: location.slotId,
2486
+ data: componentData(table, componentId)
2487
+ };
2488
+ (occupantsBySlot[location.slotId] ??= []).push(slotOccupant);
2489
+ });
2490
+ return occupantsBySlot;
2491
+ }
2492
+ function getCardOwner(table, cardId) {
2493
+ return table.ownerOfCard[cardId];
2494
+ }
2495
+ function getCardVisibility(table, cardId) {
2496
+ return table.visibility[cardId];
2497
+ }
2498
+
2499
+ // src/reducer/table/resource-ops.ts
2500
+ function getPlayerOrder(table) {
2501
+ return table.playerOrder;
2502
+ }
2503
+ function getPlayerResources(table, playerId) {
2504
+ return perPlayerGet(
2505
+ table.resources,
2506
+ playerId
2791
2507
  );
2792
2508
  }
2793
- function getSpaceEdges(table, boardId, spaceId) {
2794
- return getTiledBoard(table, boardId).edges.filter((edge) => edge.spaceIds.includes(spaceId)).map((edge) => edge.id);
2795
- }
2796
- function getSpaceVertices(table, boardId, spaceId) {
2797
- return getTiledBoard(table, boardId).vertices.filter((vertex) => vertex.spaceIds.includes(spaceId)).map((vertex) => vertex.id);
2509
+ function getPlayerResourceAmount(table, playerId, resourceId) {
2510
+ const playerResources = perPlayerGet(
2511
+ table.resources,
2512
+ playerId
2513
+ );
2514
+ if (!playerResources) return 0;
2515
+ const value = playerResources[resourceId];
2516
+ return typeof value === "number" ? value : 0;
2798
2517
  }
2799
- function getIncidentEdges(table, boardId, vertexId) {
2800
- const tiledBoard = getTiledBoard(table, boardId);
2801
- const vertex = tiledBoard.vertices.find(
2802
- (candidate) => candidate.id === vertexId
2518
+ function getPlayerResourceTotal(table, playerId) {
2519
+ const playerResources = perPlayerGet(
2520
+ table.resources,
2521
+ playerId
2803
2522
  );
2804
- if (!vertex) {
2805
- throw new Error(`Unknown vertex '${vertexId}' on board '${boardId}'.`);
2523
+ if (!playerResources) return 0;
2524
+ let total = 0;
2525
+ for (const key of Object.keys(playerResources)) {
2526
+ const value = playerResources[key];
2527
+ if (typeof value === "number") total += value;
2806
2528
  }
2807
- const vertexSpaceIds = new Set(vertex.spaceIds);
2808
- return tiledBoard.edges.filter(
2809
- (edge) => edge.spaceIds.every((spaceId) => vertexSpaceIds.has(spaceId))
2810
- ).map((edge) => edge.id);
2529
+ return total;
2811
2530
  }
2812
- function getIncidentVertices(table, boardId, edgeId) {
2813
- const tiledBoard = getTiledBoard(table, boardId);
2814
- const edge = tiledBoard.edges.find((candidate) => candidate.id === edgeId);
2815
- if (!edge) {
2816
- throw new Error(`Unknown edge '${edgeId}' on board '${boardId}'.`);
2531
+ function getNextPlayerInOrder(table, playerId) {
2532
+ const order = table.playerOrder;
2533
+ if (order.length === 0) return null;
2534
+ const idx = order.indexOf(playerId);
2535
+ if (idx < 0) return null;
2536
+ return order[(idx + 1) % order.length] ?? null;
2537
+ }
2538
+ function forEachResourceEntry(amounts, visit) {
2539
+ for (const resourceId of Object.keys(amounts)) {
2540
+ const amount = amounts[resourceId];
2541
+ if (typeof amount !== "number" || amount === 0) continue;
2542
+ visit(resourceId, amount);
2817
2543
  }
2818
- const edgeSpaceIds = new Set(edge.spaceIds);
2819
- return tiledBoard.vertices.filter(
2820
- (vertex) => Array.from(edgeSpaceIds).every(
2821
- (spaceId) => vertex.spaceIds.includes(spaceId)
2822
- )
2823
- ).map((vertex) => vertex.id);
2824
2544
  }
2825
- function getRelatedSpaces(table, boardId, spaceId, relationTypeId) {
2826
- const board = getBoard(table, boardId);
2827
- const related = /* @__PURE__ */ new Set();
2828
- for (const relation of board.relations) {
2829
- if (relation.typeId !== relationTypeId) {
2830
- continue;
2831
- }
2832
- if (relation.fromSpaceId === spaceId) {
2833
- related.add(relation.toSpaceId);
2834
- continue;
2835
- }
2836
- if (!relation.directed && relation.toSpaceId === spaceId) {
2837
- related.add(relation.fromSpaceId);
2545
+ function canAffordResources(table, playerId, amounts) {
2546
+ for (const resourceId of Object.keys(amounts)) {
2547
+ const required = amounts[resourceId];
2548
+ if (typeof required !== "number" || required <= 0) continue;
2549
+ if (getPlayerResourceAmount(table, playerId, resourceId) < required) {
2550
+ return false;
2838
2551
  }
2839
2552
  }
2840
- return [...related];
2841
- }
2842
- function getAdjacentSpaces(table, boardId, spaceId) {
2843
- return getRelatedSpaces(
2844
- table,
2845
- boardId,
2846
- spaceId,
2847
- "adjacent"
2848
- );
2553
+ return true;
2849
2554
  }
2850
- function getSpaceDistance(table, boardId, fromSpaceId, toSpaceId) {
2851
- if (fromSpaceId === toSpaceId) {
2852
- return 0;
2853
- }
2854
- const visited = /* @__PURE__ */ new Set([fromSpaceId]);
2855
- let frontier = [fromSpaceId];
2856
- let distance = 0;
2857
- while (frontier.length > 0) {
2858
- distance += 1;
2859
- const nextFrontier = [];
2860
- for (const currentSpaceId of frontier) {
2861
- for (const neighborId of getAdjacentSpaces(
2862
- table,
2863
- boardId,
2864
- currentSpaceId
2865
- )) {
2866
- if (neighborId === toSpaceId) {
2867
- return distance;
2868
- }
2869
- if (!visited.has(neighborId)) {
2870
- visited.add(neighborId);
2871
- nextFrontier.push(neighborId);
2872
- }
2873
- }
2874
- }
2875
- frontier = nextFrontier;
2555
+ function getMissingResources(table, playerId, amounts) {
2556
+ const missing = {};
2557
+ for (const resourceId of Object.keys(amounts)) {
2558
+ const required = amounts[resourceId];
2559
+ if (typeof required !== "number" || required <= 0) continue;
2560
+ const have = getPlayerResourceAmount(table, playerId, resourceId);
2561
+ if (have < required) missing[resourceId] = required - have;
2876
2562
  }
2877
- return Number.POSITIVE_INFINITY;
2563
+ return missing;
2878
2564
  }
2879
- function getSquareNeighbors(table, boardId, spaceId, options = {}) {
2880
- const board = getSquareBoard(table, boardId);
2881
- const space = getSquareSpace(table, boardId, spaceId);
2882
- const offsets = options.mode === "diagonal" ? [
2883
- [-1, -1],
2884
- [-1, 1],
2885
- [1, -1],
2886
- [1, 1]
2887
- ] : options.mode === "all" ? [
2888
- [-1, 0],
2889
- [0, 1],
2890
- [1, 0],
2891
- [0, -1],
2892
- [-1, -1],
2893
- [-1, 1],
2894
- [1, -1],
2895
- [1, 1]
2896
- ] : [
2897
- [-1, 0],
2898
- [0, 1],
2899
- [1, 0],
2900
- [0, -1]
2901
- ];
2902
- return offsets.map(
2903
- ([rowOffset, colOffset]) => Object.values(board.spaces).find(
2904
- (candidate) => candidate.row === space.row + rowOffset && candidate.col === space.col + colOffset
2565
+ function withPlayerResources(table, playerId, nextForPlayer) {
2566
+ return {
2567
+ ...table,
2568
+ resources: perPlayerSet(
2569
+ table.resources,
2570
+ playerId,
2571
+ nextForPlayer
2905
2572
  )
2906
- ).filter((candidate) => candidate !== void 0).map((candidate) => candidate.id);
2907
- }
2908
- function getSquareDistance(table, boardId, fromSpaceId, toSpaceId, options = {}) {
2909
- const from = getSquareSpace(table, boardId, fromSpaceId);
2910
- const to = getSquareSpace(table, boardId, toSpaceId);
2911
- const rowDistance = Math.abs(from.row - to.row);
2912
- const colDistance = Math.abs(from.col - to.col);
2913
- return options.metric === "chebyshev" ? Math.max(rowDistance, colDistance) : rowDistance + colDistance;
2573
+ };
2914
2574
  }
2915
- function getBoardsByTypeId(table, typeId) {
2916
- return Object.entries(table.boards.byId).filter(([, board]) => board.typeId === typeId).map(([boardId]) => boardId);
2575
+ function addPlayerResources(table, playerId, amounts) {
2576
+ const prev = perPlayerGet(
2577
+ table.resources,
2578
+ playerId
2579
+ ) ?? {};
2580
+ const next = { ...prev };
2581
+ forEachResourceEntry(amounts, (resourceId, amount) => {
2582
+ if (amount < 0) {
2583
+ throw new Error(
2584
+ `addPlayerResources: negative amount for resource '${resourceId}'. Use spendPlayerResources or transferPlayerResources instead.`
2585
+ );
2586
+ }
2587
+ next[resourceId] = (next[resourceId] ?? 0) + amount;
2588
+ });
2589
+ return withPlayerResources(table, playerId, next);
2917
2590
  }
2918
- function getSpacesByTypeId(table, boardId, typeId) {
2919
- return Object.entries(getBoard(table, boardId).spaces).filter(([, space]) => space.typeId === typeId).map(([spaceId]) => spaceId);
2591
+ function spendPlayerResources(table, playerId, amounts) {
2592
+ if (!canAffordResources(table, playerId, amounts)) {
2593
+ const missing = getMissingResources(table, playerId, amounts);
2594
+ throw new Error(
2595
+ `spendPlayerResources: player '${playerId}' cannot afford ${JSON.stringify(
2596
+ missing
2597
+ )}. Check canAfford in your validate step first.`
2598
+ );
2599
+ }
2600
+ const prev = perPlayerGet(
2601
+ table.resources,
2602
+ playerId
2603
+ ) ?? {};
2604
+ const next = { ...prev };
2605
+ forEachResourceEntry(amounts, (resourceId, amount) => {
2606
+ if (amount < 0) {
2607
+ throw new Error(
2608
+ `spendPlayerResources: negative amount for resource '${resourceId}'. Pass positive amounts \u2014 the op deducts them from the player.`
2609
+ );
2610
+ }
2611
+ next[resourceId] = Math.max(0, (next[resourceId] ?? 0) - amount);
2612
+ });
2613
+ return withPlayerResources(table, playerId, next);
2920
2614
  }
2921
- function getEdgesByTypeId(table, boardId, typeId) {
2922
- return getTiledBoard(table, boardId).edges.filter((edge) => edge.typeId === typeId).map((edge) => edge.id);
2615
+ function transferPlayerResources(table, fromPlayerId, toPlayerId, amounts) {
2616
+ const afterSpend = spendPlayerResources(table, fromPlayerId, amounts);
2617
+ return addPlayerResources(afterSpend, toPlayerId, amounts);
2923
2618
  }
2924
- function getVerticesByTypeId(table, boardId, typeId) {
2925
- return getTiledBoard(table, boardId).vertices.filter((vertex) => vertex.typeId === typeId).map((vertex) => vertex.id);
2619
+ function setPlayerResource(table, playerId, resourceId, amount) {
2620
+ if (!Number.isFinite(amount) || amount < 0) {
2621
+ throw new Error(
2622
+ `setPlayerResource: amount must be a non-negative finite number, got ${amount}.`
2623
+ );
2624
+ }
2625
+ const prev = perPlayerGet(
2626
+ table.resources,
2627
+ playerId
2628
+ ) ?? {};
2629
+ return withPlayerResources(table, playerId, {
2630
+ ...prev,
2631
+ [resourceId]: amount
2632
+ });
2926
2633
  }
2927
- function getComponentsOnSpace(table, boardId, spaceId) {
2928
- const zoneId = getSpace(table, boardId, spaceId).zoneId;
2929
- return orderedComponentIdsForLocation(
2930
- table,
2931
- (location) => location.type === "OnSpace" && location.boardId === boardId && location.spaceId === spaceId || location.type === "InZone" && typeof zoneId === "string" && zoneId.length > 0 && location.zoneId === zoneId
2634
+
2635
+ // src/reducer/table/component-mutations.ts
2636
+ function reindexSpaceOccupants(table, boardId, spaceId) {
2637
+ getComponentsOnSpace(table, boardId, spaceId).forEach(
2638
+ (componentId, index) => {
2639
+ const location = table.componentLocations[componentId];
2640
+ if (location?.type === "OnSpace") {
2641
+ table.componentLocations[componentId] = {
2642
+ ...location,
2643
+ position: index
2644
+ };
2645
+ }
2646
+ }
2932
2647
  );
2933
- }
2934
- function getComponentsInContainer(table, boardId, containerId) {
2935
- const zoneId = getContainer(table, boardId, containerId).zoneId;
2936
- return orderedComponentIdsForLocation(
2937
- table,
2938
- (location) => location.type === "InContainer" && location.boardId === boardId && location.containerId === containerId || location.type === "InZone" && typeof zoneId === "string" && zoneId.length > 0 && location.zoneId === zoneId
2648
+ }
2649
+ function reindexContainerOccupants(table, boardId, containerId) {
2650
+ getComponentsInContainer(table, boardId, containerId).forEach(
2651
+ (componentId, index) => {
2652
+ const location = table.componentLocations[componentId];
2653
+ if (location?.type === "InContainer") {
2654
+ table.componentLocations[componentId] = {
2655
+ ...location,
2656
+ position: index
2657
+ };
2658
+ }
2659
+ }
2939
2660
  );
2940
2661
  }
2941
- function getComponentsOnEdge(table, boardId, edgeId) {
2942
- return orderedComponentIdsForLocation(
2662
+ function reindexEdgeOccupants(table, boardId, edgeId) {
2663
+ orderedComponentIdsForLocation(
2943
2664
  table,
2944
2665
  (location) => location.type === "OnEdge" && location.boardId === boardId && location.edgeId === edgeId
2945
- );
2666
+ ).forEach((componentId, index) => {
2667
+ const location = table.componentLocations[componentId];
2668
+ if (location?.type === "OnEdge") {
2669
+ table.componentLocations[componentId] = {
2670
+ ...location,
2671
+ position: index
2672
+ };
2673
+ }
2674
+ });
2946
2675
  }
2947
- function getComponentsOnVertex(table, boardId, vertexId) {
2948
- return orderedComponentIdsForLocation(
2676
+ function reindexVertexOccupants(table, boardId, vertexId) {
2677
+ orderedComponentIdsForLocation(
2949
2678
  table,
2950
2679
  (location) => location.type === "OnVertex" && location.boardId === boardId && location.vertexId === vertexId
2951
- );
2680
+ ).forEach((componentId, index) => {
2681
+ const location = table.componentLocations[componentId];
2682
+ if (location?.type === "OnVertex") {
2683
+ table.componentLocations[componentId] = {
2684
+ ...location,
2685
+ position: index
2686
+ };
2687
+ }
2688
+ });
2689
+ }
2690
+ function reindexSlotOccupants(table, host, slotId) {
2691
+ orderedComponentIdsForLocation(
2692
+ table,
2693
+ (location) => location.type === "InSlot" && location.host.kind === host.kind && location.host.id === host.id && location.slotId === slotId
2694
+ ).forEach((componentId, index) => {
2695
+ const location = table.componentLocations[componentId];
2696
+ if (location?.type === "InSlot") {
2697
+ table.componentLocations[componentId] = {
2698
+ ...location,
2699
+ position: index
2700
+ };
2701
+ }
2702
+ });
2703
+ }
2704
+ function removeComponentFromCurrentLocation(table, componentId) {
2705
+ const currentLocation = table.componentLocations[componentId];
2706
+ if (!currentLocation) {
2707
+ return;
2708
+ }
2709
+ if (currentLocation.type === "InDeck") {
2710
+ const nextCards = ensureArray(table.decks[currentLocation.deckId]).filter(
2711
+ (candidate) => candidate !== componentId
2712
+ );
2713
+ syncSharedZoneWithDeck(
2714
+ table,
2715
+ currentLocation.deckId,
2716
+ nextCards
2717
+ );
2718
+ nextCards.forEach((cardId, index) => {
2719
+ const location = table.componentLocations[cardId];
2720
+ if (location?.type === "InDeck") {
2721
+ table.componentLocations[cardId] = {
2722
+ ...location,
2723
+ position: index
2724
+ };
2725
+ }
2726
+ });
2727
+ return;
2728
+ }
2729
+ if (currentLocation.type === "InHand") {
2730
+ const nextCards = ensureArray(
2731
+ ppRead(table.hands[currentLocation.handId], currentLocation.playerId)
2732
+ ).filter((candidate) => candidate !== componentId);
2733
+ syncPlayerZoneWithHand(
2734
+ table,
2735
+ currentLocation.handId,
2736
+ currentLocation.playerId,
2737
+ nextCards
2738
+ );
2739
+ nextCards.forEach((cardId, index) => {
2740
+ const location = table.componentLocations[cardId];
2741
+ if (location?.type === "InHand") {
2742
+ table.componentLocations[cardId] = {
2743
+ ...location,
2744
+ position: index
2745
+ };
2746
+ }
2747
+ });
2748
+ return;
2749
+ }
2750
+ if (currentLocation.type === "OnSpace") {
2751
+ delete table.componentLocations[componentId];
2752
+ reindexSpaceOccupants(
2753
+ table,
2754
+ currentLocation.boardId,
2755
+ currentLocation.spaceId
2756
+ );
2757
+ return;
2758
+ }
2759
+ if (currentLocation.type === "InZone") {
2760
+ if (currentLocation.zoneId in table.zones.shared) {
2761
+ const nextComponents = ensureArray(
2762
+ table.zones.shared[currentLocation.zoneId]
2763
+ ).filter((candidate) => candidate !== componentId);
2764
+ syncSharedZoneWithDeck(
2765
+ table,
2766
+ currentLocation.zoneId,
2767
+ nextComponents
2768
+ );
2769
+ nextComponents.forEach((currentComponentId, index) => {
2770
+ const location = table.componentLocations[currentComponentId];
2771
+ if (location?.type === "InZone") {
2772
+ table.componentLocations[currentComponentId] = {
2773
+ ...location,
2774
+ position: index
2775
+ };
2776
+ }
2777
+ });
2778
+ }
2779
+ delete table.componentLocations[componentId];
2780
+ return;
2781
+ }
2782
+ if (currentLocation.type === "InContainer") {
2783
+ delete table.componentLocations[componentId];
2784
+ reindexContainerOccupants(
2785
+ table,
2786
+ currentLocation.boardId,
2787
+ currentLocation.containerId
2788
+ );
2789
+ return;
2790
+ }
2791
+ if (currentLocation.type === "OnEdge") {
2792
+ delete table.componentLocations[componentId];
2793
+ reindexEdgeOccupants(
2794
+ table,
2795
+ currentLocation.boardId,
2796
+ currentLocation.edgeId
2797
+ );
2798
+ return;
2799
+ }
2800
+ if (currentLocation.type === "OnVertex") {
2801
+ delete table.componentLocations[componentId];
2802
+ reindexVertexOccupants(
2803
+ table,
2804
+ currentLocation.boardId,
2805
+ currentLocation.vertexId
2806
+ );
2807
+ return;
2808
+ }
2809
+ if (currentLocation.type === "InSlot") {
2810
+ delete table.componentLocations[componentId];
2811
+ reindexSlotOccupants(table, currentLocation.host, currentLocation.slotId);
2812
+ return;
2813
+ }
2814
+ delete table.componentLocations[componentId];
2952
2815
  }
2953
2816
  function moveComponentToSpace(table, componentId, boardId, spaceId) {
2954
2817
  const nextTable = cloneRuntimeTable(table);
@@ -2979,37 +2842,176 @@ function moveComponentToContainer(table, componentId, boardId, containerId) {
2979
2842
  };
2980
2843
  return nextTable;
2981
2844
  }
2982
- function moveComponentToDetached(table, componentId) {
2845
+ function moveComponentToDetached(table, componentId) {
2846
+ const nextTable = cloneRuntimeTable(table);
2847
+ removeComponentFromCurrentLocation(nextTable, componentId);
2848
+ nextTable.componentLocations[componentId] = { type: "Detached" };
2849
+ return nextTable;
2850
+ }
2851
+ function moveComponentToEdge(table, componentId, boardId, edgeId) {
2852
+ const nextTable = cloneRuntimeTable(table);
2853
+ getEdge(nextTable, boardId, edgeId);
2854
+ const position = getComponentsOnEdge(nextTable, boardId, edgeId).length;
2855
+ removeComponentFromCurrentLocation(nextTable, componentId);
2856
+ nextTable.componentLocations[componentId] = {
2857
+ type: "OnEdge",
2858
+ boardId,
2859
+ edgeId,
2860
+ position
2861
+ };
2862
+ return nextTable;
2863
+ }
2864
+ function moveComponentToVertex(table, componentId, boardId, vertexId) {
2865
+ const nextTable = cloneRuntimeTable(table);
2866
+ getVertex(nextTable, boardId, vertexId);
2867
+ const position = getComponentsOnVertex(nextTable, boardId, vertexId).length;
2868
+ removeComponentFromCurrentLocation(nextTable, componentId);
2869
+ nextTable.componentLocations[componentId] = {
2870
+ type: "OnVertex",
2871
+ boardId,
2872
+ vertexId,
2873
+ position
2874
+ };
2875
+ return nextTable;
2876
+ }
2877
+
2878
+ // src/reducer/table/card-mutations.ts
2879
+ function shufflePlayerZoneCards(table, zoneId, playerId, nextCards) {
2880
+ if (nextCards === void 0) {
2881
+ const fromZone = ppRead(
2882
+ table.zones.perPlayer[zoneId] ?? table.hands[zoneId],
2883
+ playerId
2884
+ );
2885
+ return [...ensureArray(fromZone)];
2886
+ }
2887
+ table.hands[zoneId] = ppWrite(table.hands[zoneId], playerId, [
2888
+ ...nextCards
2889
+ ]);
2890
+ table.zones.perPlayer[zoneId] = ppWrite(
2891
+ table.zones.perPlayer[zoneId],
2892
+ playerId,
2893
+ [...nextCards]
2894
+ );
2895
+ return [...nextCards];
2896
+ }
2897
+ function appendToDeck(table, deckId, cardId, playedBy = null, position = "bottom") {
2898
+ const nextTable = cloneRuntimeTable(table);
2899
+ assertZoneScope(
2900
+ nextTable,
2901
+ deckId,
2902
+ "shared",
2903
+ "addCardToSharedZone",
2904
+ "zoneId"
2905
+ );
2906
+ const deckCards = [
2907
+ ...ensureArray(nextTable.decks[deckId])
2908
+ ];
2909
+ assertCardAllowedInZone(nextTable, deckId, cardId);
2910
+ if (position === "top") {
2911
+ deckCards.unshift(cardId);
2912
+ } else {
2913
+ deckCards.push(cardId);
2914
+ }
2915
+ syncSharedZoneWithDeck(nextTable, deckId, deckCards);
2916
+ for (const [index, currentCardId] of deckCards.entries()) {
2917
+ if (currentCardId === cardId) {
2918
+ nextTable.componentLocations[currentCardId] = {
2919
+ type: "InDeck",
2920
+ deckId,
2921
+ playedBy,
2922
+ position: index
2923
+ };
2924
+ continue;
2925
+ }
2926
+ const existing = nextTable.componentLocations[currentCardId];
2927
+ nextTable.componentLocations[currentCardId] = {
2928
+ type: "InDeck",
2929
+ deckId,
2930
+ playedBy: existing?.type === "InDeck" ? existing.playedBy : null,
2931
+ position: index
2932
+ };
2933
+ }
2934
+ nextTable.ownerOfCard[cardId] = playedBy;
2935
+ nextTable.visibility[cardId] = {
2936
+ faceUp: true
2937
+ };
2938
+ return nextTable;
2939
+ }
2940
+ function removeFromDeck(table, deckId, cardId) {
2983
2941
  const nextTable = cloneRuntimeTable(table);
2984
- removeComponentFromCurrentLocation(nextTable, componentId);
2985
- nextTable.componentLocations[componentId] = { type: "Detached" };
2942
+ assertZoneScope(
2943
+ nextTable,
2944
+ deckId,
2945
+ "shared",
2946
+ "removeCardFromSharedZone",
2947
+ "zoneId"
2948
+ );
2949
+ const remaining = ensureArray(nextTable.decks[deckId]).filter(
2950
+ (candidate) => candidate !== cardId
2951
+ );
2952
+ syncSharedZoneWithDeck(nextTable, deckId, remaining);
2953
+ for (const [index, currentCardId] of remaining.entries()) {
2954
+ const currentLocation = nextTable.componentLocations[currentCardId];
2955
+ if (currentLocation?.type === "InDeck") {
2956
+ nextTable.componentLocations[currentCardId] = {
2957
+ ...currentLocation,
2958
+ position: index
2959
+ };
2960
+ }
2961
+ }
2986
2962
  return nextTable;
2987
2963
  }
2988
- function moveComponentToEdge(table, componentId, boardId, edgeId) {
2989
- const nextTable = cloneRuntimeTable(table);
2990
- getEdge(nextTable, boardId, edgeId);
2991
- const position = getComponentsOnEdge(nextTable, boardId, edgeId).length;
2992
- removeComponentFromCurrentLocation(nextTable, componentId);
2993
- nextTable.componentLocations[componentId] = {
2994
- type: "OnEdge",
2995
- boardId,
2996
- edgeId,
2997
- position
2998
- };
2999
- return nextTable;
2964
+ function computeVisibilityForPlayerZone(table, zoneId, playerId) {
2965
+ const mode = table.handVisibility[zoneId];
2966
+ if (mode === "all" || mode === "public") {
2967
+ return { faceUp: true };
2968
+ }
2969
+ return { faceUp: false, visibleTo: [playerId] };
3000
2970
  }
3001
- function moveComponentToVertex(table, componentId, boardId, vertexId) {
3002
- const nextTable = cloneRuntimeTable(table);
3003
- getVertex(nextTable, boardId, vertexId);
3004
- const position = getComponentsOnVertex(nextTable, boardId, vertexId).length;
3005
- removeComponentFromCurrentLocation(nextTable, componentId);
3006
- nextTable.componentLocations[componentId] = {
3007
- type: "OnVertex",
3008
- boardId,
3009
- vertexId,
3010
- position
2971
+ function moveFromHandToDeck(options) {
2972
+ const nextTable = cloneRuntimeTable(options.table);
2973
+ assertZoneScope(
2974
+ nextTable,
2975
+ options.handId,
2976
+ "perPlayer",
2977
+ "moveCardFromPlayerZoneToSharedZone",
2978
+ "fromZoneId"
2979
+ );
2980
+ assertZoneScope(
2981
+ nextTable,
2982
+ options.deckId,
2983
+ "shared",
2984
+ "moveCardFromPlayerZoneToSharedZone",
2985
+ "toZoneId"
2986
+ );
2987
+ const currentHand = ensureArray(
2988
+ ppRead(nextTable.hands[options.handId], options.playerId)
2989
+ ).filter((candidate) => candidate !== options.cardId);
2990
+ syncPlayerZoneWithHand(
2991
+ nextTable,
2992
+ options.handId,
2993
+ options.playerId,
2994
+ currentHand
2995
+ );
2996
+ for (const [index, currentCardId] of currentHand.entries()) {
2997
+ nextTable.componentLocations[currentCardId] = {
2998
+ type: "InHand",
2999
+ handId: options.handId,
3000
+ playerId: options.playerId,
3001
+ position: index
3002
+ };
3003
+ }
3004
+ nextTable.ownerOfCard[options.cardId] = options.playedBy ?? options.playerId;
3005
+ nextTable.visibility[options.cardId] = {
3006
+ faceUp: true
3011
3007
  };
3012
- return nextTable;
3008
+ return appendToDeck(
3009
+ nextTable,
3010
+ options.deckId,
3011
+ options.cardId,
3012
+ options.playedBy ?? options.playerId,
3013
+ options.position ?? "bottom"
3014
+ );
3013
3015
  }
3014
3016
  function moveCardFromPlayerZoneToSharedZone(options) {
3015
3017
  return moveFromHandToDeck({
@@ -3264,6 +3266,23 @@ function dealCardsFromDeckToHand(table, fromZoneId, playerId, toZoneId, count) {
3264
3266
  return nextTable;
3265
3267
  }
3266
3268
 
3269
+ // src/reducer/table/phase-state.ts
3270
+ function setActivePlayers(state, activePlayers) {
3271
+ return {
3272
+ ...state,
3273
+ flow: {
3274
+ ...state.flow,
3275
+ activePlayers
3276
+ }
3277
+ };
3278
+ }
3279
+ function setPhaseState(state, phaseState) {
3280
+ return {
3281
+ ...state,
3282
+ phase: phaseState
3283
+ };
3284
+ }
3285
+
3267
3286
  // src/reducer/rng.ts
3268
3287
  function nextRandomInt(bound, rng) {
3269
3288
  if (bound <= 0) {
@@ -4904,33 +4923,7 @@ function createTrustedInstructionRunner(scope, interactions, lifecycle) {
4904
4923
  };
4905
4924
  }
4906
4925
 
4907
- // src/reducer/parse-utils.ts
4908
- function formatIssue(label, issue) {
4909
- const path = issue.path.map(
4910
- (segment) => typeof segment === "symbol" ? segment.toString() : String(segment)
4911
- ).join(".");
4912
- return `${path || label}: ${issue.message}`;
4913
- }
4914
- function safeParseOrThrow(schema, value, label) {
4915
- const parsed = schema.safeParse(value);
4916
- if (!parsed.success) {
4917
- const message = parsed.error.issues.map((issue) => formatIssue(label, issue)).join("; ");
4918
- throw new Error(message || `Invalid ${label}`);
4919
- }
4920
- return parsed.data;
4921
- }
4922
-
4923
- // src/reducer/bundle/trusted/interaction-types.ts
4924
- function makeValidationError(errorCode, message) {
4925
- return {
4926
- valid: false,
4927
- errorCode,
4928
- message
4929
- };
4930
- }
4931
-
4932
- // src/reducer/bundle/trusted/interaction-collectors.ts
4933
- var MAX_EAGER_DEPENDENT_DOMAIN_CASES = 64;
4926
+ // src/reducer/bundle/trusted/collector-introspection.ts
4934
4927
  function collectTargetDomainMetadata(collector) {
4935
4928
  switch (collector.kind) {
4936
4929
  case "card": {
@@ -4956,128 +4949,44 @@ function collectTargetDomainMetadata(collector) {
4956
4949
  return {};
4957
4950
  }
4958
4951
  }
4959
- function withSelection(domain, collector) {
4960
- if (!collector.selection) return domain;
4961
- return { ...domain, selection: collector.selection };
4962
- }
4963
- function lazyTargetDomainFromCollectorMetadata(key, collector, dependencies) {
4964
- const metadata = collectTargetDomainMetadata(collector);
4965
- if (collector.kind === "card") {
4966
- return withSelection(
4967
- {
4968
- type: "cardTarget",
4969
- projection: "lazy",
4970
- targetKind: "card",
4971
- zoneIds: metadata.zoneIds ?? (metadata.zoneId === void 0 ? [] : [metadata.zoneId]),
4972
- dependencies: {
4973
- mode: "lazy",
4974
- dependsOn: dependencies,
4975
- resolver: { inputKey: key }
4976
- }
4977
- },
4978
- collector
4979
- );
4980
- }
4981
- if (collector.kind === "board-edge" || collector.kind === "board-space" || collector.kind === "board-tile" || collector.kind === "board-vertex") {
4982
- return withSelection(
4983
- {
4984
- type: "boardTarget",
4985
- projection: "lazy",
4986
- targetKind: metadata.targetKind ?? collector.kind.replace("board-", ""),
4987
- boardId: metadata.boardId ?? "",
4988
- ...metadata.valueKind ? { valueKind: metadata.valueKind } : {},
4989
- dependencies: {
4990
- mode: "lazy",
4991
- dependsOn: dependencies,
4992
- resolver: { inputKey: key }
4993
- }
4994
- },
4995
- collector
4996
- );
4997
- }
4998
- return null;
4952
+ function interactionInputsOf(interaction) {
4953
+ return interaction.inputs;
4999
4954
  }
5000
- function finiteValuesForDependency(key, domain) {
5001
- if (!domain) {
5002
- throw new Error(
5003
- `Interaction input '${key}' is declared as a dependency before it has a projected domain.`
5004
- );
5005
- }
5006
- if (domain.type === "cardTarget" || domain.type === "boardTarget") {
5007
- if (domain.projection !== "resolved") {
5008
- throw new Error(
5009
- `Interaction input '${key}' cannot be used as a dependency because its target domain is not finite.`
5010
- );
4955
+ function collectFirstCardZoneId(interaction) {
4956
+ const collectors = interactionInputsOf(interaction);
4957
+ for (const collector of Object.values(collectors)) {
4958
+ if (collector.kind === "card") {
4959
+ if (collector.meta.zoneId.length > 0) {
4960
+ return collector.meta.zoneId;
4961
+ }
5011
4962
  }
5012
- return [...domain.eligibleTargets];
5013
- }
5014
- if (domain.type === "choice") {
5015
- return domain.choices.flatMap(
5016
- (choice) => choice.value === null ? [] : [choice.value]
5017
- );
5018
4963
  }
5019
- throw new Error(
5020
- `Interaction input '${key}' cannot be used as a dependency. V1 supports finite target and choice dependencies only.`
5021
- );
5022
- }
5023
- function cartesianDependencyTuples(dependencies, domainsByKey) {
5024
- return dependencies.reduce(
5025
- (tuples, key) => {
5026
- const values = finiteValuesForDependency(key, domainsByKey[key]);
5027
- return tuples.flatMap(
5028
- (tuple) => values.map((value) => ({ ...tuple, [key]: value }))
5029
- );
5030
- },
5031
- [{}]
5032
- );
5033
- }
5034
- function withoutDependentProjection(domain) {
5035
- const { dependencies: _dependencies, ...rest } = domain;
5036
- return rest;
4964
+ return void 0;
5037
4965
  }
5038
- function toLazyTargetDomain(key, domain, dependencies) {
5039
- if (domain.type === "cardTarget") {
5040
- return {
5041
- type: "cardTarget",
5042
- projection: "lazy",
5043
- targetKind: "card",
5044
- zoneIds: domain.zoneIds,
5045
- ...domain.selection ? { selection: domain.selection } : {},
5046
- dependencies: {
5047
- mode: "lazy",
5048
- dependsOn: dependencies,
5049
- resolver: { inputKey: key }
5050
- }
5051
- };
5052
- }
5053
- if (domain.type === "boardTarget") {
5054
- return {
5055
- type: "boardTarget",
5056
- projection: "lazy",
5057
- targetKind: domain.targetKind,
5058
- boardId: domain.boardId,
5059
- ...domain.valueKind ? { valueKind: domain.valueKind } : {},
5060
- ...domain.selection ? { selection: domain.selection } : {},
5061
- dependencies: {
5062
- mode: "lazy",
5063
- dependsOn: dependencies,
5064
- resolver: { inputKey: key }
4966
+ function collectCardZoneIds(interaction) {
4967
+ const collectors = interactionInputsOf(interaction);
4968
+ const zoneIds = /* @__PURE__ */ new Set();
4969
+ for (const collector of Object.values(collectors)) {
4970
+ if (collector.kind === "card") {
4971
+ for (const zoneId of collector.meta.zoneIds ?? [collector.meta.zoneId]) {
4972
+ if (zoneId.length > 0) zoneIds.add(zoneId);
5065
4973
  }
5066
- };
4974
+ }
5067
4975
  }
5068
- return null;
5069
- }
5070
- function dependencyCaseCount(dependencies, domainsByKey) {
5071
- return dependencies.map((key) => finiteValuesForDependency(key, domainsByKey[key]).length).reduce((total, count) => total * count, 1);
4976
+ return [...zoneIds];
5072
4977
  }
5073
- function unsupportedDefaultInputError(key, collector) {
5074
- return new Error(
5075
- `Interaction input '${key}' uses a '${collector.kind}' collector without a default-renderable domain or target metadata. Use formInput.choice(...), formInput.choiceList(...), formInput.number(...), formInput.resourceMap(...), cardInput(...), or boardInput(...). For custom payloads, use a custom interaction surface with paramsSchema instead of the default InteractionForm.`
5076
- );
4978
+ function findCardInputKey(interaction) {
4979
+ return Object.entries(interactionInputsOf(interaction)).find(
4980
+ ([, collector]) => collector.kind === "card"
4981
+ )?.[0];
5077
4982
  }
5078
- function interactionInputsOf(interaction) {
5079
- return interaction.inputs;
4983
+ function findCardInputKeyForZone(interaction, zoneId) {
4984
+ return Object.entries(interactionInputsOf(interaction)).find(
4985
+ ([, collector]) => collector.kind === "card" && (collector.meta.zoneIds ?? [collector.meta.zoneId]).map(String).includes(zoneId)
4986
+ )?.[0];
5080
4987
  }
4988
+
4989
+ // src/reducer/bundle/trusted/collector-eligibility.ts
5081
4990
  function collectEligibleTargets(interaction, domainState, playerId, options = {}) {
5082
4991
  const collectors = interactionInputsOf(interaction);
5083
4992
  const result = {};
@@ -5108,233 +5017,33 @@ function collectEligibleTargets(interaction, domainState, playerId, options = {}
5108
5017
  }
5109
5018
  return result;
5110
5019
  }
5111
- function collectInputDomains(interaction, domainState, playerId, eligibleTargets, options = {}) {
5112
- const collectors = interactionInputsOf(interaction);
5113
- let queriesLazy = options.queries ?? null;
5114
- const queries = () => queriesLazy ??= createStateQueries(
5115
- domainState
5116
- );
5117
- let derivedLazy = options.derived ?? null;
5118
- const derived = () => derivedLazy ??= createDerivedResolver(domainState, { q: queries() });
5119
- const result = {};
5120
- for (const [key, collector] of Object.entries(collectors)) {
5121
- if (collector.kind === "rng") {
5122
- continue;
5123
- }
5124
- if (collector.kind === "prompt") {
5125
- const optionFactory = collector.meta?.options;
5126
- if (!optionFactory) continue;
5127
- result[key] = {
5128
- type: "choice",
5129
- choices: optionFactory(domainState, playerId, queries()).map(
5130
- (option) => ({
5131
- value: String(option.id),
5132
- label: option.label ?? String(option.id),
5133
- ...(() => {
5134
- const issue = collector.validateTarget?.(
5135
- domainState,
5136
- playerId,
5137
- queries(),
5138
- String(option.id)
5139
- );
5140
- return issue ? {
5141
- disabled: true,
5142
- disabledReason: issue.message ?? issue.errorCode
5143
- } : {};
5144
- })()
5145
- })
5146
- )
5147
- };
5148
- continue;
5149
- }
5150
- if (collector.domain) {
5151
- const domainProjector = collector.domain;
5152
- const dependencies = collector.dependsOn ?? [];
5153
- if (options.includeEligibleTargets === false && !Object.prototype.hasOwnProperty.call(eligibleTargets, key)) {
5154
- const lazyDomain2 = lazyTargetDomainFromCollectorMetadata(
5155
- key,
5156
- collector,
5157
- dependencies
5158
- );
5159
- if (lazyDomain2) {
5160
- result[key] = lazyDomain2;
5161
- continue;
5162
- }
5163
- }
5164
- const baseDomain = withSelection(
5165
- domainProjector(
5166
- domainState,
5167
- playerId,
5168
- queries(),
5169
- derived(),
5170
- {}
5171
- ),
5172
- collector
5173
- );
5174
- if (dependencies.length === 0) {
5175
- result[key] = baseDomain;
5176
- continue;
5177
- }
5178
- const caseCount = dependencyCaseCount(dependencies, result);
5179
- const lazyDomain = caseCount > MAX_EAGER_DEPENDENT_DOMAIN_CASES ? toLazyTargetDomain(key, baseDomain, dependencies) : null;
5180
- if (lazyDomain) {
5181
- result[key] = lazyDomain;
5182
- continue;
5183
- }
5184
- const dependentCases = cartesianDependencyTuples(
5185
- dependencies,
5186
- result
5187
- ).map((values) => ({
5188
- when: values,
5189
- domain: withoutDependentProjection(
5190
- withSelection(
5191
- domainProjector(
5192
- domainState,
5193
- playerId,
5194
- queries(),
5195
- derived(),
5196
- values
5197
- ),
5198
- collector
5199
- )
5200
- )
5201
- }));
5202
- result[key] = {
5203
- ...baseDomain,
5204
- dependencies: {
5205
- mode: "eager",
5206
- dependentCases
5207
- }
5208
- };
5209
- continue;
5210
- }
5211
- throw unsupportedDefaultInputError(key, collector);
5212
- }
5213
- return result;
5214
- }
5215
- function collectInteractionInputs(interaction, domainState, playerId, options = {}) {
5216
- const collectors = interactionInputsOf(interaction);
5217
- const dependencyKeys = new Set(
5218
- Object.values(collectors).flatMap((collector) => [
5219
- ...collector.dependsOn ?? []
5220
- ])
5221
- );
5222
- const collectedEligibleTargets = options.includeEligibleTargets === false && dependencyKeys.size === 0 ? {} : collectEligibleTargets(interaction, domainState, playerId, options);
5223
- const eligibleTargets = options.includeEligibleTargets === false ? Object.fromEntries(
5224
- Object.entries(collectedEligibleTargets).filter(
5225
- ([key]) => dependencyKeys.has(key)
5226
- )
5227
- ) : collectedEligibleTargets;
5228
- const domainsByKey = collectInputDomains(
5229
- interaction,
5230
- domainState,
5231
- playerId,
5232
- eligibleTargets,
5233
- options
5234
- );
5235
- let queriesLazy = options.queries ?? null;
5236
- const queries = () => queriesLazy ??= createStateQueries(
5237
- domainState
5238
- );
5239
- let derivedLazy = options.derived ?? null;
5240
- const derived = () => derivedLazy ??= createDerivedResolver(domainState, { q: queries() });
5241
- return Object.entries(collectors).flatMap(([key, collector]) => {
5242
- if (collector.kind === "rng") {
5243
- return [];
5244
- }
5245
- const domain = domainsByKey[key];
5246
- if (!domain && collector.kind === "prompt") {
5247
- return [];
5248
- }
5249
- if (!domain) {
5250
- throw unsupportedDefaultInputError(key, collector);
5251
- }
5252
- const dynamicDefault = collector.resolveDefaultValue?.(
5253
- domainState,
5254
- playerId,
5255
- queries(),
5256
- derived(),
5257
- domain
5258
- );
5259
- const defaultValue = "defaultValue" in collector ? collector.defaultValue : dynamicDefault;
5260
- warnConcreteDependentChoiceDefault({
5261
- inputKey: key,
5262
- collector,
5263
- domain,
5264
- defaultValue,
5265
- dependencyKeys
5266
- });
5267
- return [
5268
- {
5269
- key,
5270
- kind: collector.kind,
5271
- domain,
5272
- ...defaultValue !== void 0 ? { defaultValue } : {}
5273
- }
5274
- ];
5275
- });
5276
- }
5277
- var concreteDependentChoiceDefaultWarnings = /* @__PURE__ */ new Set();
5278
- function shouldEmitAuthoringWarning() {
5279
- const env = globalThis.process?.env;
5280
- return env?.NODE_ENV !== "production" && env?.DREAMBOARD_SUPPRESS_AUTHORING_WARNINGS !== "1";
5281
- }
5282
- function warnConcreteDependentChoiceDefault({
5283
- inputKey,
5284
- collector,
5285
- domain,
5286
- defaultValue,
5287
- dependencyKeys
5288
- }) {
5289
- if (!shouldEmitAuthoringWarning() || !dependencyKeys.has(inputKey) || collector.kind !== "form" || domain.type !== "choice" || defaultValue === void 0 || defaultValue === null) {
5290
- return;
5291
- }
5292
- const warningKey = `${inputKey}:${String(defaultValue)}`;
5293
- if (concreteDependentChoiceDefaultWarnings.has(warningKey)) {
5294
- return;
5295
- }
5296
- concreteDependentChoiceDefaultWarnings.add(warningKey);
5297
- console.warn(
5298
- `[dreamboard] Form choice input "${inputKey}" feeds another collector and defaulted to "${String(
5299
- defaultValue
5300
- )}". Dependent choices that select the object an action applies to should usually use defaultValue: () => undefined so collection stays explicit.`
5301
- );
5302
- }
5303
- function collectFirstCardZoneId(interaction) {
5304
- const collectors = interactionInputsOf(interaction);
5305
- for (const collector of Object.values(collectors)) {
5306
- if (collector.kind === "card") {
5307
- if (collector.meta.zoneId.length > 0) {
5308
- return collector.meta.zoneId;
5309
- }
5310
- }
5311
- }
5312
- return void 0;
5313
- }
5314
- function collectCardZoneIds(interaction) {
5315
- const collectors = interactionInputsOf(interaction);
5316
- const zoneIds = /* @__PURE__ */ new Set();
5317
- for (const collector of Object.values(collectors)) {
5318
- if (collector.kind === "card") {
5319
- for (const zoneId of collector.meta.zoneIds ?? [collector.meta.zoneId]) {
5320
- if (zoneId.length > 0) zoneIds.add(zoneId);
5321
- }
5322
- }
5323
- }
5324
- return [...zoneIds];
5020
+
5021
+ // src/reducer/parse-utils.ts
5022
+ function formatIssue(label, issue) {
5023
+ const path = issue.path.map(
5024
+ (segment) => typeof segment === "symbol" ? segment.toString() : String(segment)
5025
+ ).join(".");
5026
+ return `${path || label}: ${issue.message}`;
5325
5027
  }
5326
- function collectPromptOptions(interaction, domainState, playerId, queries) {
5327
- for (const collector of Object.values(interaction.inputs)) {
5328
- if (collector.kind !== "prompt") continue;
5329
- const factory = collector.meta?.options;
5330
- if (!factory) continue;
5331
- return factory(domainState, playerId, queries).map((option) => ({
5332
- id: String(option.id),
5333
- ...option.label !== void 0 ? { label: option.label } : {}
5334
- }));
5028
+ function safeParseOrThrow(schema, value, label) {
5029
+ const parsed = schema.safeParse(value);
5030
+ if (!parsed.success) {
5031
+ const message = parsed.error.issues.map((issue) => formatIssue(label, issue)).join("; ");
5032
+ throw new Error(message || `Invalid ${label}`);
5335
5033
  }
5336
- return void 0;
5034
+ return parsed.data;
5035
+ }
5036
+
5037
+ // src/reducer/bundle/trusted/interaction-types.ts
5038
+ function makeValidationError(errorCode, message) {
5039
+ return {
5040
+ valid: false,
5041
+ errorCode,
5042
+ message
5043
+ };
5337
5044
  }
5045
+
5046
+ // src/reducer/bundle/trusted/collector-params.ts
5338
5047
  function parseInteractionParams(interaction, rawParams, options = {}) {
5339
5048
  const collectors = interactionInputsOf(interaction);
5340
5049
  const record = rawParams ?? {};
@@ -5490,16 +5199,6 @@ function stableValueKey(value) {
5490
5199
  return `json:${JSON.stringify(value)}`;
5491
5200
  }
5492
5201
  }
5493
- function findCardInputKey(interaction) {
5494
- return Object.entries(interactionInputsOf(interaction)).find(
5495
- ([, collector]) => collector.kind === "card"
5496
- )?.[0];
5497
- }
5498
- function findCardInputKeyForZone(interaction, zoneId) {
5499
- return Object.entries(interactionInputsOf(interaction)).find(
5500
- ([, collector]) => collector.kind === "card" && (collector.meta.zoneIds ?? [collector.meta.zoneId]).map(String).includes(zoneId)
5501
- )?.[0];
5502
- }
5503
5202
 
5504
5203
  // src/reducer/bundle/trusted/interaction-authorization.ts
5505
5204
  function resolvePromptToSet(to) {
@@ -5557,46 +5256,364 @@ function createInteractionAuthorization(scope) {
5557
5256
  },
5558
5257
  projection
5559
5258
  )
5560
- });
5561
- return {
5562
- mode: "actors",
5563
- actors: resolvePromptToSet(resolved)
5259
+ });
5260
+ return {
5261
+ mode: "actors",
5262
+ actors: resolvePromptToSet(resolved)
5263
+ };
5264
+ }
5265
+ return { mode: "active" };
5266
+ }
5267
+ function isActorAuthorized(state, playerId, authorization) {
5268
+ if (authorization.mode === "addressees") {
5269
+ return authorization.addressees.has(playerId);
5270
+ }
5271
+ if (authorization.mode === "actors") {
5272
+ return authorization.actors.has(playerId);
5273
+ }
5274
+ const active = state.flow.activePlayers;
5275
+ if (active.length === 0) {
5276
+ return true;
5277
+ }
5278
+ return active.includes(playerId);
5279
+ }
5280
+ function isInteractionVisible(interaction, authorization, authorized) {
5281
+ return !(authorization.mode === "addressees" && !authorized) && !(interaction.visibility === "actorsOnly" && !authorized);
5282
+ }
5283
+ return {
5284
+ isActorAuthorized,
5285
+ isInteractionVisible,
5286
+ resolveInteractionActorAuthorization
5287
+ };
5288
+ }
5289
+
5290
+ // src/reducer/bundle/trusted/collector-domains.ts
5291
+ var MAX_EAGER_DEPENDENT_DOMAIN_CASES = 64;
5292
+ function withSelection(domain, collector) {
5293
+ if (!collector.selection) return domain;
5294
+ return { ...domain, selection: collector.selection };
5295
+ }
5296
+ function lazyTargetDomainFromCollectorMetadata(key, collector, dependencies) {
5297
+ const metadata = collectTargetDomainMetadata(collector);
5298
+ if (collector.kind === "card") {
5299
+ return withSelection(
5300
+ {
5301
+ type: "cardTarget",
5302
+ projection: "lazy",
5303
+ targetKind: "card",
5304
+ zoneIds: metadata.zoneIds ?? (metadata.zoneId === void 0 ? [] : [metadata.zoneId]),
5305
+ dependencies: {
5306
+ mode: "lazy",
5307
+ dependsOn: dependencies,
5308
+ resolver: { inputKey: key }
5309
+ }
5310
+ },
5311
+ collector
5312
+ );
5313
+ }
5314
+ if (collector.kind === "board-edge" || collector.kind === "board-space" || collector.kind === "board-tile" || collector.kind === "board-vertex") {
5315
+ return withSelection(
5316
+ {
5317
+ type: "boardTarget",
5318
+ projection: "lazy",
5319
+ targetKind: metadata.targetKind ?? collector.kind.replace("board-", ""),
5320
+ boardId: metadata.boardId ?? "",
5321
+ ...metadata.valueKind ? { valueKind: metadata.valueKind } : {},
5322
+ dependencies: {
5323
+ mode: "lazy",
5324
+ dependsOn: dependencies,
5325
+ resolver: { inputKey: key }
5326
+ }
5327
+ },
5328
+ collector
5329
+ );
5330
+ }
5331
+ return null;
5332
+ }
5333
+ function finiteValuesForDependency(key, domain) {
5334
+ if (!domain) {
5335
+ throw new Error(
5336
+ `Interaction input '${key}' is declared as a dependency before it has a projected domain.`
5337
+ );
5338
+ }
5339
+ if (domain.type === "cardTarget" || domain.type === "boardTarget") {
5340
+ if (domain.projection !== "resolved") {
5341
+ throw new Error(
5342
+ `Interaction input '${key}' cannot be used as a dependency because its target domain is not finite.`
5343
+ );
5344
+ }
5345
+ return [...domain.eligibleTargets];
5346
+ }
5347
+ if (domain.type === "choice") {
5348
+ return domain.choices.flatMap(
5349
+ (choice) => choice.value === null ? [] : [choice.value]
5350
+ );
5351
+ }
5352
+ throw new Error(
5353
+ `Interaction input '${key}' cannot be used as a dependency. V1 supports finite target and choice dependencies only.`
5354
+ );
5355
+ }
5356
+ function cartesianDependencyTuples(dependencies, domainsByKey) {
5357
+ return dependencies.reduce(
5358
+ (tuples, key) => {
5359
+ const values = finiteValuesForDependency(key, domainsByKey[key]);
5360
+ return tuples.flatMap(
5361
+ (tuple) => values.map((value) => ({ ...tuple, [key]: value }))
5362
+ );
5363
+ },
5364
+ [{}]
5365
+ );
5366
+ }
5367
+ function withoutDependentProjection(domain) {
5368
+ const { dependencies: _dependencies, ...rest } = domain;
5369
+ return rest;
5370
+ }
5371
+ function toLazyTargetDomain(key, domain, dependencies) {
5372
+ if (domain.type === "cardTarget") {
5373
+ return {
5374
+ type: "cardTarget",
5375
+ projection: "lazy",
5376
+ targetKind: "card",
5377
+ zoneIds: domain.zoneIds,
5378
+ ...domain.selection ? { selection: domain.selection } : {},
5379
+ dependencies: {
5380
+ mode: "lazy",
5381
+ dependsOn: dependencies,
5382
+ resolver: { inputKey: key }
5383
+ }
5384
+ };
5385
+ }
5386
+ if (domain.type === "boardTarget") {
5387
+ return {
5388
+ type: "boardTarget",
5389
+ projection: "lazy",
5390
+ targetKind: domain.targetKind,
5391
+ boardId: domain.boardId,
5392
+ ...domain.valueKind ? { valueKind: domain.valueKind } : {},
5393
+ ...domain.selection ? { selection: domain.selection } : {},
5394
+ dependencies: {
5395
+ mode: "lazy",
5396
+ dependsOn: dependencies,
5397
+ resolver: { inputKey: key }
5398
+ }
5399
+ };
5400
+ }
5401
+ return null;
5402
+ }
5403
+ function dependencyCaseCount(dependencies, domainsByKey) {
5404
+ return dependencies.map((key) => finiteValuesForDependency(key, domainsByKey[key]).length).reduce((total, count) => total * count, 1);
5405
+ }
5406
+ function unsupportedDefaultInputError(key, collector) {
5407
+ return new Error(
5408
+ `Interaction input '${key}' uses a '${collector.kind}' collector without a default-renderable domain or target metadata. Use formInput.choice(...), formInput.choiceList(...), formInput.number(...), formInput.resourceMap(...), cardInput(...), or boardInput(...). For custom payloads, use a custom interaction surface with paramsSchema instead of the default InteractionForm.`
5409
+ );
5410
+ }
5411
+ function collectInputDomains(interaction, domainState, playerId, eligibleTargets, options = {}) {
5412
+ const collectors = interactionInputsOf(interaction);
5413
+ let queriesLazy = options.queries ?? null;
5414
+ const queries = () => queriesLazy ??= createStateQueries(
5415
+ domainState
5416
+ );
5417
+ let derivedLazy = options.derived ?? null;
5418
+ const derived = () => derivedLazy ??= createDerivedResolver(domainState, { q: queries() });
5419
+ const result = {};
5420
+ for (const [key, collector] of Object.entries(collectors)) {
5421
+ if (collector.kind === "rng") {
5422
+ continue;
5423
+ }
5424
+ if (collector.kind === "prompt") {
5425
+ const optionFactory = collector.meta?.options;
5426
+ if (!optionFactory) continue;
5427
+ result[key] = {
5428
+ type: "choice",
5429
+ choices: optionFactory(domainState, playerId, queries()).map(
5430
+ (option) => ({
5431
+ value: String(option.id),
5432
+ label: option.label ?? String(option.id),
5433
+ ...(() => {
5434
+ const issue = collector.validateTarget?.(
5435
+ domainState,
5436
+ playerId,
5437
+ queries(),
5438
+ String(option.id)
5439
+ );
5440
+ return issue ? {
5441
+ disabled: true,
5442
+ disabledReason: issue.message ?? issue.errorCode
5443
+ } : {};
5444
+ })()
5445
+ })
5446
+ )
5447
+ };
5448
+ continue;
5449
+ }
5450
+ if (collector.domain) {
5451
+ const domainProjector = collector.domain;
5452
+ const dependencies = collector.dependsOn ?? [];
5453
+ if (options.includeEligibleTargets === false && !Object.prototype.hasOwnProperty.call(eligibleTargets, key)) {
5454
+ const lazyDomain2 = lazyTargetDomainFromCollectorMetadata(
5455
+ key,
5456
+ collector,
5457
+ dependencies
5458
+ );
5459
+ if (lazyDomain2) {
5460
+ result[key] = lazyDomain2;
5461
+ continue;
5462
+ }
5463
+ }
5464
+ const baseDomain = withSelection(
5465
+ domainProjector(
5466
+ domainState,
5467
+ playerId,
5468
+ queries(),
5469
+ derived(),
5470
+ {}
5471
+ ),
5472
+ collector
5473
+ );
5474
+ if (dependencies.length === 0) {
5475
+ result[key] = baseDomain;
5476
+ continue;
5477
+ }
5478
+ const caseCount = dependencyCaseCount(dependencies, result);
5479
+ const lazyDomain = caseCount > MAX_EAGER_DEPENDENT_DOMAIN_CASES ? toLazyTargetDomain(key, baseDomain, dependencies) : null;
5480
+ if (lazyDomain) {
5481
+ result[key] = lazyDomain;
5482
+ continue;
5483
+ }
5484
+ const dependentCases = cartesianDependencyTuples(
5485
+ dependencies,
5486
+ result
5487
+ ).map((values) => ({
5488
+ when: values,
5489
+ domain: withoutDependentProjection(
5490
+ withSelection(
5491
+ domainProjector(
5492
+ domainState,
5493
+ playerId,
5494
+ queries(),
5495
+ derived(),
5496
+ values
5497
+ ),
5498
+ collector
5499
+ )
5500
+ )
5501
+ }));
5502
+ result[key] = {
5503
+ ...baseDomain,
5504
+ dependencies: {
5505
+ mode: "eager",
5506
+ dependentCases
5507
+ }
5564
5508
  };
5509
+ continue;
5565
5510
  }
5566
- return { mode: "active" };
5511
+ throw unsupportedDefaultInputError(key, collector);
5567
5512
  }
5568
- function isActorAuthorized(state, playerId, authorization) {
5569
- if (authorization.mode === "addressees") {
5570
- return authorization.addressees.has(playerId);
5513
+ return result;
5514
+ }
5515
+ function collectInteractionInputs(interaction, domainState, playerId, options = {}) {
5516
+ const collectors = interactionInputsOf(interaction);
5517
+ const dependencyKeys = new Set(
5518
+ Object.values(collectors).flatMap((collector) => [
5519
+ ...collector.dependsOn ?? []
5520
+ ])
5521
+ );
5522
+ const collectedEligibleTargets = options.includeEligibleTargets === false && dependencyKeys.size === 0 ? {} : collectEligibleTargets(interaction, domainState, playerId, options);
5523
+ const eligibleTargets = options.includeEligibleTargets === false ? Object.fromEntries(
5524
+ Object.entries(collectedEligibleTargets).filter(
5525
+ ([key]) => dependencyKeys.has(key)
5526
+ )
5527
+ ) : collectedEligibleTargets;
5528
+ const domainsByKey = collectInputDomains(
5529
+ interaction,
5530
+ domainState,
5531
+ playerId,
5532
+ eligibleTargets,
5533
+ options
5534
+ );
5535
+ let queriesLazy = options.queries ?? null;
5536
+ const queries = () => queriesLazy ??= createStateQueries(
5537
+ domainState
5538
+ );
5539
+ let derivedLazy = options.derived ?? null;
5540
+ const derived = () => derivedLazy ??= createDerivedResolver(domainState, { q: queries() });
5541
+ return Object.entries(collectors).flatMap(([key, collector]) => {
5542
+ if (collector.kind === "rng") {
5543
+ return [];
5571
5544
  }
5572
- if (authorization.mode === "actors") {
5573
- return authorization.actors.has(playerId);
5545
+ const domain = domainsByKey[key];
5546
+ if (!domain && collector.kind === "prompt") {
5547
+ return [];
5574
5548
  }
5575
- const active = state.flow.activePlayers;
5576
- if (active.length === 0) {
5577
- return true;
5549
+ if (!domain) {
5550
+ throw unsupportedDefaultInputError(key, collector);
5578
5551
  }
5579
- return active.includes(playerId);
5552
+ const dynamicDefault = collector.resolveDefaultValue?.(
5553
+ domainState,
5554
+ playerId,
5555
+ queries(),
5556
+ derived(),
5557
+ domain
5558
+ );
5559
+ const defaultValue = "defaultValue" in collector ? collector.defaultValue : dynamicDefault;
5560
+ warnConcreteDependentChoiceDefault({
5561
+ inputKey: key,
5562
+ collector,
5563
+ domain,
5564
+ defaultValue,
5565
+ dependencyKeys
5566
+ });
5567
+ return [
5568
+ {
5569
+ key,
5570
+ kind: collector.kind,
5571
+ domain,
5572
+ ...defaultValue !== void 0 ? { defaultValue } : {}
5573
+ }
5574
+ ];
5575
+ });
5576
+ }
5577
+ var concreteDependentChoiceDefaultWarnings = /* @__PURE__ */ new Set();
5578
+ var AUTHORING_WARNINGS_FLAG = "__DREAMBOARD_AUTHORING_WARNINGS__";
5579
+ function shouldEmitAuthoringWarning() {
5580
+ return globalThis[AUTHORING_WARNINGS_FLAG] === true;
5581
+ }
5582
+ function warnConcreteDependentChoiceDefault({
5583
+ inputKey,
5584
+ collector,
5585
+ domain,
5586
+ defaultValue,
5587
+ dependencyKeys
5588
+ }) {
5589
+ if (!shouldEmitAuthoringWarning() || !dependencyKeys.has(inputKey) || collector.kind !== "form" || domain.type !== "choice" || defaultValue === void 0 || defaultValue === null) {
5590
+ return;
5580
5591
  }
5581
- function isInteractionVisible(interaction, authorization, authorized) {
5582
- return !(authorization.mode === "addressees" && !authorized) && !(interaction.visibility === "actorsOnly" && !authorized);
5592
+ const warningKey = `${inputKey}:${String(defaultValue)}`;
5593
+ if (concreteDependentChoiceDefaultWarnings.has(warningKey)) {
5594
+ return;
5583
5595
  }
5584
- return {
5585
- isActorAuthorized,
5586
- isInteractionVisible,
5587
- resolveInteractionActorAuthorization
5588
- };
5589
- }
5590
-
5591
- // src/reducer/bundle/trusted/interaction-decision.ts
5592
- function issueFromRule(rule) {
5593
- return { errorCode: rule.errorCode, message: rule.message };
5596
+ concreteDependentChoiceDefaultWarnings.add(warningKey);
5597
+ console.warn(
5598
+ `[dreamboard] Form choice input "${inputKey}" feeds another collector and defaulted to "${String(
5599
+ defaultValue
5600
+ )}". Dependent choices that select the object an action applies to should usually use defaultValue: () => undefined so collection stays explicit.`
5601
+ );
5594
5602
  }
5595
- function issueFromRuleValidationResult(rule, result) {
5596
- if (result === false) return issueFromRule(rule);
5597
- if (result && typeof result === "object") return result;
5603
+ function collectPromptOptions(interaction, domainState, playerId, queries) {
5604
+ for (const collector of Object.values(interaction.inputs)) {
5605
+ if (collector.kind !== "prompt") continue;
5606
+ const factory = collector.meta?.options;
5607
+ if (!factory) continue;
5608
+ return factory(domainState, playerId, queries).map((option) => ({
5609
+ id: String(option.id),
5610
+ ...option.label !== void 0 ? { label: option.label } : {}
5611
+ }));
5612
+ }
5598
5613
  return void 0;
5599
5614
  }
5615
+
5616
+ // src/reducer/bundle/trusted/interaction-descriptor.ts
5600
5617
  function humanizeInteractionId(id) {
5601
5618
  if (!id) return id;
5602
5619
  const withSpaces = id.replace(/[-_]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").trim();
@@ -5738,6 +5755,78 @@ function interactionAvailabilityFromDecision(decision) {
5738
5755
  reason: decision.unavailableReason ?? "Interaction unavailable"
5739
5756
  };
5740
5757
  }
5758
+ function buildInteractionDescriptor(scope, state, playerId, interactionId, interaction, decision, options = {}) {
5759
+ const domainState = options.projection?.domainState ?? scope.toDomainState(state);
5760
+ const phaseName = state.flow.currentPhase;
5761
+ const interactionInputs = interactionInputsOf(interaction);
5762
+ const metadata = projectInteractionMetadata({
5763
+ ...interaction,
5764
+ inputs: interactionInputs
5765
+ });
5766
+ const queries = options.projection?.q ?? createStateQueries(domainState);
5767
+ const derived = options.projection?.derived;
5768
+ const shouldMaterializeInputDomains = decision.available || decision.unavailableReason !== "Not your turn";
5769
+ const promptContext = metadata.kind === "prompt" ? {
5770
+ to: playerId,
5771
+ title: humanizeInteractionId(interactionId),
5772
+ options: shouldMaterializeInputDomains ? collectPromptOptions(
5773
+ { inputs: interactionInputs },
5774
+ domainState,
5775
+ playerId,
5776
+ queries
5777
+ ) : void 0
5778
+ } : void 0;
5779
+ const inputs = shouldMaterializeInputDomains ? enrichResourceInputPresentation(
5780
+ collectInteractionInputs(interaction, domainState, playerId, {
5781
+ queries,
5782
+ derived,
5783
+ eligibleTargetCache: options.projection?.eligibleTargets,
5784
+ eligibleTargetCachePrefix: `${phaseName}:${String(
5785
+ interactionId
5786
+ )}:${String(playerId)}`,
5787
+ includeEligibleTargets: options.includeEligibleTargets
5788
+ }),
5789
+ scope.definition.contract.manifest
5790
+ ) : [];
5791
+ const baseDescriptor = {
5792
+ phaseName,
5793
+ interactionKey: `${phaseName}.${interactionId}`,
5794
+ interactionId,
5795
+ commit: metadata.commit,
5796
+ zoneId: collectFirstCardZoneId(interaction),
5797
+ zoneIds: collectCardZoneIds(interaction),
5798
+ inputs,
5799
+ cost: decision.cost ? { ...decision.cost } : void 0,
5800
+ currentResources: decision.cost ? {
5801
+ ...queries.player.resources(playerId)
5802
+ } : void 0,
5803
+ availability: interactionAvailabilityFromDecision(decision)
5804
+ };
5805
+ if (metadata.kind === "prompt") {
5806
+ return {
5807
+ ...baseDescriptor,
5808
+ kind: "prompt",
5809
+ context: promptContext ?? {
5810
+ to: playerId,
5811
+ title: humanizeInteractionId(interactionId)
5812
+ }
5813
+ };
5814
+ }
5815
+ return {
5816
+ ...baseDescriptor,
5817
+ kind: "action"
5818
+ };
5819
+ }
5820
+
5821
+ // src/reducer/bundle/trusted/interaction-decision.ts
5822
+ function issueFromRule(rule) {
5823
+ return { errorCode: rule.errorCode, message: rule.message };
5824
+ }
5825
+ function issueFromRuleValidationResult(rule, result) {
5826
+ if (result === false) return issueFromRule(rule);
5827
+ if (result && typeof result === "object") return result;
5828
+ return void 0;
5829
+ }
5741
5830
  function createInteractionDecisionResolver(scope, stages, authorization) {
5742
5831
  function evaluateInteractionCost(state, interaction, playerId, params, projection) {
5743
5832
  if (!interaction.cost) {
@@ -5760,68 +5849,6 @@ function createInteractionDecisionResolver(scope, stages, authorization) {
5760
5849
  return { error };
5761
5850
  }
5762
5851
  }
5763
- function buildBaseDescriptor(state, playerId, interactionId, interaction, decision, options = {}) {
5764
- const domainState = options.projection?.domainState ?? scope.toDomainState(state);
5765
- const phaseName = state.flow.currentPhase;
5766
- const interactionInputs = interactionInputsOf(interaction);
5767
- const metadata = projectInteractionMetadata({
5768
- ...interaction,
5769
- inputs: interactionInputs
5770
- });
5771
- const queries = options.projection?.q ?? createStateQueries(domainState);
5772
- const derived = options.projection?.derived;
5773
- const shouldMaterializeInputDomains = decision.available || decision.unavailableReason !== "Not your turn";
5774
- const promptContext = metadata.kind === "prompt" ? {
5775
- to: playerId,
5776
- title: humanizeInteractionId(interactionId),
5777
- options: shouldMaterializeInputDomains ? collectPromptOptions(
5778
- { inputs: interactionInputs },
5779
- domainState,
5780
- playerId,
5781
- queries
5782
- ) : void 0
5783
- } : void 0;
5784
- const inputs = shouldMaterializeInputDomains ? enrichResourceInputPresentation(
5785
- collectInteractionInputs(interaction, domainState, playerId, {
5786
- queries,
5787
- derived,
5788
- eligibleTargetCache: options.projection?.eligibleTargets,
5789
- eligibleTargetCachePrefix: `${phaseName}:${String(
5790
- interactionId
5791
- )}:${String(playerId)}`,
5792
- includeEligibleTargets: options.includeEligibleTargets
5793
- }),
5794
- scope.definition.contract.manifest
5795
- ) : [];
5796
- const baseDescriptor = {
5797
- phaseName,
5798
- interactionKey: `${phaseName}.${interactionId}`,
5799
- interactionId,
5800
- commit: metadata.commit,
5801
- zoneId: collectFirstCardZoneId(interaction),
5802
- zoneIds: collectCardZoneIds(interaction),
5803
- inputs,
5804
- cost: decision.cost ? { ...decision.cost } : void 0,
5805
- currentResources: decision.cost ? {
5806
- ...queries.player.resources(playerId)
5807
- } : void 0,
5808
- availability: interactionAvailabilityFromDecision(decision)
5809
- };
5810
- if (metadata.kind === "prompt") {
5811
- return {
5812
- ...baseDescriptor,
5813
- kind: "prompt",
5814
- context: promptContext ?? {
5815
- to: playerId,
5816
- title: humanizeInteractionId(interactionId)
5817
- }
5818
- };
5819
- }
5820
- return {
5821
- ...baseDescriptor,
5822
- kind: "action"
5823
- };
5824
- }
5825
5852
  function resolveInteractionDecision({
5826
5853
  state,
5827
5854
  playerId,
@@ -5856,7 +5883,8 @@ function createInteractionDecisionResolver(scope, stages, authorization) {
5856
5883
  interaction,
5857
5884
  parsedParams: {},
5858
5885
  visible: true,
5859
- descriptor: buildBaseDescriptor(
5886
+ descriptor: buildInteractionDescriptor(
5887
+ scope,
5860
5888
  state,
5861
5889
  playerId,
5862
5890
  trustedInteractionId,
@@ -6030,7 +6058,8 @@ function createInteractionDecisionResolver(scope, stages, authorization) {
6030
6058
  }
6031
6059
  const available = stageAllowed && stepAllowed && authorized && !ruleAvailabilityIssue && costAffordable && !authoredValidation;
6032
6060
  const unavailableReason = available ? void 0 : !authorized ? "Not your turn" : !stageAllowed ? "Interaction not allowed in current stage" : !stepAllowed ? "Interaction not allowed in current step" : ruleAvailabilityIssue ? ruleAvailabilityIssue.message ?? ruleAvailabilityIssue.errorCode : !costAffordable ? "INSUFFICIENT_RESOURCES" : mode === "card" ? authoredValidation?.message ?? "Interaction unavailable" : "Interaction unavailable";
6033
- const descriptor = buildBaseDescriptor(
6061
+ const descriptor = buildInteractionDescriptor(
6062
+ scope,
6034
6063
  state,
6035
6064
  playerId,
6036
6065
  trustedInteractionId,
@@ -6837,14 +6866,18 @@ function createProjectionBuilder(scope, interactions) {
6837
6866
  const byRef = {};
6838
6867
  const byHash = /* @__PURE__ */ new Map();
6839
6868
  return {
6840
- add(descriptor) {
6841
- const fingerprint = stableStringify(descriptor);
6869
+ add(descriptor, actorSeat) {
6870
+ const enriched = descriptorWithBrowserReplayDigests(
6871
+ descriptor,
6872
+ actorSeat
6873
+ );
6874
+ const fingerprint = stableStringify(enriched);
6842
6875
  const existing = byHash.get(fingerprint);
6843
6876
  if (existing) return existing;
6844
- const base = typeof descriptor.interactionId === "string" && descriptor.interactionId.length > 0 ? descriptor.interactionId : "interaction";
6877
+ const base = typeof enriched.interactionId === "string" && enriched.interactionId.length > 0 ? enriched.interactionId : "interaction";
6845
6878
  const ref = `${base}:${fnv1a64(fingerprint)}`;
6846
6879
  byHash.set(fingerprint, ref);
6847
- byRef[ref] = descriptor;
6880
+ byRef[ref] = enriched;
6848
6881
  return ref;
6849
6882
  },
6850
6883
  entries() {
@@ -6852,7 +6885,7 @@ function createProjectionBuilder(scope, interactions) {
6852
6885
  }
6853
6886
  };
6854
6887
  }
6855
- function resolveZoneHandlesFor(combinedState, playerId, projection, registry) {
6888
+ function resolveZoneHandlesFor(combinedState, playerId, actorSeat, projection, registry) {
6856
6889
  const phaseName = combinedState.flow.currentPhase;
6857
6890
  const zoneIds = new Set(scope.zonesForPhase(phaseName).map(String));
6858
6891
  for (const [, interaction] of scope.interactionEntriesForPhase(phaseName)) {
@@ -6907,10 +6940,13 @@ function createProjectionBuilder(scope, interactions) {
6907
6940
  continue;
6908
6941
  }
6909
6942
  perCard.push(
6910
- registry.add({
6911
- ...decision.descriptor,
6912
- zoneId
6913
- })
6943
+ registry.add(
6944
+ {
6945
+ ...decision.descriptor,
6946
+ zoneId
6947
+ },
6948
+ actorSeat
6949
+ )
6914
6950
  );
6915
6951
  }
6916
6952
  playableByCardId[cardId] = perCard;
@@ -6990,16 +7026,17 @@ function createProjectionBuilder(scope, interactions) {
6990
7026
  });
6991
7027
  const registry = createDescriptorRegistry();
6992
7028
  const seats = {};
6993
- for (const playerId of playerIds) {
7029
+ for (const [actorSeat, playerId] of playerIds.entries()) {
6994
7030
  const availableInteractionRefs = interactions.resolveAvailableInteractionsFor(combinedState, playerId, {
6995
7031
  projection
6996
- }).map((descriptor) => registry.add(descriptor));
7032
+ }).map((descriptor) => registry.add(descriptor, actorSeat));
6997
7033
  seats[playerId] = {
6998
7034
  ...projectionMode === "full" ? {
6999
7035
  view: resolveViewFor(combinedState, playerId, viewId, projection),
7000
7036
  zones: resolveZoneHandlesFor(
7001
7037
  combinedState,
7002
7038
  playerId,
7039
+ actorSeat,
7003
7040
  projection,
7004
7041
  registry
7005
7042
  )
@@ -7036,6 +7073,126 @@ function createProjectionBuilder(scope, interactions) {
7036
7073
  resolveZoneHandlesFor
7037
7074
  };
7038
7075
  }
7076
+ function descriptorWithBrowserReplayDigests(descriptor, actorSeat) {
7077
+ const descriptorDigestValue = descriptor.descriptorDigest ?? interactionDescriptorDigest(descriptor);
7078
+ return {
7079
+ ...descriptor,
7080
+ descriptorDigest: descriptorDigestValue,
7081
+ actorSeat,
7082
+ draftDigest: descriptor.draftDigest ?? interactionDraftDigest({
7083
+ actorSeat,
7084
+ descriptor,
7085
+ descriptorDigest: descriptorDigestValue
7086
+ })
7087
+ };
7088
+ }
7089
+ function interactionDescriptorDigest(descriptor) {
7090
+ return hashJson({
7091
+ commitMode: descriptor.commit.mode,
7092
+ defaults: toDescriptorDigestJson(defaultsForDescriptor(descriptor)),
7093
+ inputKeys: descriptor.inputs.map((input) => input.key),
7094
+ inputs: descriptor.inputs.map((input) => ({
7095
+ key: input.key,
7096
+ kind: input.kind,
7097
+ domain: toDescriptorDigestJson(input.domain),
7098
+ defaultValue: input.defaultValue === void 0 ? null : toDescriptorDigestJson(input.defaultValue)
7099
+ })),
7100
+ interactionId: descriptor.interactionId,
7101
+ interactionKey: descriptor.interactionKey,
7102
+ stableIdentity: `${descriptor.interactionKey}:${descriptor.interactionId}`
7103
+ });
7104
+ }
7105
+ function interactionDraftDigest({
7106
+ actorSeat,
7107
+ descriptor,
7108
+ descriptorDigest
7109
+ }) {
7110
+ return hashJson({
7111
+ digestVersion: "interaction-draft@2",
7112
+ actorSeat,
7113
+ descriptorDigest,
7114
+ emitted: false,
7115
+ interactionId: descriptor.interactionId,
7116
+ interactionKey: descriptor.interactionKey,
7117
+ values: defaultsForDescriptor(descriptor)
7118
+ });
7119
+ }
7120
+ function toDescriptorDigestJson(value) {
7121
+ const canonical = toCanonicalJson(value);
7122
+ return normalizeOrderInsensitiveDescriptorFields(canonical);
7123
+ }
7124
+ function normalizeOrderInsensitiveDescriptorFields(value) {
7125
+ if (Array.isArray(value)) {
7126
+ return value.map(normalizeOrderInsensitiveDescriptorFields);
7127
+ }
7128
+ if (value === null || typeof value !== "object") {
7129
+ return value;
7130
+ }
7131
+ return Object.fromEntries(
7132
+ Object.entries(value).map(([key, item]) => {
7133
+ const normalized = normalizeOrderInsensitiveDescriptorFields(item);
7134
+ if ((key === "eligibleTargets" || key === "dependentCases") && Array.isArray(normalized)) {
7135
+ return [
7136
+ key,
7137
+ [...normalized].sort(
7138
+ (left, right) => compareCanonicalJson(canonicalJson(left), canonicalJson(right))
7139
+ )
7140
+ ];
7141
+ }
7142
+ return [key, normalized];
7143
+ })
7144
+ );
7145
+ }
7146
+ function defaultsForDescriptor(descriptor) {
7147
+ return Object.fromEntries(
7148
+ descriptor.inputs.flatMap(
7149
+ (input) => input.defaultValue === void 0 ? [] : [[input.key, toCanonicalJson(input.defaultValue)]]
7150
+ )
7151
+ );
7152
+ }
7153
+ function toCanonicalJson(value) {
7154
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
7155
+ return value;
7156
+ }
7157
+ if (typeof value === "number") {
7158
+ return value;
7159
+ }
7160
+ if (Array.isArray(value)) {
7161
+ return value.map((item) => toCanonicalJson(item));
7162
+ }
7163
+ if (typeof value === "object") {
7164
+ return Object.fromEntries(
7165
+ Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => [key, toCanonicalJson(item)])
7166
+ );
7167
+ }
7168
+ return null;
7169
+ }
7170
+ function hashJson(value) {
7171
+ return `sha256:${sha256Hex(canonicalJson(value))}`;
7172
+ }
7173
+ function canonicalJson(value) {
7174
+ return JSON.stringify(canonicalizeJson(value));
7175
+ }
7176
+ function canonicalizeJson(value) {
7177
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
7178
+ return value;
7179
+ }
7180
+ if (typeof value === "number") {
7181
+ if (!Number.isFinite(value)) {
7182
+ throw new Error("canonical JSON contains a non-finite number");
7183
+ }
7184
+ return value;
7185
+ }
7186
+ if (Array.isArray(value)) {
7187
+ return value.map((item) => canonicalizeJson(item));
7188
+ }
7189
+ return Object.fromEntries(
7190
+ Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, canonicalizeJson(item)])
7191
+ );
7192
+ }
7193
+ function compareCanonicalJson(left, right) {
7194
+ return left < right ? -1 : left > right ? 1 : 0;
7195
+ }
7039
7196
  function stableStringify(value) {
7040
7197
  if (value === null || typeof value !== "object") {
7041
7198
  return JSON.stringify(value);
@@ -7055,6 +7212,171 @@ function fnv1a64(value) {
7055
7212
  }
7056
7213
  return hash.toString(16).padStart(16, "0");
7057
7214
  }
7215
+ var SHA256_K = [
7216
+ 1116352408,
7217
+ 1899447441,
7218
+ 3049323471,
7219
+ 3921009573,
7220
+ 961987163,
7221
+ 1508970993,
7222
+ 2453635748,
7223
+ 2870763221,
7224
+ 3624381080,
7225
+ 310598401,
7226
+ 607225278,
7227
+ 1426881987,
7228
+ 1925078388,
7229
+ 2162078206,
7230
+ 2614888103,
7231
+ 3248222580,
7232
+ 3835390401,
7233
+ 4022224774,
7234
+ 264347078,
7235
+ 604807628,
7236
+ 770255983,
7237
+ 1249150122,
7238
+ 1555081692,
7239
+ 1996064986,
7240
+ 2554220882,
7241
+ 2821834349,
7242
+ 2952996808,
7243
+ 3210313671,
7244
+ 3336571891,
7245
+ 3584528711,
7246
+ 113926993,
7247
+ 338241895,
7248
+ 666307205,
7249
+ 773529912,
7250
+ 1294757372,
7251
+ 1396182291,
7252
+ 1695183700,
7253
+ 1986661051,
7254
+ 2177026350,
7255
+ 2456956037,
7256
+ 2730485921,
7257
+ 2820302411,
7258
+ 3259730800,
7259
+ 3345764771,
7260
+ 3516065817,
7261
+ 3600352804,
7262
+ 4094571909,
7263
+ 275423344,
7264
+ 430227734,
7265
+ 506948616,
7266
+ 659060556,
7267
+ 883997877,
7268
+ 958139571,
7269
+ 1322822218,
7270
+ 1537002063,
7271
+ 1747873779,
7272
+ 1955562222,
7273
+ 2024104815,
7274
+ 2227730452,
7275
+ 2361852424,
7276
+ 2428436474,
7277
+ 2756734187,
7278
+ 3204031479,
7279
+ 3329325298
7280
+ ];
7281
+ function sha256Hex(input) {
7282
+ const bytes = utf8Bytes(input);
7283
+ const bitLength = bytes.length * 8;
7284
+ bytes.push(128);
7285
+ while (bytes.length % 64 !== 56) {
7286
+ bytes.push(0);
7287
+ }
7288
+ for (let shift = 56; shift >= 0; shift -= 8) {
7289
+ bytes.push(Math.floor(bitLength / 2 ** shift) & 255);
7290
+ }
7291
+ let h0 = 1779033703;
7292
+ let h1 = 3144134277;
7293
+ let h2 = 1013904242;
7294
+ let h3 = 2773480762;
7295
+ let h4 = 1359893119;
7296
+ let h5 = 2600822924;
7297
+ let h6 = 528734635;
7298
+ let h7 = 1541459225;
7299
+ const words = new Array(64);
7300
+ for (let offset = 0; offset < bytes.length; offset += 64) {
7301
+ for (let index = 0; index < 16; index += 1) {
7302
+ const base = offset + index * 4;
7303
+ words[index] = (bytes[base] << 24 | bytes[base + 1] << 16 | bytes[base + 2] << 8 | bytes[base + 3]) >>> 0;
7304
+ }
7305
+ for (let index = 16; index < 64; index += 1) {
7306
+ const s0 = rotateRight(words[index - 15], 7) ^ rotateRight(words[index - 15], 18) ^ words[index - 15] >>> 3;
7307
+ const s1 = rotateRight(words[index - 2], 17) ^ rotateRight(words[index - 2], 19) ^ words[index - 2] >>> 10;
7308
+ words[index] = words[index - 16] + s0 + words[index - 7] + s1 >>> 0;
7309
+ }
7310
+ let a = h0;
7311
+ let b = h1;
7312
+ let c = h2;
7313
+ let d = h3;
7314
+ let e = h4;
7315
+ let f = h5;
7316
+ let g = h6;
7317
+ let h = h7;
7318
+ for (let index = 0; index < 64; index += 1) {
7319
+ const s1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
7320
+ const ch = e & f ^ ~e & g;
7321
+ const temp1 = h + s1 + ch + SHA256_K[index] + words[index] >>> 0;
7322
+ const s0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
7323
+ const maj = a & b ^ a & c ^ b & c;
7324
+ const temp2 = s0 + maj >>> 0;
7325
+ h = g;
7326
+ g = f;
7327
+ f = e;
7328
+ e = d + temp1 >>> 0;
7329
+ d = c;
7330
+ c = b;
7331
+ b = a;
7332
+ a = temp1 + temp2 >>> 0;
7333
+ }
7334
+ h0 = h0 + a >>> 0;
7335
+ h1 = h1 + b >>> 0;
7336
+ h2 = h2 + c >>> 0;
7337
+ h3 = h3 + d >>> 0;
7338
+ h4 = h4 + e >>> 0;
7339
+ h5 = h5 + f >>> 0;
7340
+ h6 = h6 + g >>> 0;
7341
+ h7 = h7 + h >>> 0;
7342
+ }
7343
+ return [h0, h1, h2, h3, h4, h5, h6, h7].map((word) => word.toString(16).padStart(8, "0")).join("");
7344
+ }
7345
+ function rotateRight(value, shift) {
7346
+ return value >>> shift | value << 32 - shift;
7347
+ }
7348
+ function utf8Bytes(input) {
7349
+ const bytes = [];
7350
+ for (let index = 0; index < input.length; index += 1) {
7351
+ let codePoint = input.charCodeAt(index);
7352
+ if (codePoint >= 55296 && codePoint <= 56319 && index + 1 < input.length) {
7353
+ const low = input.charCodeAt(index + 1);
7354
+ if (low >= 56320 && low <= 57343) {
7355
+ codePoint = 65536 + (codePoint - 55296 << 10) + (low - 56320);
7356
+ index += 1;
7357
+ }
7358
+ }
7359
+ if (codePoint < 128) {
7360
+ bytes.push(codePoint);
7361
+ } else if (codePoint < 2048) {
7362
+ bytes.push(192 | codePoint >>> 6, 128 | codePoint & 63);
7363
+ } else if (codePoint < 65536) {
7364
+ bytes.push(
7365
+ 224 | codePoint >>> 12,
7366
+ 128 | codePoint >>> 6 & 63,
7367
+ 128 | codePoint & 63
7368
+ );
7369
+ } else {
7370
+ bytes.push(
7371
+ 240 | codePoint >>> 18,
7372
+ 128 | codePoint >>> 12 & 63,
7373
+ 128 | codePoint >>> 6 & 63,
7374
+ 128 | codePoint & 63
7375
+ );
7376
+ }
7377
+ }
7378
+ return bytes;
7379
+ }
7058
7380
 
7059
7381
  // src/reducer/bundle/trusted/static-projection.ts
7060
7382
  function stableStringify2(value) {
@@ -7475,7 +7797,7 @@ function createIngressRuntimeCodec(definition) {
7475
7797
  },
7476
7798
  parseState(rawState) {
7477
7799
  const envelope = safeParseOrThrow(
7478
- ReducerSessionStateSchema,
7800
+ zod_exports.ReducerSessionStateSchema,
7479
7801
  rawState,
7480
7802
  "state"
7481
7803
  );
@@ -7555,44 +7877,6 @@ function createIngressRuntimeCodec(definition) {
7555
7877
  };
7556
7878
  }
7557
7879
 
7558
- // src/generated/reducer-contract/builders.ts
7559
- function createEffectIdMinter() {
7560
- let i = 0;
7561
- return () => `ef${i++}`;
7562
- }
7563
- function createEffectBuilders(mint) {
7564
- return {
7565
- transition(args, continuation) {
7566
- const effect = { effectId: mint(), type: "transition", to: args.to };
7567
- return continuation === void 0 ? effect : Object.assign(effect, { __continuation: continuation });
7568
- },
7569
- rollDie(args, continuation) {
7570
- const effect = { effectId: mint(), type: "rollDie", dieId: args.dieId };
7571
- return continuation === void 0 ? effect : Object.assign(effect, { __continuation: continuation });
7572
- },
7573
- shuffleSharedZone(args, continuation) {
7574
- const effect = { effectId: mint(), type: "shuffleSharedZone", zoneId: args.zoneId };
7575
- return continuation === void 0 ? effect : Object.assign(effect, { __continuation: continuation });
7576
- },
7577
- shufflePlayerZone(args, continuation) {
7578
- const effect = { effectId: mint(), type: "shufflePlayerZone", zoneId: args.zoneId, playerId: args.playerId };
7579
- return continuation === void 0 ? effect : Object.assign(effect, { __continuation: continuation });
7580
- }
7581
- };
7582
- }
7583
- function materializeAccept(pending) {
7584
- const effects = [];
7585
- const continuations = {};
7586
- for (const pe of pending) {
7587
- const { __continuation, ...effect } = pe;
7588
- effects.push(effect);
7589
- if (__continuation !== void 0) {
7590
- continuations[effect.effectId] = __continuation;
7591
- }
7592
- }
7593
- return { effects, continuations };
7594
- }
7595
-
7596
7880
  // src/reducer/bundle/ingress-bundle.ts
7597
7881
  function routeInteraction(input) {
7598
7882
  return {
@@ -7649,12 +7933,12 @@ function toWireReduceResult(result, serializeState) {
7649
7933
  message: result.message
7650
7934
  };
7651
7935
  }
7652
- const mint = createEffectIdMinter();
7653
- const fx = createEffectBuilders(mint);
7936
+ const mint = builders_exports.createEffectIdMinter();
7937
+ const fx = builders_exports.createEffectBuilders(mint);
7654
7938
  const pending = (result.instructions ?? []).map(
7655
7939
  (instruction) => toPendingEffect(instruction, fx)
7656
7940
  );
7657
- const { effects, continuations } = materializeAccept(pending);
7941
+ const { effects, continuations } = builders_exports.materializeAccept(pending);
7658
7942
  return {
7659
7943
  kind: "accept",
7660
7944
  state: serializeState(result.state),
@@ -7670,8 +7954,8 @@ function toWireDispatchResult(result, serializeState) {
7670
7954
  message: result.message
7671
7955
  };
7672
7956
  }
7673
- const mint = createEffectIdMinter();
7674
- const fx = createEffectBuilders(mint);
7957
+ const mint = builders_exports.createEffectIdMinter();
7958
+ const fx = builders_exports.createEffectBuilders(mint);
7675
7959
  const trace = [];
7676
7960
  for (const entry of result.trace) {
7677
7961
  switch (entry.type) {
@@ -7704,7 +7988,7 @@ function toWireDispatchResult(result, serializeState) {
7704
7988
  entry.instruction,
7705
7989
  fx
7706
7990
  );
7707
- const { effects, continuations } = materializeAccept([
7991
+ const { effects, continuations } = builders_exports.materializeAccept([
7708
7992
  pending
7709
7993
  ]);
7710
7994
  const [wireEffect] = effects;