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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +186 -45
  2. package/dist/board-target-B0QG6OYx.d.ts +8 -0
  3. package/dist/chunk-3JXUL4VN.js +139 -0
  4. package/dist/chunk-3JXUL4VN.js.map +1 -0
  5. package/dist/{chunk-S2JEDVWH.js → chunk-C77SYIVT.js} +6102 -6505
  6. package/dist/chunk-C77SYIVT.js.map +1 -0
  7. package/dist/chunk-DMFVAZI2.js +16 -0
  8. package/dist/chunk-DMFVAZI2.js.map +1 -0
  9. package/dist/chunk-OOFMBCFF.js +1338 -0
  10. package/dist/chunk-OOFMBCFF.js.map +1 -0
  11. package/dist/chunk-UG2Q4NLG.js +590 -0
  12. package/dist/chunk-UG2Q4NLG.js.map +1 -0
  13. package/dist/chunk-XU6WJW6K.js +847 -0
  14. package/dist/chunk-XU6WJW6K.js.map +1 -0
  15. package/dist/diagnostics-B8d5aLad.d.ts +65 -0
  16. package/dist/extract-gVKcB8Og.d.ts +4736 -0
  17. package/dist/index.d.ts +3782 -1
  18. package/dist/index.js +1096 -6
  19. package/dist/index.js.map +1 -1
  20. package/dist/model-vdpLHrxh.d.ts +432 -0
  21. package/dist/react.d.ts +60 -0
  22. package/dist/react.js +208 -0
  23. package/dist/react.js.map +1 -0
  24. package/dist/reducer.d.ts +1244 -78
  25. package/dist/reducer.js +4204 -121
  26. package/dist/reducer.js.map +1 -1
  27. package/dist/testing.d.ts +520 -564
  28. package/dist/testing.js +2451 -915
  29. package/dist/testing.js.map +1 -1
  30. package/dist/types-Bj5ZLTUV.d.ts +114 -0
  31. package/package.json +40 -142
  32. package/dist/HandView-Bi2lxzQP.d.ts +0 -79
  33. package/dist/ResourceCounter-BFxJknqp.d.ts +0 -98
  34. package/dist/ThemeProvider-CElaL2ik.d.ts +0 -99
  35. package/dist/attributes-DbvyMbXw.d.ts +0 -68
  36. package/dist/browser-interaction.d.ts +0 -910
  37. package/dist/browser-interaction.js +0 -119
  38. package/dist/browser-interaction.js.map +0 -1
  39. package/dist/chunk-32PFKDV7.js +0 -11705
  40. package/dist/chunk-32PFKDV7.js.map +0 -1
  41. package/dist/chunk-3BAD4VC2.js +0 -263
  42. package/dist/chunk-3BAD4VC2.js.map +0 -1
  43. package/dist/chunk-3SKDNDPG.js +0 -104
  44. package/dist/chunk-3SKDNDPG.js.map +0 -1
  45. package/dist/chunk-5YV2WHJ4.js +0 -25
  46. package/dist/chunk-5YV2WHJ4.js.map +0 -1
  47. package/dist/chunk-ANRB4XKA.js +0 -90
  48. package/dist/chunk-ANRB4XKA.js.map +0 -1
  49. package/dist/chunk-BI4G3PM2.js +0 -1864
  50. package/dist/chunk-BI4G3PM2.js.map +0 -1
  51. package/dist/chunk-EVCLTY4Q.js +0 -935
  52. package/dist/chunk-EVCLTY4Q.js.map +0 -1
  53. package/dist/chunk-GDPKTBUR.js +0 -564
  54. package/dist/chunk-GDPKTBUR.js.map +0 -1
  55. package/dist/chunk-H6VDGFL5.js +0 -2903
  56. package/dist/chunk-H6VDGFL5.js.map +0 -1
  57. package/dist/chunk-LR3ZTWQF.js +0 -5960
  58. package/dist/chunk-LR3ZTWQF.js.map +0 -1
  59. package/dist/chunk-PF7L4BMG.js +0 -117
  60. package/dist/chunk-PF7L4BMG.js.map +0 -1
  61. package/dist/chunk-PN5O6GG2.js +0 -32
  62. package/dist/chunk-PN5O6GG2.js.map +0 -1
  63. package/dist/chunk-PZ5AY32C.js +0 -10
  64. package/dist/chunk-PZ5AY32C.js.map +0 -1
  65. package/dist/chunk-S2JEDVWH.js.map +0 -1
  66. package/dist/chunk-T3ZKNUZ7.js +0 -1
  67. package/dist/chunk-T3ZKNUZ7.js.map +0 -1
  68. package/dist/chunk-UGH54WZ2.js +0 -223
  69. package/dist/chunk-UGH54WZ2.js.map +0 -1
  70. package/dist/chunk-VCDWP5VL.js +0 -746
  71. package/dist/chunk-VCDWP5VL.js.map +0 -1
  72. package/dist/chunk-VDXOF4FW.js +0 -69
  73. package/dist/chunk-VDXOF4FW.js.map +0 -1
  74. package/dist/chunk-WHR5UW3F.js +0 -1988
  75. package/dist/chunk-WHR5UW3F.js.map +0 -1
  76. package/dist/chunk-Y75CFE77.js +0 -17
  77. package/dist/chunk-Y75CFE77.js.map +0 -1
  78. package/dist/chunk-Z7QCREWI.js +0 -3435
  79. package/dist/chunk-Z7QCREWI.js.map +0 -1
  80. package/dist/chunk-ZZGN2ATS.js +0 -1941
  81. package/dist/chunk-ZZGN2ATS.js.map +0 -1
  82. package/dist/components-gAbIz7hK.d.ts +0 -1284
  83. package/dist/definitions-BgmV_GhC.d.ts +0 -300
  84. package/dist/diagnostics-DIi3ina7.d.ts +0 -50
  85. package/dist/digest.d.ts +0 -8
  86. package/dist/game-CsZScpcn.d.ts +0 -763
  87. package/dist/hex-board-view-aKkblDp6.d.ts +0 -1228
  88. package/dist/index-CcT9Q7N7.d.ts +0 -193
  89. package/dist/index.d-DjzoK7zn.d.ts +0 -2101
  90. package/dist/package-set.d.ts +0 -13
  91. package/dist/package-set.js +0 -12
  92. package/dist/package-set.js.map +0 -1
  93. package/dist/player-state-Cqpyeql0.d.ts +0 -371
  94. package/dist/plugin-runtime-contract.d.ts +0 -17
  95. package/dist/plugin-runtime-contract.js +0 -92
  96. package/dist/plugin-runtime-contract.js.map +0 -1
  97. package/dist/primitive-props-BNHDkgd7.d.ts +0 -16
  98. package/dist/protocol-dYgafTYY.d.ts +0 -314
  99. package/dist/reducer/advanced.d.ts +0 -82
  100. package/dist/reducer/advanced.js +0 -51
  101. package/dist/reducer/advanced.js.map +0 -1
  102. package/dist/reducer-contract.d.ts +0 -11
  103. package/dist/reducer-contract.js +0 -16
  104. package/dist/reducer-contract.js.map +0 -1
  105. package/dist/reference-games/index.d.ts +0 -31
  106. package/dist/reference-games/index.js +0 -48
  107. package/dist/reference-games/index.js.map +0 -1
  108. package/dist/runtime/primitives.d.ts +0 -250
  109. package/dist/runtime/primitives.js +0 -189
  110. package/dist/runtime/primitives.js.map +0 -1
  111. package/dist/runtime/runtime-api.d.ts +0 -2
  112. package/dist/runtime/runtime-api.js +0 -1
  113. package/dist/runtime/runtime-api.js.map +0 -1
  114. package/dist/runtime/workspace-contract.d.ts +0 -392
  115. package/dist/runtime/workspace-contract.js +0 -28
  116. package/dist/runtime/workspace-contract.js.map +0 -1
  117. package/dist/runtime-RJ5orDJU.d.ts +0 -1699
  118. package/dist/runtime-api-Bz5pwNU_.d.ts +0 -296
  119. package/dist/runtime-json-CQ9QbLZ5.d.ts +0 -5
  120. package/dist/runtime.d.ts +0 -83
  121. package/dist/runtime.js +0 -242
  122. package/dist/runtime.js.map +0 -1
  123. package/dist/schema.d.ts +0 -379
  124. package/dist/stale-contract-artifact-error-XLaweZtF.d.ts +0 -18
  125. package/dist/testing-compiler.d.ts +0 -37
  126. package/dist/testing-compiler.js +0 -272
  127. package/dist/testing-compiler.js.map +0 -1
  128. package/dist/testing-runtime.d.ts +0 -119
  129. package/dist/testing-runtime.js +0 -212
  130. package/dist/testing-runtime.js.map +0 -1
  131. package/dist/types-DJj5MJkl.d.ts +0 -256
  132. package/dist/types-DR7DoB1x.d.ts +0 -28
  133. package/dist/types-JWCYHmu7.d.ts +0 -122
  134. package/dist/types.d.ts +0 -1958
  135. package/dist/types.js +0 -14
  136. package/dist/types.js.map +0 -1
  137. package/dist/ui/components.d.ts +0 -17
  138. package/dist/ui/components.js +0 -216
  139. package/dist/ui/components.js.map +0 -1
  140. package/dist/ui/defaults.d.ts +0 -19
  141. package/dist/ui/defaults.js +0 -104
  142. package/dist/ui/defaults.js.map +0 -1
  143. package/dist/ui/player-state.d.ts +0 -2
  144. package/dist/ui/player-state.js +0 -1
  145. package/dist/ui/player-state.js.map +0 -1
  146. package/dist/ui/plugin-styles.css +0 -2
  147. package/dist/ui-contract-GTUPkf8z.d.ts +0 -1168
  148. package/dist/ui.d.ts +0 -317
  149. package/dist/ui.js +0 -277
  150. package/dist/ui.js.map +0 -1
  151. package/dist/views-BpuWcOyN.d.ts +0 -1641
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @dreamboard-games/sdk
2
2
 
3
+ [Guides and API reference](../../docs/index.md) · [Registry](../../registry/README.md) · [Examples](../../examples/reference-games/README.md)
4
+
3
5
  The public TypeScript SDK for authoring, testing, and rendering Dreamboard
4
6
  games. Install this package rather than any of the repository's unpublished
5
7
  workspace inputs.
@@ -8,33 +10,15 @@ workspace inputs.
8
10
  pnpm add @dreamboard-games/sdk
9
11
  ```
10
12
 
11
- The package's declarations and export map are the API authority. Supported
12
- imports include the root module and explicit subpaths for authoring, runtime,
13
- reducer contracts, testing, browser interaction, UI, and reference-game
14
- metadata. Import only subpaths present in the installed package's `exports`
15
- field.
16
-
17
- ```ts
18
- import { DREAMBOARD_SDK_VERSION } from "@dreamboard-games/sdk";
19
- import type { ReducerWire } from "@dreamboard-games/sdk/reducer-contract";
20
- import {
21
- REFERENCE_GAME_MANIFEST_SCHEMA_VERSION,
22
- parseReferenceGameManifest,
23
- type ReferenceGameManifest,
24
- } from "@dreamboard-games/sdk/reference-games";
25
-
26
- const manifest: ReferenceGameManifest = parseReferenceGameManifest(input);
27
- console.log(REFERENCE_GAME_MANIFEST_SCHEMA_VERSION, manifest.id);
28
- ```
29
-
30
- Reference-game manifests use schema V5. They describe the game workspace,
31
- teaching purpose, mechanics, UI patterns, and substantive rights metadata.
13
+ The package declarations and export map are the API authority. There are four entry points:
32
14
 
33
- Include the packaged stylesheet when using SDK UI components:
15
+ - `@dreamboard-games/sdk`: framework-free instances, sources, features and canonical host protocol schemas.
16
+ - `@dreamboard-games/sdk/react`: typed React provider, selectors, subscriptions, and dnd-kit card/drop hooks.
17
+ - `@dreamboard-games/sdk/reducer`: game authoring, manifest compilation, execution and trusted worker admission.
18
+ - `@dreamboard-games/sdk/testing`: browser-safe local/scenario sources, replay, inspection and bounded exploration.
34
19
 
35
- ```ts
36
- import "@dreamboard-games/sdk/ui/plugin-styles.css";
37
- ```
20
+ UI components are source-owned registry items installed into your application.
21
+ The SDK contains no styled components or stylesheet.
38
22
 
39
23
  ## Game authoring
40
24
 
@@ -45,14 +29,19 @@ codes. The returned value is three things at once:
45
29
  - the **type leaf**: `typeof game.types.State`, `.ErrorCode`, `.PlayerId`,
46
30
  `.Queries`, `.Tx` (phantom carriers; reading them at runtime throws),
47
31
  - the **factory namespace**: `game.phase(name)`, `phase.define`,
48
- `phase.interaction`, `phase.inputs.*`, `game.views.*`,
49
- - the **assembler**: `game.assemble({ initial, initialPhase, phases, views })`.
32
+ `phase.interaction`, `phase.inputs.*`, `game.view`,
33
+ - the **assembler**: `game.assemble({ initial, initialPhase, phases, view })`.
50
34
 
51
35
  Mutation callbacks (`enter`, `reduce`, `resolve`) receive an open transaction
52
36
  `tx`. Mutate through it and finish with a bare `return` (accept), or with
53
37
  `tx.transition(name)`, `tx.endGame(outcome)`, or `tx.reject(code)`. Events go
54
38
  through `tx.emit(...)`. `state` is the read-only snapshot the callback started
55
- from; `tx.state` is the current draft.
39
+ from; `tx.state` is the current draft. The transaction clones its table once;
40
+ all card, component, resource, and state-slice updates use that draft. Use
41
+ `tx.q` when a query must observe an earlier mutation in the same callback.
42
+ State patch callbacks return a replacement slice without mutating their input.
43
+ The former `ops`, `pipe`, flat `setActivePlayers`, and `tx.apply` APIs are removed;
44
+ call the named transaction methods directly.
56
45
 
57
46
  ```ts
58
47
  // app/game-model.ts — the model, bound once
@@ -114,8 +103,11 @@ export default play.define({
114
103
  });
115
104
  tx.patchPhaseState({ leadCardId: input.params.cardId });
116
105
  const next = q.player.nextInOrder(input.playerId);
117
- if (next) tx.setActivePlayers([next]);
118
- if (q.zone.playerCards(input.playerId, "hand").length === 0) {
106
+ if (next) {
107
+ tx.patchPublicState({ currentPlayerId: next });
108
+ tx.setActivePlayers([next]);
109
+ }
110
+ if (tx.q.zone.playerCards(input.playerId, "hand").length === 0) {
119
111
  return tx.transition("setup");
120
112
  }
121
113
  },
@@ -124,6 +116,42 @@ export default play.define({
124
116
  });
125
117
  ```
126
118
 
119
+ Dependent choices use `phase.steps()` instead of `inputs`. Each accepted command
120
+ commits exactly one current value. Factories receive only earlier parsed
121
+ `selected` values; descriptors expose only the current input. The final commit
122
+ runs complete-parameter validation and the reducer once. Use explicit `null`
123
+ for a no-target choice, and `many(...)` for one atomic multi-selection.
124
+
125
+ ```ts
126
+ const choose = play.interaction({
127
+ steps: play
128
+ .steps()
129
+ .input(
130
+ "kind",
131
+ play.inputs.form.choice({
132
+ choices: [{ value: "single", label: "Single" }],
133
+ defaultValue: () => undefined,
134
+ }),
135
+ )
136
+ .input("count", ({ selected }) =>
137
+ play.inputs.form.number({
138
+ min: 1,
139
+ max: selected.kind === "single" ? 1 : 3,
140
+ defaultValue: 1,
141
+ }),
142
+ ),
143
+ reduce({ input }) {
144
+ // input.params contains both kind and count here.
145
+ },
146
+ });
147
+ ```
148
+
149
+ `rules.available` controls action eligibility; `rules.validate` checks a final
150
+ submission. Accepted state changes reconcile pending prefixes, while phase
151
+ entry clears them. A rejected final submission keeps the prior prefix. The
152
+ actor can cancel an unsealed prefix with `interaction.cancel`, using the same
153
+ transport basis and action identity as submission.
154
+
127
155
  ```ts
128
156
  // app/game.ts — assembly
129
157
  import { game } from "./game-model";
@@ -138,16 +166,11 @@ export default game.assemble({
138
166
  },
139
167
  initialPhase: "setup",
140
168
  phases: { setup, play },
141
- views: {
142
- shared: game.views.empty(),
143
- player: game.views.player({
144
- project: ({ state, playerId, q }) => ({
145
- me: playerId,
146
- hand: q.zone.playerCards(playerId, "hand"),
147
- current: state.publicState.currentPlayerId,
148
- }),
149
- }),
150
- },
169
+ view: game.view(({ state, playerId, q }) => ({
170
+ me: playerId,
171
+ hand: q.zone.playerCards(playerId, "hand"),
172
+ current: state.publicState.currentPlayerId,
173
+ })),
151
174
  });
152
175
  ```
153
176
 
@@ -159,18 +182,69 @@ imports the assembled game, so there is no import cycle.
159
182
  New workspaces keep authored starter code in `app/game.ts` and `ui/App.tsx`.
160
183
  Import the manifest directly. `compileManifest(manifest)` provides inferred ID schemas,
161
184
  table schemas, fresh initial tables, and board metadata in memory. `createGame`
162
- also accepts the authored manifest directly. Bind UI primitives with
163
- `createGameUi(game)` from `@dreamboard-games/sdk/runtime/workspace-contract`.
185
+ also accepts the authored manifest directly. Bind a typed React hook with
186
+ `createGameHook<Game>()({ features, coverage })` from `@dreamboard-games/sdk/react`,
187
+ and pass a source to its `GameProvider`. The hosted UI imports `Game` only as a type;
188
+ `iframeSource()` supplies authoritative frames and handles commands.
164
189
  No authoring generation step or shared workspace files are needed.
165
190
 
191
+ Manifest card counts and explicit piece/die seed counts must be positive safe
192
+ integers. Omitted seed counts mean one copy. Zero, negative, fractional, non-finite,
193
+ and unsafe counts fail before ID expansion; negative, zero, and fractional literals
194
+ also fail typechecking. Widened `number` values still require runtime validation.
195
+ Player bounds must be positive safe integers with `minPlayers <= maxPlayers`.
196
+
197
+ `defineTopologyManifest` returns `ValidatedManifest<M>` after semantic validation.
198
+ `compileManifest` returns a branded `CompiledManifest<M>` after validation and table
199
+ materialization. Both snapshot the authored input so later source edits do not
200
+ change the validated value or future initial tables. Literal IDs remain inferred.
201
+
202
+ `ReducerGameDefinitionInput` describes the authored assembly fields. The assembled
203
+ `ReducerGameDefinition` adds the bound contract and validation brand; authors never
204
+ supply those output-only fields.
205
+
206
+ `game.assemble` checks phase names, the initial phase, interaction declarations,
207
+ and simultaneous-phase requirements, then returns a branded `ReducerGameDefinition`.
208
+ Runtime consumers such as `createReducerBundle` require that assembled type;
209
+ handwritten structural lookalikes do not satisfy it. These brands record SDK
210
+ construction checks, not correctness of arbitrary reducer callbacks, and do not
211
+ replace validation of runtime commands or state. As with other TypeScript types,
212
+ explicit assertions can bypass them; treat constructed definitions as immutable.
213
+ Each card in a manual card set requires `id` for its definition and `cardType`
214
+ for its category:
215
+
216
+ ```ts
217
+ cards: [
218
+ { id: "ace", cardType: "ranked", name: "Ace", count: 2, properties: {} },
219
+ { id: "king", cardType: "ranked", name: "King", count: 1, properties: {} },
220
+ ];
221
+ ```
222
+
223
+ These definitions create runtime IDs `ace-1`, `ace-2`, and `king`, all in the
224
+ `ranked` category. When the card schema has `variants`, every `cardType` must
225
+ name one of them; `compileManifest` and `createGame` reject unknown categories.
226
+ The inferred table narrows a card lookup by its runtime ID to that definition's
227
+ card set, category, and properties. A variant property overrides a shared
228
+ property of the same name in both the runtime schema and inferred type.
229
+ To migrate an older manifest, replace each card's `type` with `id` and set
230
+ `cardType` explicitly (often to the former `type` value).
231
+
166
232
  ## Reducer runner contract
167
233
 
168
234
  `createReducerBundle(game)` returns exactly the contract version and four
169
235
  operations: `boardStatic()`, `initialize(input)`, `dispatch({ state, input })`,
170
- and `project({ state, playerIds })`. The runner contract is `0.5.0`; hosts must
171
- require that exact version. Dispatch includes validation and effect execution. Initialization returns
236
+ and `project({ state, playerIds })`. The runner contract is `0.7.0`; hosts must
237
+ require that exact version. Dispatch includes validation, direct transaction mutations, and phase entry.
238
+ Initialization returns
172
239
  `{ state, terminal?, events? }`, preserving outcomes and events from initial
173
- phase entry and automatic continuations.
240
+ phase entry and returned transitions.
241
+
242
+ Mutation callbacks use `tx.roll(dieId)`, `tx.shuffle({ zoneId, playerId? })`, and
243
+ `tx.deal({ fromZoneId, toZoneId, playerId, count })` directly. Return
244
+ `tx.transition(phaseName)` to enter a phase, including reentering the current
245
+ phase. An unreturned outcome schedules no work. Entry chains are bounded to
246
+ 1,000 entries per dispatch. `tx.endGame(outcome, { transition })` enters the
247
+ final phase once; that entry must not return another transition.
174
248
 
175
249
  The authoritative state is explicit on every dispatch and projection. A host
176
250
  may retain a warm worker and SDK caches, but replaying the same state and input
@@ -182,3 +256,70 @@ owns the monotonically increasing version, perspective, and action-set identity.
182
256
  The plugin frame basis contains `version`, `actionSetVersion`, and
183
257
  `perspectivePlayerId`; it has no generation counter. Hosts merge the separately
184
258
  cached board static projection when materializing plugin gameplay frames.
259
+
260
+ ### Initialization options and actors
261
+
262
+ Declare lobby options once on `createGame({ options: z.strictObject({ ... }), ... })`.
263
+ The bundle accepts JSON-safe `options` at initialization, validates them with that
264
+ schema, persists the parsed values, and supplies them to initial state and phase
265
+ initializers. Without a schema, only `{}` is accepted. Options schemas must be
266
+ JSON-native: transforms, preprocessing, and coercion are rejected. Restored
267
+ sessions validate their stored options with the same schema.
268
+
269
+ Perform shuffle, deal, and other initialization mutations in an ordinary phase
270
+ entry callback. Setup profiles and bootstrap instructions are removed.
271
+ Interactions use `actor` to override their phase actor; only authorized seats
272
+ receive their input domains. Use ordinary form choices for responses and explicit
273
+ rules plus transaction resource mutations for affordability. Prompt collectors,
274
+ implicit costs, guidance metadata, and phase zone declarations are removed.
275
+
276
+ A game authors one `view` for each requested seat. Public and private fields
277
+ compose in that function; the transport never uses a seat view as a spectator
278
+ payload. Static boards come directly from the compiled manifest.
279
+
280
+ Use `memoize((input: SomeImmutableObject) => result)` for shared pure calculations.
281
+ It caches by object identity with a WeakMap, including `undefined` results. Pass
282
+ immutable snapshots (or stable immutable branches), not an open mutable transaction.
283
+ There is no injected derived-value resolver.
284
+
285
+ ## React adapter dependency
286
+
287
+ Framework-free consumers can import the package root without React. Applications
288
+ using `@dreamboard-games/sdk/react` must install the maintained React store adapter
289
+ alongside React:
290
+
291
+ ```sh
292
+ pnpm add @dreamboard-games/sdk react@^19 react-dom@^19 @tanstack/react-store@0.11.1
293
+ ```
294
+
295
+ `@tanstack/react-store` is an optional peer of the SDK so headless consumers do not
296
+ install the React adapter. The `/react` entry delegates selectors to that package;
297
+ the application bundler resolves its supported React subscription dependencies.
298
+
299
+ The SDK includes dnd-kit for `/react` drag and drop. Enable `dragFeature`, export
300
+ `useCardDrag` and `useBoardDrop` from the binding, and use the copied Hand and
301
+ BoardTargets components or your own renderer. GameProvider owns the drag provider.
302
+ Headless `game.drag` remains browser-free and handles atomic domain routing.
303
+
304
+ ## Local development and tests
305
+
306
+ `localSource(game, { players, seed, as, options })` executes the production reducer
307
+ and materializes the selected seat. `scenarioSource` starts from authored scenario
308
+ checkpoints. Keep these executable game imports in your local development entry;
309
+ the hosted entry uses only `iframeSource()` and type imports.
310
+
311
+ Local sources expose `inspect`, bounded `explore`, typed explicit-actor `apply`,
312
+ `switchSeat`, `checkpoint`, and validated `restore`. A JSON checkpoint preserves
313
+ pending selections and terminal state; restoring does not replay commands.
314
+ `createTestSource(snapshot)` supplies controlled frames and acknowledgements for
315
+ instance and React tests. Static and hosted sources do not expose `apply`.
316
+
317
+ Hosts import `assertReducerBundleContract`, `REDUCER_CONTRACT_VERSION`,
318
+ `ReducerWire` types and `ReducerWireZod` schemas from `/reducer`. Canonical iframe
319
+ and gameplay websocket schemas plus `materializePluginGameplayFrame` live at the
320
+ root. Materialize the seat projection with static board data before publishing it;
321
+ sources publish the canonical seat view and keep command bases private.
322
+
323
+ Card sets contain their authored `cards`, `cardSchema`, and `defaultHome` directly.
324
+ Standard playing cards are game-owned definitions with ordinary suit/rank
325
+ properties; the SDK does not synthesize inventories or assign built-in rules.
@@ -0,0 +1,8 @@
1
+ /** A space on one player's board; boardId is the base manifest board ID. */
2
+ type PlayerBoardSpaceTarget<BoardId extends string = string, SpaceId extends string = string, PlayerId extends string = string> = {
3
+ readonly boardId: BoardId;
4
+ readonly playerId: PlayerId;
5
+ readonly spaceId: SpaceId;
6
+ };
7
+
8
+ export type { PlayerBoardSpaceTarget as P };
@@ -0,0 +1,139 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/shared/lookup.ts
8
+ function requireLookup(value, kind, id) {
9
+ if (value === void 0) throw new Error(`${kind} "${id}" is not present.`);
10
+ return value;
11
+ }
12
+
13
+ // src/shared/board-target.ts
14
+ import { z } from "zod";
15
+ var PlayerBoardSpaceTargetSchema = z.strictObject({
16
+ boardId: z.string(),
17
+ playerId: z.string(),
18
+ spaceId: z.string()
19
+ });
20
+ function isPlayerBoardSpaceTarget(value) {
21
+ return typeof value === "object" && value !== null && Object.keys(value).length === 3 && "boardId" in value && typeof value.boardId === "string" && "playerId" in value && typeof value.playerId === "string" && "spaceId" in value && typeof value.spaceId === "string";
22
+ }
23
+ function samePlayerBoardSpaceTarget(left, right) {
24
+ return left.boardId === right.boardId && left.playerId === right.playerId && left.spaceId === right.spaceId;
25
+ }
26
+
27
+ // src/shared/input-domain.ts
28
+ function inputDomainErrors(domain, value, selection, options = {}) {
29
+ if (selection?.mode === "many") {
30
+ if (!Array.isArray(value)) return ["Expected a list of values."];
31
+ const errors = cardinalityErrors(
32
+ value,
33
+ selection.min,
34
+ selection.max,
35
+ "value",
36
+ options.ignoreMinimum
37
+ );
38
+ if (selection.distinct && new Set(value.map(inputValueKey)).size !== value.length)
39
+ errors.push("Choose each value only once.");
40
+ if (value.some(
41
+ (item) => inputDomainErrors(domain, item, void 0, options).length > 0
42
+ ))
43
+ errors.push(ineligibleMessage(domain));
44
+ return errors;
45
+ }
46
+ switch (domain.type) {
47
+ case "choice":
48
+ return inputTargetInDomain(domain, value) ? [] : [ineligibleMessage(domain)];
49
+ case "choiceList": {
50
+ if (!Array.isArray(value)) return ["Expected a list of choices."];
51
+ const errors = cardinalityErrors(
52
+ value,
53
+ domain.min ?? 0,
54
+ domain.max ?? domain.choices.length,
55
+ "option",
56
+ options.ignoreMinimum
57
+ );
58
+ if (value.some((item) => !inputTargetInDomain(domain, item)))
59
+ errors.push(ineligibleMessage(domain));
60
+ return errors;
61
+ }
62
+ case "cardTarget":
63
+ case "boardTarget":
64
+ return inputTargetInDomain(domain, value) ? [] : [ineligibleMessage(domain)];
65
+ case "boundedNumber":
66
+ return typeof value === "number" && value >= domain.min && value <= domain.max && Math.abs(
67
+ (value - domain.min) / (domain.step ?? 1) - Math.round((value - domain.min) / (domain.step ?? 1))
68
+ ) < 1e-9 ? [] : ["Value is outside the current input domain."];
69
+ case "resourceMap":
70
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).every(
71
+ (key) => domain.resources.some((entry) => entry.resourceId === key)
72
+ ) && domain.resources.every((entry) => {
73
+ const amount = value[entry.resourceId] ?? 0;
74
+ return typeof amount === "number" && Number.isInteger(amount) && amount >= (entry.min ?? 0) && amount <= (entry.max ?? Infinity);
75
+ }) ? [] : ["Value is outside the current input domain."];
76
+ }
77
+ }
78
+ function inputValueInDomain(domain, value, selection, options = {}) {
79
+ return inputDomainErrors(domain, value, selection, options).length === 0;
80
+ }
81
+ function inputTargetInDomain(domain, value) {
82
+ switch (domain.type) {
83
+ case "choice":
84
+ case "choiceList":
85
+ return domain.choices.some(
86
+ (choice) => !choice.disabled && Object.is(choice.value, value)
87
+ );
88
+ case "cardTarget":
89
+ return typeof value === "string" && domain.eligibleTargets.includes(value);
90
+ case "boardTarget":
91
+ return domain.valueKind === "player-board-space" ? isPlayerBoardSpaceTarget(value) && domain.eligibleTargets.some(
92
+ (candidate) => samePlayerBoardSpaceTarget(candidate, value)
93
+ ) : typeof value === "string" && domain.eligibleTargets.includes(value);
94
+ default:
95
+ return inputValueInDomain(domain, value);
96
+ }
97
+ }
98
+ function cardinalityErrors(value, min, max, unit, ignoreMinimum = false) {
99
+ const errors = [];
100
+ if (!ignoreMinimum && value.length < min)
101
+ errors.push(`Choose at least ${min} ${pluralize(unit, min)}.`);
102
+ if (max !== void 0 && value.length > max)
103
+ errors.push(`Choose at most ${max} ${pluralize(unit, max)}.`);
104
+ return errors;
105
+ }
106
+ function ineligibleMessage(domain) {
107
+ return domain.type === "choice" || domain.type === "choiceList" ? "Selected choice is not eligible." : domain.type === "cardTarget" || domain.type === "boardTarget" ? "Selected target is not eligible." : "Value is outside the current input domain.";
108
+ }
109
+ function inputValueKey(value) {
110
+ if (value === null) return "null";
111
+ if (isPlayerBoardSpaceTarget(value))
112
+ return `player-board-space:${JSON.stringify([value.boardId, value.playerId, value.spaceId])}`;
113
+ switch (typeof value) {
114
+ case "string":
115
+ return `string:${value}`;
116
+ case "number":
117
+ case "boolean":
118
+ case "undefined":
119
+ return `${typeof value}:${String(value)}`;
120
+ default:
121
+ return `json:${JSON.stringify(value)}`;
122
+ }
123
+ }
124
+ function pluralize(word, count) {
125
+ return count === 1 ? word : `${word}s`;
126
+ }
127
+
128
+ export {
129
+ __export,
130
+ requireLookup,
131
+ PlayerBoardSpaceTargetSchema,
132
+ isPlayerBoardSpaceTarget,
133
+ samePlayerBoardSpaceTarget,
134
+ inputDomainErrors,
135
+ inputValueInDomain,
136
+ inputTargetInDomain,
137
+ inputValueKey
138
+ };
139
+ //# sourceMappingURL=chunk-3JXUL4VN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/lookup.ts","../src/shared/board-target.ts","../src/shared/input-domain.ts"],"sourcesContent":["/** Assert presence at a mutable table or projected-snapshot lookup boundary. */\nexport function requireLookup<T>(\n value: T | undefined,\n kind: string,\n id: string,\n): T {\n if (value === undefined) throw new Error(`${kind} \"${id}\" is not present.`);\n return value;\n}\n","import { z } from \"zod\";\n\n/** A space on one player's board; boardId is the base manifest board ID. */\nexport type PlayerBoardSpaceTarget<\n BoardId extends string = string,\n SpaceId extends string = string,\n PlayerId extends string = string,\n> = {\n readonly boardId: BoardId;\n readonly playerId: PlayerId;\n readonly spaceId: SpaceId;\n};\n\nexport const PlayerBoardSpaceTargetSchema = z.strictObject({\n boardId: z.string(),\n playerId: z.string(),\n spaceId: z.string(),\n});\n\nexport function isPlayerBoardSpaceTarget(\n value: unknown,\n): value is PlayerBoardSpaceTarget {\n return (\n typeof value === \"object\" &&\n value !== null &&\n Object.keys(value).length === 3 &&\n \"boardId\" in value &&\n typeof value.boardId === \"string\" &&\n \"playerId\" in value &&\n typeof value.playerId === \"string\" &&\n \"spaceId\" in value &&\n typeof value.spaceId === \"string\"\n );\n}\n\nexport function samePlayerBoardSpaceTarget(\n left: PlayerBoardSpaceTarget,\n right: PlayerBoardSpaceTarget,\n): boolean {\n return (\n left.boardId === right.boardId &&\n left.playerId === right.playerId &&\n left.spaceId === right.spaceId\n );\n}\n","import {\n isPlayerBoardSpaceTarget,\n samePlayerBoardSpaceTarget,\n} from \"./board-target\";\nimport type {\n InputDomainDescriptor,\n InputSelectionDescriptor,\n} from \"../reducer/model/spec/inputs\";\n\n/** Projected domain rules only; schemas and authored target checks remain server-owned. */\nexport function inputDomainErrors(\n domain: InputDomainDescriptor,\n value: unknown,\n selection?: InputSelectionDescriptor,\n options: { ignoreMinimum?: boolean } = {},\n): string[] {\n if (selection?.mode === \"many\") {\n if (!Array.isArray(value)) return [\"Expected a list of values.\"];\n const errors = cardinalityErrors(\n value,\n selection.min,\n selection.max,\n \"value\",\n options.ignoreMinimum,\n );\n if (\n selection.distinct &&\n new Set(value.map(inputValueKey)).size !== value.length\n )\n errors.push(\"Choose each value only once.\");\n if (\n value.some(\n (item) =>\n inputDomainErrors(domain, item, undefined, options).length > 0,\n )\n )\n errors.push(ineligibleMessage(domain));\n return errors;\n }\n switch (domain.type) {\n case \"choice\":\n return inputTargetInDomain(domain, value)\n ? []\n : [ineligibleMessage(domain)];\n case \"choiceList\": {\n if (!Array.isArray(value)) return [\"Expected a list of choices.\"];\n const errors = cardinalityErrors(\n value,\n domain.min ?? 0,\n domain.max ?? domain.choices.length,\n \"option\",\n options.ignoreMinimum,\n );\n if (value.some((item) => !inputTargetInDomain(domain, item)))\n errors.push(ineligibleMessage(domain));\n return errors;\n }\n case \"cardTarget\":\n case \"boardTarget\":\n return inputTargetInDomain(domain, value)\n ? []\n : [ineligibleMessage(domain)];\n case \"boundedNumber\":\n return typeof value === \"number\" &&\n value >= domain.min &&\n value <= domain.max &&\n Math.abs(\n (value - domain.min) / (domain.step ?? 1) -\n Math.round((value - domain.min) / (domain.step ?? 1)),\n ) < 1e-9\n ? []\n : [\"Value is outside the current input domain.\"];\n case \"resourceMap\":\n return typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.keys(value).every((key) =>\n domain.resources.some((entry) => entry.resourceId === key),\n ) &&\n domain.resources.every((entry) => {\n const amount =\n (value as Record<string, unknown>)[entry.resourceId] ?? 0;\n return (\n typeof amount === \"number\" &&\n Number.isInteger(amount) &&\n amount >= (entry.min ?? 0) &&\n amount <= (entry.max ?? Infinity)\n );\n })\n ? []\n : [\"Value is outside the current input domain.\"];\n }\n}\n\nexport function inputValueInDomain(\n domain: InputDomainDescriptor,\n value: unknown,\n selection?: InputSelectionDescriptor,\n options: { ignoreMinimum?: boolean } = {},\n): boolean {\n return inputDomainErrors(domain, value, selection, options).length === 0;\n}\n\n/** One selectable option, including an option within a choiceList or many input. */\nexport function inputTargetInDomain(\n domain: InputDomainDescriptor,\n value: unknown,\n): boolean {\n switch (domain.type) {\n case \"choice\":\n case \"choiceList\":\n return domain.choices.some(\n (choice) => !choice.disabled && Object.is(choice.value, value),\n );\n case \"cardTarget\":\n return (\n typeof value === \"string\" && domain.eligibleTargets.includes(value)\n );\n case \"boardTarget\":\n return domain.valueKind === \"player-board-space\"\n ? isPlayerBoardSpaceTarget(value) &&\n domain.eligibleTargets.some((candidate) =>\n samePlayerBoardSpaceTarget(candidate, value),\n )\n : typeof value === \"string\" && domain.eligibleTargets.includes(value);\n default:\n return inputValueInDomain(domain, value);\n }\n}\n\nfunction cardinalityErrors(\n value: readonly unknown[],\n min: number,\n max: number | undefined,\n unit: string,\n ignoreMinimum = false,\n): string[] {\n const errors: string[] = [];\n if (!ignoreMinimum && value.length < min)\n errors.push(`Choose at least ${min} ${pluralize(unit, min)}.`);\n if (max !== undefined && value.length > max)\n errors.push(`Choose at most ${max} ${pluralize(unit, max)}.`);\n return errors;\n}\n\nfunction ineligibleMessage(domain: InputDomainDescriptor): string {\n return domain.type === \"choice\" || domain.type === \"choiceList\"\n ? \"Selected choice is not eligible.\"\n : domain.type === \"cardTarget\" || domain.type === \"boardTarget\"\n ? \"Selected target is not eligible.\"\n : \"Value is outside the current input domain.\";\n}\n\n/** Matches the reducer's JSON value identity for projected distinct selections. */\nexport function inputValueKey(value: unknown): string {\n if (value === null) return \"null\";\n if (isPlayerBoardSpaceTarget(value))\n return `player-board-space:${JSON.stringify([value.boardId, value.playerId, value.spaceId])}`;\n switch (typeof value) {\n case \"string\":\n return `string:${value}`;\n case \"number\":\n case \"boolean\":\n case \"undefined\":\n return `${typeof value}:${String(value)}`;\n default:\n return `json:${JSON.stringify(value)}`;\n }\n}\n\nfunction pluralize(word: string, count: number): string {\n return count === 1 ? word : `${word}s`;\n}\n"],"mappings":"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","names":[]}