@dreamboard-games/sdk 0.5.0-alpha.2 → 0.5.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +462 -53
- package/dist/board-element-CqtxU5VZ.d.ts +13 -0
- package/dist/chunk-CZRPUWCT.js +1602 -0
- package/dist/chunk-CZRPUWCT.js.map +1 -0
- package/dist/chunk-DMSXVMYY.js +7199 -0
- package/dist/chunk-DMSXVMYY.js.map +1 -0
- package/dist/chunk-HVNA4RAM.js +1792 -0
- package/dist/chunk-HVNA4RAM.js.map +1 -0
- package/dist/chunk-U6R7FAH4.js +630 -0
- package/dist/chunk-U6R7FAH4.js.map +1 -0
- package/dist/chunk-YT3RPO5J.js +635 -0
- package/dist/chunk-YT3RPO5J.js.map +1 -0
- package/dist/diagnostics-ZAPEBlJP.d.ts +64 -0
- package/dist/extract-CwJWDrSD.d.ts +6850 -0
- package/dist/index.d.ts +5177 -1
- package/dist/index.js +1990 -6
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +158 -0
- package/dist/react.js +1119 -0
- package/dist/react.js.map +1 -0
- package/dist/reducer.d.ts +1028 -97
- package/dist/reducer.js +4892 -125
- package/dist/reducer.js.map +1 -1
- package/dist/shortcuts-B_RrufOM.d.ts +822 -0
- package/dist/testing.d.ts +529 -569
- package/dist/testing.js +2588 -934
- package/dist/testing.js.map +1 -1
- package/dist/types-BxXRINRF.d.ts +118 -0
- package/package.json +39 -144
- package/dist/HandView-Bi2lxzQP.d.ts +0 -79
- package/dist/ResourceCounter-BFxJknqp.d.ts +0 -98
- package/dist/ThemeProvider-CElaL2ik.d.ts +0 -99
- package/dist/attributes-DbvyMbXw.d.ts +0 -68
- package/dist/browser-interaction.d.ts +0 -910
- package/dist/browser-interaction.js +0 -119
- package/dist/browser-interaction.js.map +0 -1
- package/dist/chunk-32PFKDV7.js +0 -11705
- package/dist/chunk-32PFKDV7.js.map +0 -1
- package/dist/chunk-3BAD4VC2.js +0 -263
- package/dist/chunk-3BAD4VC2.js.map +0 -1
- package/dist/chunk-3SKDNDPG.js +0 -104
- package/dist/chunk-3SKDNDPG.js.map +0 -1
- package/dist/chunk-5YV2WHJ4.js +0 -25
- package/dist/chunk-5YV2WHJ4.js.map +0 -1
- package/dist/chunk-ANRB4XKA.js +0 -90
- package/dist/chunk-ANRB4XKA.js.map +0 -1
- package/dist/chunk-BI4G3PM2.js +0 -1864
- package/dist/chunk-BI4G3PM2.js.map +0 -1
- package/dist/chunk-EVCLTY4Q.js +0 -935
- package/dist/chunk-EVCLTY4Q.js.map +0 -1
- package/dist/chunk-GDPKTBUR.js +0 -564
- package/dist/chunk-GDPKTBUR.js.map +0 -1
- package/dist/chunk-H6VDGFL5.js +0 -2903
- package/dist/chunk-H6VDGFL5.js.map +0 -1
- package/dist/chunk-LR3ZTWQF.js +0 -5960
- package/dist/chunk-LR3ZTWQF.js.map +0 -1
- package/dist/chunk-PF7L4BMG.js +0 -117
- package/dist/chunk-PF7L4BMG.js.map +0 -1
- package/dist/chunk-PN5O6GG2.js +0 -32
- package/dist/chunk-PN5O6GG2.js.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -10
- package/dist/chunk-PZ5AY32C.js.map +0 -1
- package/dist/chunk-S2JEDVWH.js +0 -7165
- package/dist/chunk-S2JEDVWH.js.map +0 -1
- package/dist/chunk-T3ZKNUZ7.js +0 -1
- package/dist/chunk-T3ZKNUZ7.js.map +0 -1
- package/dist/chunk-UGH54WZ2.js +0 -223
- package/dist/chunk-UGH54WZ2.js.map +0 -1
- package/dist/chunk-VCDWP5VL.js +0 -746
- package/dist/chunk-VCDWP5VL.js.map +0 -1
- package/dist/chunk-VDXOF4FW.js +0 -69
- package/dist/chunk-VDXOF4FW.js.map +0 -1
- package/dist/chunk-WHR5UW3F.js +0 -1988
- package/dist/chunk-WHR5UW3F.js.map +0 -1
- package/dist/chunk-Y75CFE77.js +0 -17
- package/dist/chunk-Y75CFE77.js.map +0 -1
- package/dist/chunk-Z7QCREWI.js +0 -3435
- package/dist/chunk-Z7QCREWI.js.map +0 -1
- package/dist/chunk-ZZGN2ATS.js +0 -1941
- package/dist/chunk-ZZGN2ATS.js.map +0 -1
- package/dist/components-gAbIz7hK.d.ts +0 -1284
- package/dist/definitions-BgmV_GhC.d.ts +0 -300
- package/dist/diagnostics-DIi3ina7.d.ts +0 -50
- package/dist/digest.d.ts +0 -8
- package/dist/game-CsZScpcn.d.ts +0 -763
- package/dist/hex-board-view-aKkblDp6.d.ts +0 -1228
- package/dist/index-CcT9Q7N7.d.ts +0 -193
- package/dist/index.d-DjzoK7zn.d.ts +0 -2101
- package/dist/package-set.d.ts +0 -13
- package/dist/package-set.js +0 -12
- package/dist/package-set.js.map +0 -1
- package/dist/player-state-Cqpyeql0.d.ts +0 -371
- package/dist/plugin-runtime-contract.d.ts +0 -17
- package/dist/plugin-runtime-contract.js +0 -92
- package/dist/plugin-runtime-contract.js.map +0 -1
- package/dist/primitive-props-BNHDkgd7.d.ts +0 -16
- package/dist/protocol-dYgafTYY.d.ts +0 -314
- package/dist/reducer/advanced.d.ts +0 -82
- package/dist/reducer/advanced.js +0 -51
- package/dist/reducer/advanced.js.map +0 -1
- package/dist/reducer-contract.d.ts +0 -11
- package/dist/reducer-contract.js +0 -16
- package/dist/reducer-contract.js.map +0 -1
- package/dist/reference-games/index.d.ts +0 -31
- package/dist/reference-games/index.js +0 -48
- package/dist/reference-games/index.js.map +0 -1
- package/dist/runtime/primitives.d.ts +0 -250
- package/dist/runtime/primitives.js +0 -189
- package/dist/runtime/primitives.js.map +0 -1
- package/dist/runtime/runtime-api.d.ts +0 -2
- package/dist/runtime/runtime-api.js +0 -1
- package/dist/runtime/runtime-api.js.map +0 -1
- package/dist/runtime/workspace-contract.d.ts +0 -392
- package/dist/runtime/workspace-contract.js +0 -28
- package/dist/runtime/workspace-contract.js.map +0 -1
- package/dist/runtime-RJ5orDJU.d.ts +0 -1699
- package/dist/runtime-api-Bz5pwNU_.d.ts +0 -296
- package/dist/runtime-json-CQ9QbLZ5.d.ts +0 -5
- package/dist/runtime.d.ts +0 -83
- package/dist/runtime.js +0 -242
- package/dist/runtime.js.map +0 -1
- package/dist/schema.d.ts +0 -379
- package/dist/stale-contract-artifact-error-XLaweZtF.d.ts +0 -18
- package/dist/testing-compiler.d.ts +0 -37
- package/dist/testing-compiler.js +0 -272
- package/dist/testing-compiler.js.map +0 -1
- package/dist/testing-runtime.d.ts +0 -119
- package/dist/testing-runtime.js +0 -212
- package/dist/testing-runtime.js.map +0 -1
- package/dist/types-DJj5MJkl.d.ts +0 -256
- package/dist/types-DR7DoB1x.d.ts +0 -28
- package/dist/types-JWCYHmu7.d.ts +0 -122
- package/dist/types.d.ts +0 -1958
- package/dist/types.js +0 -14
- package/dist/types.js.map +0 -1
- package/dist/ui/components.d.ts +0 -17
- package/dist/ui/components.js +0 -216
- package/dist/ui/components.js.map +0 -1
- package/dist/ui/defaults.d.ts +0 -19
- package/dist/ui/defaults.js +0 -104
- package/dist/ui/defaults.js.map +0 -1
- package/dist/ui/player-state.d.ts +0 -2
- package/dist/ui/player-state.js +0 -1
- package/dist/ui/player-state.js.map +0 -1
- package/dist/ui/plugin-styles.css +0 -2
- package/dist/ui-contract-GTUPkf8z.d.ts +0 -1168
- package/dist/ui.d.ts +0 -317
- package/dist/ui.js +0 -277
- package/dist/ui.js.map +0 -1
- 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
|
|
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
|
-
|
|
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.
|
|
34
19
|
|
|
35
|
-
|
|
36
|
-
|
|
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.
|
|
49
|
-
- the **assembler**: `game.assemble({ initial, initialPhase, phases,
|
|
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
|
|
@@ -93,7 +82,7 @@ const ownCard = play.inputs.card({
|
|
|
93
82
|
id: "own-card",
|
|
94
83
|
errorCode: "NOT_YOUR_CARD",
|
|
95
84
|
test: ({ q, playerId, targetId }) =>
|
|
96
|
-
q.zone
|
|
85
|
+
q.zone("hand", playerId).includes(targetId),
|
|
97
86
|
},
|
|
98
87
|
});
|
|
99
88
|
|
|
@@ -106,16 +95,18 @@ export default play.define({
|
|
|
106
95
|
inputs: { cardId: ownCard },
|
|
107
96
|
reduce({ tx, input, q }) {
|
|
108
97
|
// tx.state.phase.leadCardId and input.params.cardId are inferred.
|
|
109
|
-
tx.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
cardId: input.params.cardId,
|
|
98
|
+
tx.moveComponentToZone({
|
|
99
|
+
componentId: input.params.cardId,
|
|
100
|
+
to: { zoneId: "trick" },
|
|
101
|
+
playedBy: input.playerId,
|
|
114
102
|
});
|
|
115
103
|
tx.patchPhaseState({ leadCardId: input.params.cardId });
|
|
116
104
|
const next = q.player.nextInOrder(input.playerId);
|
|
117
|
-
if (next)
|
|
118
|
-
|
|
105
|
+
if (next) {
|
|
106
|
+
tx.patchPublicState({ currentPlayerId: next });
|
|
107
|
+
tx.setActivePlayers([next]);
|
|
108
|
+
}
|
|
109
|
+
if (tx.q.zone("hand", input.playerId).length === 0) {
|
|
119
110
|
return tx.transition("setup");
|
|
120
111
|
}
|
|
121
112
|
},
|
|
@@ -124,6 +115,42 @@ export default play.define({
|
|
|
124
115
|
});
|
|
125
116
|
```
|
|
126
117
|
|
|
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
|
+
```
|
|
147
|
+
|
|
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.
|
|
153
|
+
|
|
127
154
|
```ts
|
|
128
155
|
// app/game.ts — assembly
|
|
129
156
|
import { game } from "./game-model";
|
|
@@ -138,16 +165,11 @@ export default game.assemble({
|
|
|
138
165
|
},
|
|
139
166
|
initialPhase: "setup",
|
|
140
167
|
phases: { setup, play },
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
hand: q.zone.playerCards(playerId, "hand"),
|
|
147
|
-
current: state.publicState.currentPlayerId,
|
|
148
|
-
}),
|
|
149
|
-
}),
|
|
150
|
-
},
|
|
168
|
+
view: game.view(({ state, playerId, q }) => ({
|
|
169
|
+
me: playerId,
|
|
170
|
+
hand: q.zone("hand", playerId),
|
|
171
|
+
current: state.publicState.currentPlayerId,
|
|
172
|
+
})),
|
|
151
173
|
});
|
|
152
174
|
```
|
|
153
175
|
|
|
@@ -159,18 +181,121 @@ imports the assembled game, so there is no import cycle.
|
|
|
159
181
|
New workspaces keep authored starter code in `app/game.ts` and `ui/App.tsx`.
|
|
160
182
|
Import the manifest directly. `compileManifest(manifest)` provides inferred ID schemas,
|
|
161
183
|
table schemas, fresh initial tables, and board metadata in memory. `createGame`
|
|
162
|
-
|
|
163
|
-
`
|
|
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.
|
|
164
188
|
No authoring generation step or shared workspace files are needed.
|
|
165
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
|
+
|
|
166
276
|
## Reducer runner contract
|
|
167
277
|
|
|
168
|
-
`createReducerBundle(game)` returns exactly the contract version and
|
|
169
|
-
operations: `
|
|
170
|
-
and `project({ state, playerIds })`. The runner contract is `0.
|
|
171
|
-
require that exact version.
|
|
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
|
|
172
289
|
`{ state, terminal?, events? }`, preserving outcomes and events from initial
|
|
173
|
-
phase entry and
|
|
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.
|
|
174
299
|
|
|
175
300
|
The authoritative state is explicit on every dispatch and projection. A host
|
|
176
301
|
may retain a warm worker and SDK caches, but replaying the same state and input
|
|
@@ -182,3 +307,287 @@ owns the monotonically increasing version, perspective, and action-set identity.
|
|
|
182
307
|
The plugin frame basis contains `version`, `actionSetVersion`, and
|
|
183
308
|
`perspectivePlayerId`; it has no generation counter. Hosts merge the separately
|
|
184
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`, `useCardRow`, `useActiveCard`, `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. A drop area can bind typed inputs with
|
|
356
|
+
`{ interaction: "play.move", params: { destination: "discard" } }`; the SDK
|
|
357
|
+
adds the dragged card and admits the complete draft against current domains.
|
|
358
|
+
A ready bound single-card drop submits the action even when its commit mode is
|
|
359
|
+
manual; unbound drops and many-card drafts keep their normal commit behavior.
|
|
360
|
+
`game.drag.getIsDropTarget(target)` uses the same admission as dropping.
|
|
361
|
+
Interaction `getSubmitProps(params)` and `getSubmitHandler(params)` accept the
|
|
362
|
+
same explicit params as `submit(params)`, retaining pending and connection gates.
|
|
363
|
+
`fanLayout` computes arc positions that always fit a hand,
|
|
364
|
+
`handFan` adds a focused, readable card with configurable presets and timing,
|
|
365
|
+
and
|
|
366
|
+
`originsFeature` adds `card.getOrigin()` so a newly shown card can animate from
|
|
367
|
+
the zone or player it came from.
|
|
368
|
+
|
|
369
|
+
## Local hand sorting
|
|
370
|
+
|
|
371
|
+
Install `handFeature(core, context, { zones })` with named sorts for each zone:
|
|
372
|
+
`{ hand: { defaultSort: "rank", sorts: { rank: { compare } } } }`.
|
|
373
|
+
Comparators receive the seat's projected `Card` objects; narrow `card.hidden`
|
|
374
|
+
before reading visible card data. Equal comparisons retain source order.
|
|
375
|
+
|
|
376
|
+
`game.hand.getSortModes(zone)` returns typed mode IDs in declaration order.
|
|
377
|
+
Read `getSortMode(zone)` and `getSortedCardIds(zone)`, then call
|
|
378
|
+
`setSortMode(zone, mode)` from your UI. The zone carries its canonical host;
|
|
379
|
+
each host's choice is independent. Labels, buttons and cycling are authored UI.
|
|
380
|
+
Without a selected or default mode, the order comes directly from the source.
|
|
381
|
+
The feature retains the zone's selected/selectable card ID helpers.
|
|
382
|
+
|
|
383
|
+
Sort changes notify subscribers and preserve drafts and card selection without
|
|
384
|
+
submitting. Choices survive frames and reset on source or seat changes. Captured
|
|
385
|
+
`game.hand` branches retain their modes, and sorted IDs are derived from the
|
|
386
|
+
projected zone passed to that branch. Old lifetime setters and disposed setters
|
|
387
|
+
do nothing. See [Optional features](../../docs/api/features.md).
|
|
388
|
+
|
|
389
|
+
## Opt-in game shortcuts
|
|
390
|
+
|
|
391
|
+
Install `shortcutsFeature` in the same `createGameHook<Definition>()` binding as
|
|
392
|
+
other features, and export its inferred `useGameShortcuts` and
|
|
393
|
+
`useShortcutTarget` and `useShortcutHints` hooks. A child inside the bound `GameProvider` installs the
|
|
394
|
+
app's bindings once:
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
function AppShortcuts() {
|
|
398
|
+
useGameShortcuts({
|
|
399
|
+
bindings: [
|
|
400
|
+
{
|
|
401
|
+
kind: "interaction",
|
|
402
|
+
keys: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
|
|
403
|
+
label: "Draw cards",
|
|
404
|
+
target: "zone",
|
|
405
|
+
zoneId: "deck",
|
|
406
|
+
interaction: "play.draw",
|
|
407
|
+
inputs: ({ key }) => ({ count: Number(key) }),
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
kind: "interaction",
|
|
411
|
+
keys: ["f"],
|
|
412
|
+
label: "Flip card",
|
|
413
|
+
target: "card",
|
|
414
|
+
input: "card",
|
|
415
|
+
interaction: "play.flip",
|
|
416
|
+
inputs: ({ target }) => ({ card: target.value }),
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
kind: "local",
|
|
420
|
+
keys: ["s"],
|
|
421
|
+
label: "Change hand order",
|
|
422
|
+
target: "zone",
|
|
423
|
+
zoneId: "hand",
|
|
424
|
+
getIsAvailable: ({ target }) => target.hostId === playerId,
|
|
425
|
+
run: ({ target }) => changeAuthoredOrder(target.hostId),
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
});
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
These interaction and input names are examples from an authored reducer;
|
|
434
|
+
`playerId` and `changeAuthoredOrder` are supplied by the game. The
|
|
435
|
+
binding's discriminated types derive from that reducer: interaction names,
|
|
436
|
+
input keys, target kinds, and every required payload field must match. A game
|
|
437
|
+
that exposes a counted draw submits one `count` payload; shortcuts never repeat
|
|
438
|
+
a draw-one action. No actions or keys are supplied by default.
|
|
439
|
+
|
|
440
|
+
Local bindings run game-authored presentation callbacks for a focused zone
|
|
441
|
+
control or a focused card in that zone. A focused card resolves its current
|
|
442
|
+
containing zone and actual host, including opaque hidden card references. Local
|
|
443
|
+
bindings always use real DOM focus, including focus acquired by clicking; hover
|
|
444
|
+
alone never activates them. Mouse browsing can change the interaction target
|
|
445
|
+
while local actions retain the focused control's zone. They are unavailable
|
|
446
|
+
during a card pointer gesture or its settling drag overlay. Games own any
|
|
447
|
+
further restrictions through the optional pure `getIsAvailable` predicate.
|
|
448
|
+
They do not submit source commands, alter interaction drafts, or require the
|
|
449
|
+
player's reducer turn; an interaction request does not block a local action.
|
|
450
|
+
`run` returns `void` or `Promise<void>` and is never evaluated for hints.
|
|
451
|
+
The SDK supplies no sorting modes or cycling policy.
|
|
452
|
+
|
|
453
|
+
Card controls using `useCardGesture` share its canonical pointer and keyboard
|
|
454
|
+
focus. Zone controls spread
|
|
455
|
+
`useShortcutTarget({ kind: "zone", zoneId: "deck", hostId: "table" }).props`;
|
|
456
|
+
`hostId` must be the actual host allowed by that zone's attachment. Selectable
|
|
457
|
+
board and tile controls can use the same hook with their canonical
|
|
458
|
+
`SelectionTarget`. Hidden cards retain their opaque seat references.
|
|
459
|
+
|
|
460
|
+
Keys are exact browser `event.key` strings. The adapter ignores held repeats,
|
|
461
|
+
composition, editing controls, modifier combinations, and open modal or menu
|
|
462
|
+
surfaces. Native radio inputs can carry a zone target for authored shortcuts;
|
|
463
|
+
their ordinary arrow and space keys remain native unless explicitly bound.
|
|
464
|
+
It prevents the browser default only when a single eligible binding
|
|
465
|
+
handles the key. Source and seat changes clear the gesture target; unmounting
|
|
466
|
+
removes the listener and binding hints.
|
|
467
|
+
|
|
468
|
+
Input factories must be pure: eligibility and hints evaluate them against the
|
|
469
|
+
current projected domains. Availability predicates for local bindings must
|
|
470
|
+
also be pure. `game.shortcuts.getHints(target, activity)` returns only
|
|
471
|
+
currently usable, unambiguous keys with their labels and `kind`. Interaction
|
|
472
|
+
hints also carry their typed interaction name; local hints do not. Both variants
|
|
473
|
+
share key collision handling. Headless callers supply local focus explicitly as
|
|
474
|
+
`activity: { focusedTarget, pointerActive }`; omitting activity admits only
|
|
475
|
+
interaction bindings. `useShortcutHints(target)` reads those hints reactively
|
|
476
|
+
and returns no hints when the feature is absent. The mounted
|
|
477
|
+
`useGameShortcuts` hook returns `{ target, hints }` for a reserved help/status
|
|
478
|
+
area. Rejected actions and transport failures use the binding's existing
|
|
479
|
+
`onError` handler; callbacks from an old source, seat, or unmounted child are
|
|
480
|
+
ignored. `game.shortcuts.handle(key, target, activity)` returns `null` for an
|
|
481
|
+
unhandled
|
|
482
|
+
key, or a promise of `{ kind: "local" }` or
|
|
483
|
+
`{ kind: "interaction", result: SubmitResult }`. Callback failures reject that
|
|
484
|
+
promise and use the same `onError` path. Interaction bindings use the same
|
|
485
|
+
atomic `interaction.submit(params)` path available to authored action buttons.
|
|
486
|
+
Explicit params are validated against the current projected descriptor without
|
|
487
|
+
mutating drafts. Existing `submit()` continues to submit the interaction draft
|
|
488
|
+
and defaults; both forms share availability and in-flight lifecycle barriers.
|
|
489
|
+
The headless feature installs no DOM listeners.
|
|
490
|
+
|
|
491
|
+
## Local development and tests
|
|
492
|
+
|
|
493
|
+
`localSource(game, { players, seed, as, options })` executes the production reducer
|
|
494
|
+
and materializes the selected seat. `scenarioSource` starts from authored scenario
|
|
495
|
+
checkpoints. Keep these executable game imports in your local development entry;
|
|
496
|
+
the hosted entry uses only `iframeSource()` and type imports.
|
|
497
|
+
|
|
498
|
+
Local sources expose `inspect`, bounded `explore`, typed explicit-actor `apply`,
|
|
499
|
+
`switchSeat`, `checkpoint`, and validated `restore`. A JSON checkpoint preserves
|
|
500
|
+
visible pending selections and terminal state; restoring does not replay commands.
|
|
501
|
+
Concealed selections expire after any other accepted input or restore. Each
|
|
502
|
+
accepted extension of that same unfinished selection issues a new validity stamp
|
|
503
|
+
for the immediately following frame. This prevents drafts from tracking hidden
|
|
504
|
+
tiles or cards through a shuffle, including a shuffle that leaves their position
|
|
505
|
+
unchanged.
|
|
506
|
+
`createTestSource(snapshot)` supplies controlled frames and acknowledgements for
|
|
507
|
+
instance and React tests. Static and hosted sources do not expose `apply`.
|
|
508
|
+
|
|
509
|
+
Hosts import `assertReducerBundleContract`, `REDUCER_CONTRACT_VERSION`,
|
|
510
|
+
`ReducerWire` types and `ReducerWireZod` schemas from `/reducer`. Canonical iframe
|
|
511
|
+
and gameplay websocket schemas plus `materializePluginGameplayFrame` live at the
|
|
512
|
+
root. Materialize the requested seat directly from its projection before publishing it;
|
|
513
|
+
sources publish the canonical seat view and submit the exact frame basis.
|
|
514
|
+
|
|
515
|
+
Visible cards share one complete `ViewCard` shape: `id`, `cardType`, and JSON
|
|
516
|
+
`properties`, plus optional `name`, `text`, `frontImage`, and `backImage`.
|
|
517
|
+
`ViewCardSchema` validates this shape. Reducer and gameplay `cardViewsById` maps
|
|
518
|
+
contain these objects directly. Headless `card.view` preserves the manifest's
|
|
519
|
+
card identity, category, and property inference and is deeply readonly; table-only
|
|
520
|
+
`cardSetId` and `componentType` are absent. Concealed cards have a positional
|
|
521
|
+
opaque seat identity, no view, and an optional separate `cardBacksById` entry.
|
|
522
|
+
This wire format requires plugin protocol version 10 and reducer contract `0.11.0`.
|
|
523
|
+
|
|
524
|
+
Tiles use opaque `SeatTileRef` values even when visible. A projected tile is either
|
|
525
|
+
`{ disclosure: "visible", ref, tileTypeId, name, ownerId, fields, properties, frontImage? }` or
|
|
526
|
+
`{ disclosure: "concealed", ref, appearance }`; an omitted tile contributes no
|
|
527
|
+
entry or count. Public appearances are authored independently of secret faces.
|
|
528
|
+
Boards and zones each carry their own projected tiles. Only visible tiles
|
|
529
|
+
contribute cells, edges, vertices, and relations to the seat's board topology.
|
|
530
|
+
Use `phase.inputs.tile({ from: [zoneId] })` or
|
|
531
|
+
`phase.inputs.tile({ boards: [boardId] })` to select a tile. Reducers receive
|
|
532
|
+
its authoritative identity after the SDK validates and resolves the reference.
|
|
533
|
+
`tx.setTileDisclosure` controls face audiences and public appearance;
|
|
534
|
+
`tx.setBoardVisibility` controls the board's audience. Location visibility always
|
|
535
|
+
caps tile disclosure. Authored defaults cannot identify concealed tiles or cards.
|
|
536
|
+
Game-owned view fields can publish visible references through `references.tile`
|
|
537
|
+
and `references.space`; arbitrary authored text and JSON remain explicit publication.
|
|
538
|
+
|
|
539
|
+
Every `tx.emit` event declares `audience: { kind: "public" }` or
|
|
540
|
+
`{ kind: "seats", playerIds }`. A typed detail `{ kind: "tile", tileId }` is
|
|
541
|
+
projected only when that tile is visible to the audience. Identity-specific event
|
|
542
|
+
details and view references never track a concealed tile. Clients receive only
|
|
543
|
+
their own events and admitted tile references.
|
|
544
|
+
|
|
545
|
+
Card sets contain their authored `cards`, `cardSchema`, and `defaultHome` directly.
|
|
546
|
+
Standard playing cards are game-owned definitions with ordinary suit/rank
|
|
547
|
+
properties; the SDK does not synthesize inventories or assign built-in rules.
|
|
548
|
+
|
|
549
|
+
The exported `createGestureRecognizer(down, callbacks, options)` defaults to
|
|
550
|
+
upward touch dragging so card containers retain sideways scrolling. A row that
|
|
551
|
+
fits uses `{ scrub: true }`: a sliding finger reports each move through
|
|
552
|
+
`browse`, and turning upward still drags. Pile controls can use
|
|
553
|
+
`{ dragDirection: "any" }` with `touch-action: none` to drag toward a hand below
|
|
554
|
+
or beside them. This classifies pointer intent only; submit an authored interaction
|
|
555
|
+
through the bound game to enforce its rules, without selecting a hidden card ID.
|
|
556
|
+
|
|
557
|
+
### Local card groups and marquee selection
|
|
558
|
+
|
|
559
|
+
`cardSelectionFeature` holds a local list of projected card IDs, separate from
|
|
560
|
+
interaction drafts. Authors decide which zones allow selection and how to draw
|
|
561
|
+
it. Selection clears on an authoritative frame or source/perspective change.
|
|
562
|
+
|
|
563
|
+
```ts
|
|
564
|
+
features(core, context) {
|
|
565
|
+
const selection = cardSelectionFeature(core, context);
|
|
566
|
+
return {
|
|
567
|
+
selection,
|
|
568
|
+
drag: dragFeature(core, context, {
|
|
569
|
+
getSelection(cardId) {
|
|
570
|
+
const ids = selection.root.cardSelection.cardIds;
|
|
571
|
+
return ids.includes(cardId) ? ids : undefined;
|
|
572
|
+
},
|
|
573
|
+
}),
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
Use `game.cardSelection.set(ids)`, `.toggle(id)` and `.clear()` for authored
|
|
579
|
+
controls. A selected group can drop only onto an interaction with a `many(...)`
|
|
580
|
+
card input that admits the entire group. Dropping submits that input once in
|
|
581
|
+
selection order, replacing its draft; it never submits a sequence of single-card
|
|
582
|
+
moves. The reducer remains responsible for validating and applying the group
|
|
583
|
+
atomically. An unselected card retains ordinary single-card draft behavior.
|
|
584
|
+
`useDragOverlay().cardIds` identifies all lifted cards; `cardId` is the anchor.
|
|
585
|
+
|
|
586
|
+
`useMarqueeSelection` from `/react` is an opt-in mouse/pen background gesture.
|
|
587
|
+
Pass `enabled`, `getCards: () => [{ id, element }]`, and
|
|
588
|
+
`onSelect: (ids, additive) => ...`; spread its `props` on the selectable area.
|
|
589
|
+
It reports intersecting cards on release and viewport `bounds` for an authored
|
|
590
|
+
fixed-position overlay. Shift requests additive selection; Escape, pointer
|
|
591
|
+
cancellation, blur or disabling the area cancels. Buttons, inputs and card
|
|
592
|
+
controls keep their own gestures, and touch retains scrolling. A background
|
|
593
|
+
click reports an empty selection. The hook supplies no styling or bulk-action UI.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { f as BoardEdgeId, n as BoardVertexId } from './extract-CwJWDrSD.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 };
|