@dreamboard-games/sdk 0.5.0-alpha.0 → 0.5.0-alpha.10

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 (164) hide show
  1. package/README.md +388 -24
  2. package/dist/board-element-CRN_hj5F.d.ts +13 -0
  3. package/dist/chunk-34IGUWOP.js +1703 -0
  4. package/dist/chunk-34IGUWOP.js.map +1 -0
  5. package/dist/chunk-6ZNAIOYG.js +7125 -0
  6. package/dist/chunk-6ZNAIOYG.js.map +1 -0
  7. package/dist/chunk-B4RMCLUN.js +617 -0
  8. package/dist/chunk-B4RMCLUN.js.map +1 -0
  9. package/dist/chunk-D3F3GCMF.js +625 -0
  10. package/dist/chunk-D3F3GCMF.js.map +1 -0
  11. package/dist/chunk-ES74C2IN.js +1576 -0
  12. package/dist/chunk-ES74C2IN.js.map +1 -0
  13. package/dist/diagnostics-BokP7HN3.d.ts +64 -0
  14. package/dist/extract-DgzrJ5el.d.ts +6743 -0
  15. package/dist/index.d.ts +4748 -1
  16. package/dist/index.js +1556 -7
  17. package/dist/index.js.map +1 -1
  18. package/dist/model-BeslAXVA.d.ts +648 -0
  19. package/dist/react.d.ts +78 -0
  20. package/dist/react.js +411 -0
  21. package/dist/react.js.map +1 -0
  22. package/dist/reducer.d.ts +786 -544
  23. package/dist/reducer.js +4286 -842
  24. package/dist/reducer.js.map +1 -1
  25. package/dist/testing.d.ts +529 -569
  26. package/dist/testing.js +2588 -934
  27. package/dist/testing.js.map +1 -1
  28. package/dist/types-Xg_RxV1n.d.ts +118 -0
  29. package/package.json +39 -163
  30. package/dist/HandView-Bi2lxzQP.d.ts +0 -79
  31. package/dist/ResourceCounter-BFxJknqp.d.ts +0 -98
  32. package/dist/ThemeProvider-CElaL2ik.d.ts +0 -99
  33. package/dist/attributes-DbvyMbXw.d.ts +0 -68
  34. package/dist/authoring/index.d.ts +0 -59
  35. package/dist/authoring/index.js +0 -24
  36. package/dist/authoring/index.js.map +0 -1
  37. package/dist/authoring-compiler.d.ts +0 -27
  38. package/dist/authoring-compiler.js +0 -11
  39. package/dist/authoring-compiler.js.map +0 -1
  40. package/dist/authoring-materialize-cli.d.ts +0 -1
  41. package/dist/authoring-materialize-cli.js +0 -45
  42. package/dist/authoring-materialize-cli.js.map +0 -1
  43. package/dist/browser-interaction.d.ts +0 -910
  44. package/dist/browser-interaction.js +0 -119
  45. package/dist/browser-interaction.js.map +0 -1
  46. package/dist/chunk-2GQIALJL.js +0 -219
  47. package/dist/chunk-2GQIALJL.js.map +0 -1
  48. package/dist/chunk-533O7C4L.js +0 -7095
  49. package/dist/chunk-533O7C4L.js.map +0 -1
  50. package/dist/chunk-6LMESVGT.js +0 -5960
  51. package/dist/chunk-6LMESVGT.js.map +0 -1
  52. package/dist/chunk-DQS2EG5E.js +0 -564
  53. package/dist/chunk-DQS2EG5E.js.map +0 -1
  54. package/dist/chunk-EOEA3I64.js +0 -1864
  55. package/dist/chunk-EOEA3I64.js.map +0 -1
  56. package/dist/chunk-FL67RDID.js +0 -2903
  57. package/dist/chunk-FL67RDID.js.map +0 -1
  58. package/dist/chunk-HIH6Y4FA.js +0 -7802
  59. package/dist/chunk-HIH6Y4FA.js.map +0 -1
  60. package/dist/chunk-HUK4234C.js +0 -166
  61. package/dist/chunk-HUK4234C.js.map +0 -1
  62. package/dist/chunk-KGV2EUNN.js +0 -532
  63. package/dist/chunk-KGV2EUNN.js.map +0 -1
  64. package/dist/chunk-KRJPRYEA.js +0 -922
  65. package/dist/chunk-KRJPRYEA.js.map +0 -1
  66. package/dist/chunk-KWHOFBDM.js +0 -9
  67. package/dist/chunk-KWHOFBDM.js.map +0 -1
  68. package/dist/chunk-LMHCGCA6.js +0 -104
  69. package/dist/chunk-LMHCGCA6.js.map +0 -1
  70. package/dist/chunk-N33CGX2F.js +0 -263
  71. package/dist/chunk-N33CGX2F.js.map +0 -1
  72. package/dist/chunk-PF7L4BMG.js +0 -117
  73. package/dist/chunk-PF7L4BMG.js.map +0 -1
  74. package/dist/chunk-PZ5AY32C.js +0 -10
  75. package/dist/chunk-PZ5AY32C.js.map +0 -1
  76. package/dist/chunk-T3ZKNUZ7.js +0 -1
  77. package/dist/chunk-T3ZKNUZ7.js.map +0 -1
  78. package/dist/chunk-TXELBVTG.js +0 -32
  79. package/dist/chunk-TXELBVTG.js.map +0 -1
  80. package/dist/chunk-VDXOF4FW.js +0 -69
  81. package/dist/chunk-VDXOF4FW.js.map +0 -1
  82. package/dist/chunk-VRRR6QZT.js +0 -11941
  83. package/dist/chunk-VRRR6QZT.js.map +0 -1
  84. package/dist/chunk-VWTKGIYF.js +0 -90
  85. package/dist/chunk-VWTKGIYF.js.map +0 -1
  86. package/dist/chunk-WHR5UW3F.js +0 -1988
  87. package/dist/chunk-WHR5UW3F.js.map +0 -1
  88. package/dist/chunk-XFMSLL3P.js +0 -637
  89. package/dist/chunk-XFMSLL3P.js.map +0 -1
  90. package/dist/chunk-ZSTCUJKZ.js +0 -21
  91. package/dist/chunk-ZSTCUJKZ.js.map +0 -1
  92. package/dist/codegen.d.ts +0 -941
  93. package/dist/codegen.js +0 -44
  94. package/dist/codegen.js.map +0 -1
  95. package/dist/components-CNAt5J1p.d.ts +0 -1300
  96. package/dist/definitions-bZSBaPjj.d.ts +0 -300
  97. package/dist/diagnostics-DIi3ina7.d.ts +0 -50
  98. package/dist/digest.d.ts +0 -8
  99. package/dist/hex-board-view-aKkblDp6.d.ts +0 -1228
  100. package/dist/index-CR69MyWr.d.ts +0 -193
  101. package/dist/index.d-Bw8AOcgp.d.ts +0 -2036
  102. package/dist/package-set.d.ts +0 -13
  103. package/dist/package-set.js +0 -13
  104. package/dist/package-set.js.map +0 -1
  105. package/dist/player-state-Cqpyeql0.d.ts +0 -371
  106. package/dist/plugin-runtime-contract.d.ts +0 -17
  107. package/dist/plugin-runtime-contract.js +0 -94
  108. package/dist/plugin-runtime-contract.js.map +0 -1
  109. package/dist/plugin-state-BRH3UiUp.d.ts +0 -214
  110. package/dist/primitive-props-BNHDkgd7.d.ts +0 -16
  111. package/dist/protocol-CVaSqPgX.d.ts +0 -320
  112. package/dist/reducer/advanced.d.ts +0 -81
  113. package/dist/reducer/advanced.js +0 -45
  114. package/dist/reducer/advanced.js.map +0 -1
  115. package/dist/reducer-contract.d.ts +0 -3
  116. package/dist/reducer-contract.js +0 -16
  117. package/dist/reducer-contract.js.map +0 -1
  118. package/dist/reference-games/index.d.ts +0 -31
  119. package/dist/reference-games/index.js +0 -48
  120. package/dist/reference-games/index.js.map +0 -1
  121. package/dist/runtime/primitives.d.ts +0 -250
  122. package/dist/runtime/primitives.js +0 -189
  123. package/dist/runtime/primitives.js.map +0 -1
  124. package/dist/runtime/runtime-api.d.ts +0 -2
  125. package/dist/runtime/runtime-api.js +0 -1
  126. package/dist/runtime/runtime-api.js.map +0 -1
  127. package/dist/runtime/workspace-contract.d.ts +0 -359
  128. package/dist/runtime/workspace-contract.js +0 -23
  129. package/dist/runtime/workspace-contract.js.map +0 -1
  130. package/dist/runtime-C7Z2acNU.d.ts +0 -1706
  131. package/dist/runtime-api-CgPuEVeF.d.ts +0 -86
  132. package/dist/runtime-json-CQ9QbLZ5.d.ts +0 -5
  133. package/dist/runtime.d.ts +0 -83
  134. package/dist/runtime.js +0 -240
  135. package/dist/runtime.js.map +0 -1
  136. package/dist/schema.d.ts +0 -405
  137. package/dist/stale-contract-artifact-error-XLaweZtF.d.ts +0 -18
  138. package/dist/testing-compiler.d.ts +0 -37
  139. package/dist/testing-compiler.js +0 -272
  140. package/dist/testing-compiler.js.map +0 -1
  141. package/dist/testing-runtime.d.ts +0 -119
  142. package/dist/testing-runtime.js +0 -212
  143. package/dist/testing-runtime.js.map +0 -1
  144. package/dist/types-DJj5MJkl.d.ts +0 -256
  145. package/dist/types-DN4O7kH_.d.ts +0 -122
  146. package/dist/types-q1Guidm5.d.ts +0 -28
  147. package/dist/types.d.ts +0 -1958
  148. package/dist/types.js +0 -26
  149. package/dist/types.js.map +0 -1
  150. package/dist/ui/components.d.ts +0 -18
  151. package/dist/ui/components.js +0 -220
  152. package/dist/ui/components.js.map +0 -1
  153. package/dist/ui/defaults.d.ts +0 -19
  154. package/dist/ui/defaults.js +0 -104
  155. package/dist/ui/defaults.js.map +0 -1
  156. package/dist/ui/player-state.d.ts +0 -2
  157. package/dist/ui/player-state.js +0 -1
  158. package/dist/ui/player-state.js.map +0 -1
  159. package/dist/ui/plugin-styles.css +0 -2
  160. package/dist/ui-contract-BC4JpysE.d.ts +0 -1169
  161. package/dist/ui.d.ts +0 -318
  162. package/dist/ui.js +0 -281
  163. package/dist/ui.js.map +0 -1
  164. package/dist/views-D2Cfzr3h.d.ts +0 -1588
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,42 +10,292 @@ 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.
13
+ The package declarations and export map are the API authority. There are four entry points:
14
+
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 card gesture and drop-area 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.
19
+
20
+ UI components are source-owned registry items installed into your application.
21
+ The SDK contains no styled components or stylesheet.
22
+
23
+ ## Game authoring
24
+
25
+ `createGame(model)` is the single authoring entry point. The **model** is a
26
+ plain object: manifest ids, state schemas, one Zod schema per phase, and error
27
+ codes. The returned value is three things at once:
28
+
29
+ - the **type leaf**: `typeof game.types.State`, `.ErrorCode`, `.PlayerId`,
30
+ `.Queries`, `.Tx` (phantom carriers; reading them at runtime throws),
31
+ - the **factory namespace**: `game.phase(name)`, `phase.define`,
32
+ `phase.interaction`, `phase.inputs.*`, `game.view`,
33
+ - the **assembler**: `game.assemble({ initial, initialPhase, phases, view })`.
34
+
35
+ Mutation callbacks (`enter`, `reduce`, `resolve`) receive an open transaction
36
+ `tx`. Mutate through it and finish with a bare `return` (accept), or with
37
+ `tx.transition(name)`, `tx.endGame(outcome)`, or `tx.reject(code)`. Events go
38
+ through `tx.emit(...)`. `state` is the read-only snapshot the callback started
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.
16
45
 
17
46
  ```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);
47
+ // app/game-model.ts — the model, bound once
48
+ import { z } from "zod";
49
+ import { compileManifest, createGame } from "@dreamboard-games/sdk/reducer";
50
+ import manifest from "../manifest";
51
+ const manifestContract = compileManifest(manifest);
52
+ const { ids } = manifestContract;
53
+
54
+ export const game = createGame({
55
+ manifest: manifestContract,
56
+ state: {
57
+ public: z.object({ currentPlayerId: ids.playerId.nullable() }),
58
+ private: z.object({}),
59
+ hidden: z.object({}),
60
+ },
61
+ phases: {
62
+ setup: z.object({}),
63
+ play: z.object({ leadCardId: ids.cardId.nullable() }),
64
+ },
65
+ errors: { NOT_YOUR_CARD: "Play a card from your own hand." },
66
+ });
67
+
68
+ export type GameState = typeof game.types.State;
69
+ ```
70
+
71
+ ```ts
72
+ // app/phases/play.ts — one phase, one file
73
+ import { game } from "../game-model";
74
+
75
+ const play = game.phase("play");
76
+
77
+ // Inputs name their candidate domain and eligibility predicates directly.
78
+ // `where.errorCode` is checked against `model.errors`.
79
+ const ownCard = play.inputs.card({
80
+ from: ["hand"],
81
+ where: {
82
+ id: "own-card",
83
+ errorCode: "NOT_YOUR_CARD",
84
+ test: ({ q, playerId, targetId }) =>
85
+ q.zone("hand", playerId).includes(targetId),
86
+ },
87
+ });
88
+
89
+ export default play.define({
90
+ kind: "player",
91
+ initialState: () => ({ leadCardId: null }),
92
+ actor: ({ state }) => state.publicState.currentPlayerId,
93
+ interactions: {
94
+ playCard: play.interaction({
95
+ inputs: { cardId: ownCard },
96
+ reduce({ tx, input, q }) {
97
+ // tx.state.phase.leadCardId and input.params.cardId are inferred.
98
+ tx.moveComponentToZone({
99
+ componentId: input.params.cardId,
100
+ to: { zoneId: "trick" },
101
+ playedBy: input.playerId,
102
+ });
103
+ tx.patchPhaseState({ leadCardId: input.params.cardId });
104
+ const next = q.player.nextInOrder(input.playerId);
105
+ if (next) {
106
+ tx.patchPublicState({ currentPlayerId: next });
107
+ tx.setActivePlayers([next]);
108
+ }
109
+ if (tx.q.zone("hand", input.playerId).length === 0) {
110
+ return tx.transition("setup");
111
+ }
112
+ },
113
+ }),
114
+ },
115
+ });
28
116
  ```
29
117
 
30
- Reference-game manifests use schema V5. They describe the game workspace,
31
- teaching purpose, mechanics, UI patterns, and substantive rights metadata.
118
+ Dependent choices use `phase.steps()` instead of `inputs`. Each accepted command
119
+ commits exactly one current value. Factories receive only earlier parsed
120
+ `selected` values; descriptors expose only the current input. The final commit
121
+ runs complete-parameter validation and the reducer once. Use explicit `null`
122
+ for a no-target choice, and `many(...)` for one atomic multi-selection.
123
+
124
+ ```ts
125
+ const choose = play.interaction({
126
+ steps: play
127
+ .steps()
128
+ .input(
129
+ "kind",
130
+ play.inputs.form.choice({
131
+ choices: [{ value: "single", label: "Single" }],
132
+ defaultValue: () => undefined,
133
+ }),
134
+ )
135
+ .input("count", ({ selected }) =>
136
+ play.inputs.form.number({
137
+ min: 1,
138
+ max: selected.kind === "single" ? 1 : 3,
139
+ defaultValue: 1,
140
+ }),
141
+ ),
142
+ reduce({ input }) {
143
+ // input.params contains both kind and count here.
144
+ },
145
+ });
146
+ ```
32
147
 
33
- Include the packaged stylesheet when using SDK UI components:
148
+ `rules.available` controls action eligibility; `rules.validate` checks a final
149
+ submission. Accepted state changes reconcile pending prefixes, while phase
150
+ entry clears them. A rejected final submission keeps the prior prefix. The
151
+ actor can cancel an unsealed prefix with `interaction.cancel`, using the same
152
+ transport basis and action identity as submission.
34
153
 
35
154
  ```ts
36
- import "@dreamboard-games/sdk/ui/plugin-styles.css";
155
+ // app/game.ts — assembly
156
+ import { game } from "./game-model";
157
+ import play from "./phases/play";
158
+ import setup from "./phases/setup";
159
+
160
+ export default game.assemble({
161
+ initial: {
162
+ public: ({ playerIds }) => ({ currentPlayerId: playerIds[0] ?? null }),
163
+ private: () => ({}),
164
+ hidden: () => ({}),
165
+ },
166
+ initialPhase: "setup",
167
+ phases: { setup, play },
168
+ view: game.view(({ state, playerId, q }) => ({
169
+ me: playerId,
170
+ hand: q.zone("hand", playerId),
171
+ current: state.publicState.currentPlayerId,
172
+ })),
173
+ });
37
174
  ```
38
175
 
176
+ A missing, extra, or misspelled phase key fails to typecheck at `assemble`
177
+ or at `game.phase(name)`. Rules, views, and tests import `GameState` and
178
+ `typeof game.types.Tx` from the model module; nothing but the test harness
179
+ imports the assembled game, so there is no import cycle.
180
+
181
+ New workspaces keep authored starter code in `app/game.ts` and `ui/App.tsx`.
182
+ Import the manifest directly. `compileManifest(manifest)` provides inferred ID schemas,
183
+ table schemas, fresh initial tables, and board metadata in memory. `createGame`
184
+ requires that compiled manifest when binding state schemas. Bind a typed React hook with
185
+ `createGameHook<Game>()({ features })` from `@dreamboard-games/sdk/react`,
186
+ and pass a source to its `GameProvider`. The hosted UI imports `Game` only as a type;
187
+ `iframeSource()` supplies authoritative frames and handles commands.
188
+ No authoring generation step or shared workspace files are needed.
189
+
190
+ `createInitialTable({ playerIds })` takes the actual roster explicitly. Cards,
191
+ piece, die and tile seeds can use `scope: "perPlayer"` to replicate for those
192
+ seats, with initial ownership derived from the replication origin. Ownership
193
+ changes do not change instance IDs. Use `perPlayerInstanceId` when constructing
194
+ a reference from a known family, expanded base and seat; decoding validates
195
+ syntax only, while table admission checks live membership. UI consumers should
196
+ use the canonical IDs supplied by projected boards and eligible targets.
197
+ Instance record factories likewise require `{ playerIds }`, for example
198
+ `manifest.records.pieceIds(0, { playerIds })`. Player IDs are nonempty and unique;
199
+ `__proto__` is reserved to preserve roster keys through record parsing.
200
+
201
+ Zones declare either `scope` or `attachedTo` a board, generic board space, tile
202
+ cell, piece type, or die type. Query and move through the same zone API with the concrete host ID;
203
+ use `boardSpaceHostId(boardId, spaceId)` for a space attachment. Component-hosted
204
+ access follows the host's current owner, independently of contained ownership
205
+ and card face state. Initialization, movement and restore reject containment
206
+ cycles. Containers, slots and their location variants are removed.
207
+
208
+ `tileTypes` own immutable hex/square cells, annotations and rule fields;
209
+ `tileSeeds` create game-owned instances with independent ownership and mutable
210
+ properties. `q.tile(id)` addresses that inventory, including detached tiles.
211
+ Canonical `OnBoard` locations own placement. Hex/square topology is derived from
212
+ those locations and immutable definitions; runtime boards store only `baseId`
213
+ and explicit session relations. Cell identity follows the tile instance and
214
+ local cell, while world edges and vertices belong to the exact board instance.
215
+ The flat seat `boards` projection is the sole client topology source.
216
+
217
+ Private tile destinations remain rejected in this layer. Generic component
218
+ moves cannot remove a placed tile or place tiles on component spaces, edges or
219
+ vertices. Tile-cell zone hosts must remain empty while their tile is unplaced.
220
+ The existing UI zone facade still presents cards.
221
+
222
+ Manifest card counts and explicit piece/die/tile seed counts must be positive safe
223
+ integers. Omitted seed counts mean one copy. Zero, negative, fractional, non-finite,
224
+ and unsafe counts fail before ID expansion; negative, zero, and fractional literals
225
+ also fail typechecking. Widened `number` values still require runtime validation.
226
+ Player bounds must be positive safe integers with `minPlayers <= maxPlayers`.
227
+
228
+ `defineTopologyManifest` returns `ValidatedManifest<M>` after semantic validation.
229
+ `compileManifest` returns a branded `CompiledManifest<M>` after validation and table
230
+ materialization. Both snapshot the authored input so later source edits do not
231
+ change the validated value or future initial tables. Literal IDs remain inferred.
232
+
233
+ `ReducerGameDefinitionInput` describes the authored assembly fields. The assembled
234
+ `ReducerGameDefinition` adds the bound contract and validation brand; authors never
235
+ supply those output-only fields.
236
+
237
+ `game.assemble` checks phase names, the initial phase, interaction declarations,
238
+ and simultaneous-phase requirements, then returns a branded `ReducerGameDefinition`.
239
+ Runtime consumers such as `createReducerBundle` require that assembled type;
240
+ handwritten structural lookalikes do not satisfy it. These brands record SDK
241
+ construction checks, not correctness of arbitrary reducer callbacks, and do not
242
+ replace validation of runtime commands or state. As with other TypeScript types,
243
+ explicit assertions can bypass them; treat constructed definitions as immutable.
244
+ Each card in a manual card set requires `id` for its definition and `cardType`
245
+ for its category:
246
+
247
+ ```ts
248
+ cards: [
249
+ { id: "ace", cardType: "ranked", name: "Ace", count: 2, properties: {} },
250
+ { id: "king", cardType: "ranked", name: "King", count: 1, properties: {} },
251
+ ];
252
+ ```
253
+
254
+ These definitions create runtime IDs `ace-1`, `ace-2`, and `king`, all in the
255
+ `ranked` category. Component IDs cannot start with `card-ref:`; that prefix is reserved
256
+ for opaque concealed-card references. These references belong to one seat and
257
+ authority version and expire after commits or restores. When the card schema has `byCardType`, every `cardType` must
258
+ name one of its schemas; `compileManifest` rejects unknown categories before `createGame` binds the result.
259
+ The inferred table narrows a card lookup by its runtime ID to that definition's
260
+ card set, category, and properties. Author field schemas with `z.object`. For card categories, use
261
+ `cardSchema: { byCardType: { ranked: base.extend({ points: z.number().int() }) } }`
262
+ to reuse shared properties. Zod `.extend` defines property overrides in both
263
+ the runtime schema and inferred type. Fields are required unless authored with
264
+ `.optional()` or an explicit `.default(...)`; primitive defaults are not inferred.
265
+ Use `ref.cardId()`, `ref.pieceId()`, and the other `ref` markers for manifest
266
+ references. `defineTopologyManifest` exports portable JSON Schema while
267
+ retaining the authored types; refinements, transforms, and unsupported Zod
268
+ constructs fail at definition time.
269
+ For external JSON documents, call `parseTopologyManifestJson(value)` before
270
+ `compileManifest`. The parser checks structural keys, field schemas and manifest
271
+ references; transport services can preserve the document without duplicating
272
+ the SDK's model.
273
+ To migrate an older manifest, replace each card's `type` with `id` and set
274
+ `cardType` explicitly (often to the former `type` value).
275
+
39
276
  ## Reducer runner contract
40
277
 
41
- `createReducerBundle(game)` returns exactly the contract version and four
42
- operations: `boardStatic()`, `initialize(input)`, `dispatch({ state, input })`,
43
- and `project({ state, playerIds })`. The runner contract is `0.5.0`; hosts must
44
- require that exact version. Dispatch includes validation and effect execution. Initialization returns
278
+ `createReducerBundle(game)` returns exactly the contract version and three
279
+ operations: `initialize(input)`, `dispatch({ state, input, referenceBasis })`,
280
+ and `project({ state, playerIds, referenceBasis })`. The runner contract is `0.11.0`; hosts must
281
+ require that exact version. The host supplies `referenceBasis: { sessionId, version }`:
282
+ use a fresh session ID for each authority lifetime, increment version exactly once
283
+ for every accepted input, and advance it on restore without restoring an old
284
+ version from checkpoint data. Never change authoritative state under the same
285
+ basis. Submitted inputs carry the complete issuing frame basis; the host checks
286
+ its action-set version before dispatch. Dispatch includes validation, direct
287
+ transaction mutations, and phase entry.
288
+ Initialization returns
45
289
  `{ state, terminal?, events? }`, preserving outcomes and events from initial
46
- phase entry and automatic continuations.
290
+ phase entry and returned transitions.
291
+
292
+ Mutation callbacks use `tx.roll(dieId)`, `tx.shuffle({ zone: { zoneId, hostId } })`,
293
+ `tx.deal({ from, to, count })` and
294
+ `tx.flipCard({ cardId, faceUp })` directly. Return
295
+ `tx.transition(phaseName)` to enter a phase, including reentering the current
296
+ phase. An unreturned outcome schedules no work. Entry chains are bounded to
297
+ 1,000 entries per dispatch. `tx.endGame(outcome, { transition })` enters the
298
+ final phase once; that entry must not return another transition.
47
299
 
48
300
  The authoritative state is explicit on every dispatch and projection. A host
49
301
  may retain a warm worker and SDK caches, but replaying the same state and input
@@ -55,3 +307,115 @@ owns the monotonically increasing version, perspective, and action-set identity.
55
307
  The plugin frame basis contains `version`, `actionSetVersion`, and
56
308
  `perspectivePlayerId`; it has no generation counter. Hosts merge the separately
57
309
  cached board static projection when materializing plugin gameplay frames.
310
+
311
+ ### Initialization options and actors
312
+
313
+ Declare lobby options once on `createGame({ options: z.strictObject({ ... }), ... })`.
314
+ The bundle accepts JSON-safe `options` at initialization, validates them with that
315
+ schema, persists the parsed values, and supplies them to initial state and phase
316
+ initializers. Without a schema, only `{}` is accepted. Options schemas must be
317
+ JSON-native: transforms, preprocessing, and coercion are rejected. Restored
318
+ sessions validate their stored options with the same schema.
319
+
320
+ Perform shuffle, deal, and other initialization mutations in an ordinary phase
321
+ entry callback. Setup profiles and bootstrap instructions are removed.
322
+ Interactions use `actor` to override their phase actor; only authorized seats
323
+ receive their input domains. Use ordinary form choices for responses and explicit
324
+ rules plus transaction resource mutations for affordability. Prompt collectors,
325
+ implicit costs, guidance metadata, and phase zone declarations are removed.
326
+
327
+ A game authors one `view` for each requested seat. Public and private fields
328
+ compose in that function; the transport never uses a seat view as a spectator
329
+ payload. Shared static boards come from compiled definitions; per-player boards
330
+ are instantiated for the actual roster.
331
+
332
+ Use `memoize((input: SomeImmutableObject) => result)` for shared pure calculations.
333
+ It caches by object identity with a WeakMap, including `undefined` results. Pass
334
+ immutable snapshots (or stable immutable branches), not an open mutable transaction.
335
+ There is no injected derived-value resolver.
336
+
337
+ ## React adapter dependency
338
+
339
+ Framework-free consumers can import the package root without React. Applications
340
+ using `@dreamboard-games/sdk/react` must install the maintained React store adapter
341
+ alongside React:
342
+
343
+ ```sh
344
+ pnpm add @dreamboard-games/sdk react@^19 react-dom@^19 @tanstack/react-store@0.11.1
345
+ ```
346
+
347
+ `@tanstack/react-store` is an optional peer of the SDK so headless consumers do not
348
+ install the React adapter. The `/react` entry delegates selectors to that package;
349
+ the application bundler resolves its supported React subscription dependencies.
350
+
351
+ `/react` classifies each card press as a tap, hold, drag or browse. Enable
352
+ `dragFeature`, export `useCardGesture`, `useDropArea` and `useDragOverlay` from the
353
+ binding, and use the copied Hand and BoardTargets components or your own renderer.
354
+ The SDK prescribes no layout. Headless `game.drag` remains browser-free and handles
355
+ atomic domain routing. `fanLayout` computes arc positions for a hand, and
356
+ `originsFeature` adds `card.getOrigin()` so a newly shown card can animate from
357
+ the zone or player it came from.
358
+
359
+ ## Local development and tests
360
+
361
+ `localSource(game, { players, seed, as, options })` executes the production reducer
362
+ and materializes the selected seat. `scenarioSource` starts from authored scenario
363
+ checkpoints. Keep these executable game imports in your local development entry;
364
+ the hosted entry uses only `iframeSource()` and type imports.
365
+
366
+ Local sources expose `inspect`, bounded `explore`, typed explicit-actor `apply`,
367
+ `switchSeat`, `checkpoint`, and validated `restore`. A JSON checkpoint preserves
368
+ visible pending selections and terminal state; restoring does not replay commands.
369
+ Concealed selections expire after any other accepted input or restore. Each
370
+ accepted extension of that same unfinished selection issues a new validity stamp
371
+ for the immediately following frame. This prevents drafts from tracking hidden
372
+ tiles or cards through a shuffle, including a shuffle that leaves their position
373
+ unchanged.
374
+ `createTestSource(snapshot)` supplies controlled frames and acknowledgements for
375
+ instance and React tests. Static and hosted sources do not expose `apply`.
376
+
377
+ Hosts import `assertReducerBundleContract`, `REDUCER_CONTRACT_VERSION`,
378
+ `ReducerWire` types and `ReducerWireZod` schemas from `/reducer`. Canonical iframe
379
+ and gameplay websocket schemas plus `materializePluginGameplayFrame` live at the
380
+ root. Materialize the requested seat directly from its projection before publishing it;
381
+ sources publish the canonical seat view and submit the exact frame basis.
382
+
383
+ Visible cards share one complete `ViewCard` shape: `id`, `cardType`, and JSON
384
+ `properties`, plus optional `name`, `text`, `frontImage`, and `backImage`.
385
+ `ViewCardSchema` validates this shape. Reducer and gameplay `cardViewsById` maps
386
+ contain these objects directly. Headless `card.view` preserves the manifest's
387
+ card identity, category, and property inference and is deeply readonly; table-only
388
+ `cardSetId` and `componentType` are absent. Concealed cards have a positional
389
+ opaque seat identity, no view, and an optional separate `cardBacksById` entry.
390
+ This wire format requires plugin protocol version 10 and reducer contract `0.11.0`.
391
+
392
+ Tiles use opaque `SeatTileRef` values even when visible. A projected tile is either
393
+ `{ disclosure: "visible", ref, tileTypeId, name, ownerId, fields, properties, frontImage? }` or
394
+ `{ disclosure: "concealed", ref, appearance }`; an omitted tile contributes no
395
+ entry or count. Public appearances are authored independently of secret faces.
396
+ Boards and zones each carry their own projected tiles. Only visible tiles
397
+ contribute cells, edges, vertices, and relations to the seat's board topology.
398
+ Use `phase.inputs.tile({ from: [zoneId] })` or
399
+ `phase.inputs.tile({ boards: [boardId] })` to select a tile. Reducers receive
400
+ its authoritative identity after the SDK validates and resolves the reference.
401
+ `tx.setTileDisclosure` controls face audiences and public appearance;
402
+ `tx.setBoardVisibility` controls the board's audience. Location visibility always
403
+ caps tile disclosure. Authored defaults cannot identify concealed tiles or cards.
404
+ Game-owned view fields can publish visible references through `references.tile`
405
+ and `references.space`; arbitrary authored text and JSON remain explicit publication.
406
+
407
+ Every `tx.emit` event declares `audience: { kind: "public" }` or
408
+ `{ kind: "seats", playerIds }`. A typed detail `{ kind: "tile", tileId }` is
409
+ projected only when that tile is visible to the audience. Identity-specific event
410
+ details and view references never track a concealed tile. Clients receive only
411
+ their own events and admitted tile references.
412
+
413
+ Card sets contain their authored `cards`, `cardSchema`, and `defaultHome` directly.
414
+ Standard playing cards are game-owned definitions with ordinary suit/rank
415
+ properties; the SDK does not synthesize inventories or assign built-in rules.
416
+
417
+ The exported `createGestureRecognizer(down, callbacks, options)` defaults to
418
+ upward touch dragging so hands retain sideways scrolling. Pile controls can use
419
+ `{ dragDirection: "any" }` with `touch-action: none` to drag toward a hand below
420
+ or beside them. This classifies pointer intent only; submit an authored interaction
421
+ through the bound game to enforce its rules, without selecting a hidden card ID.
@@ -0,0 +1,13 @@
1
+ import { f as BoardEdgeId, n as BoardVertexId } from './extract-DgzrJ5el.js';
2
+
3
+ declare function boardEdgeId<BoardId extends string>(layout: "hex" | "square", boardId: BoardId, latticeId: string): BoardEdgeId<BoardId>;
4
+ declare function boardVertexId<BoardId extends string>(layout: "hex" | "square", boardId: BoardId, latticeId: string): BoardVertexId<BoardId>;
5
+ /** Syntax proves namespace only; current placement owns element membership. */
6
+ declare function parseBoardElementId(value: unknown): {
7
+ kind: "edge" | "vertex";
8
+ layout: "hex" | "square";
9
+ boardId: string;
10
+ latticeId: string;
11
+ } | null;
12
+
13
+ export { boardVertexId as a, boardEdgeId as b, parseBoardElementId as p };