@dxos/plugin-game 0.0.0

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 (117) hide show
  1. package/LICENSE +105 -0
  2. package/PLUGIN.mdl +334 -0
  3. package/dist/lib/neutral/GameArticle-AUJ4YDKW.mjs +40 -0
  4. package/dist/lib/neutral/GameArticle-AUJ4YDKW.mjs.map +7 -0
  5. package/dist/lib/neutral/GameCard-QBKHIL4Q.mjs +32 -0
  6. package/dist/lib/neutral/GameCard-QBKHIL4Q.mjs.map +7 -0
  7. package/dist/lib/neutral/GamePlugin.mjs +38 -0
  8. package/dist/lib/neutral/GamePlugin.mjs.map +7 -0
  9. package/dist/lib/neutral/GamePlugin.node.mjs +21 -0
  10. package/dist/lib/neutral/GamePlugin.node.mjs.map +7 -0
  11. package/dist/lib/neutral/GamePlugin.workerd.mjs +18 -0
  12. package/dist/lib/neutral/GamePlugin.workerd.mjs.map +7 -0
  13. package/dist/lib/neutral/capabilities/index.mjs +11 -0
  14. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  15. package/dist/lib/neutral/chunk-DSUJWWNN.mjs +98 -0
  16. package/dist/lib/neutral/chunk-DSUJWWNN.mjs.map +7 -0
  17. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  18. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  19. package/dist/lib/neutral/chunk-R4V5PVU7.mjs +50 -0
  20. package/dist/lib/neutral/chunk-R4V5PVU7.mjs.map +7 -0
  21. package/dist/lib/neutral/components/index.mjs +96 -0
  22. package/dist/lib/neutral/components/index.mjs.map +7 -0
  23. package/dist/lib/neutral/containers/index.mjs +11 -0
  24. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  25. package/dist/lib/neutral/create-object-UAD2JUWF.mjs +51 -0
  26. package/dist/lib/neutral/create-object-UAD2JUWF.mjs.map +7 -0
  27. package/dist/lib/neutral/index.mjs +24 -0
  28. package/dist/lib/neutral/index.mjs.map +7 -0
  29. package/dist/lib/neutral/meta.json +1 -0
  30. package/dist/lib/neutral/meta.mjs +8 -0
  31. package/dist/lib/neutral/meta.mjs.map +7 -0
  32. package/dist/lib/neutral/plugin.mjs +12 -0
  33. package/dist/lib/neutral/plugin.mjs.map +7 -0
  34. package/dist/lib/neutral/react-surface-GO3SKEMA.mjs +35 -0
  35. package/dist/lib/neutral/react-surface-GO3SKEMA.mjs.map +7 -0
  36. package/dist/lib/neutral/translations.mjs +34 -0
  37. package/dist/lib/neutral/translations.mjs.map +7 -0
  38. package/dist/lib/neutral/types/index.mjs +20 -0
  39. package/dist/lib/neutral/types/index.mjs.map +7 -0
  40. package/dist/types/dx.config.d.ts +28 -0
  41. package/dist/types/dx.config.d.ts.map +1 -0
  42. package/dist/types/src/GamePlugin.d.ts +4 -0
  43. package/dist/types/src/GamePlugin.d.ts.map +1 -0
  44. package/dist/types/src/GamePlugin.node.d.ts +10 -0
  45. package/dist/types/src/GamePlugin.node.d.ts.map +1 -0
  46. package/dist/types/src/GamePlugin.workerd.d.ts +4 -0
  47. package/dist/types/src/GamePlugin.workerd.d.ts.map +1 -0
  48. package/dist/types/src/capabilities/create-object.d.ts +5 -0
  49. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  50. package/dist/types/src/capabilities/index.d.ts +4 -0
  51. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  52. package/dist/types/src/capabilities/react-surface.d.ts +5 -0
  53. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  54. package/dist/types/src/components/CreateGamePanel.d.ts +19 -0
  55. package/dist/types/src/components/CreateGamePanel.d.ts.map +1 -0
  56. package/dist/types/src/components/CreateGamePanel.stories.d.ts +49 -0
  57. package/dist/types/src/components/CreateGamePanel.stories.d.ts.map +1 -0
  58. package/dist/types/src/components/index.d.ts +2 -0
  59. package/dist/types/src/components/index.d.ts.map +1 -0
  60. package/dist/types/src/containers/GameArticle/GameArticle.d.ts +6 -0
  61. package/dist/types/src/containers/GameArticle/GameArticle.d.ts.map +1 -0
  62. package/dist/types/src/containers/GameArticle/index.d.ts +2 -0
  63. package/dist/types/src/containers/GameArticle/index.d.ts.map +1 -0
  64. package/dist/types/src/containers/GameCard/GameCard.d.ts +6 -0
  65. package/dist/types/src/containers/GameCard/GameCard.d.ts.map +1 -0
  66. package/dist/types/src/containers/GameCard/index.d.ts +2 -0
  67. package/dist/types/src/containers/GameCard/index.d.ts.map +1 -0
  68. package/dist/types/src/containers/index.d.ts +4 -0
  69. package/dist/types/src/containers/index.d.ts.map +1 -0
  70. package/dist/types/src/index.d.ts +3 -0
  71. package/dist/types/src/index.d.ts.map +1 -0
  72. package/dist/types/src/meta.d.ts +33 -0
  73. package/dist/types/src/meta.d.ts.map +1 -0
  74. package/dist/types/src/plugin.d.ts +3 -0
  75. package/dist/types/src/plugin.d.ts.map +1 -0
  76. package/dist/types/src/translations.d.ts +36 -0
  77. package/dist/types/src/translations.d.ts.map +1 -0
  78. package/dist/types/src/types/Game.d.ts +67 -0
  79. package/dist/types/src/types/Game.d.ts.map +1 -0
  80. package/dist/types/src/types/GameCapabilities.d.ts +10 -0
  81. package/dist/types/src/types/GameCapabilities.d.ts.map +1 -0
  82. package/dist/types/src/types/index.d.ts +5 -0
  83. package/dist/types/src/types/index.d.ts.map +1 -0
  84. package/dist/types/src/types/types.d.ts +46 -0
  85. package/dist/types/src/types/types.d.ts.map +1 -0
  86. package/dist/types/src/util/index.d.ts +2 -0
  87. package/dist/types/src/util/index.d.ts.map +1 -0
  88. package/dist/types/src/util/load-game.d.ts +21 -0
  89. package/dist/types/src/util/load-game.d.ts.map +1 -0
  90. package/dist/types/tsconfig.tsbuildinfo +1 -0
  91. package/dx.config.ts +40 -0
  92. package/package.json +124 -0
  93. package/src/GamePlugin.node.tsx +24 -0
  94. package/src/GamePlugin.tsx +27 -0
  95. package/src/GamePlugin.workerd.tsx +13 -0
  96. package/src/capabilities/create-object.ts +67 -0
  97. package/src/capabilities/index.ts +8 -0
  98. package/src/capabilities/react-surface.tsx +36 -0
  99. package/src/components/CreateGamePanel.stories.tsx +92 -0
  100. package/src/components/CreateGamePanel.tsx +127 -0
  101. package/src/components/index.ts +5 -0
  102. package/src/containers/GameArticle/GameArticle.tsx +42 -0
  103. package/src/containers/GameArticle/index.ts +5 -0
  104. package/src/containers/GameCard/GameCard.tsx +35 -0
  105. package/src/containers/GameCard/index.ts +5 -0
  106. package/src/containers/index.ts +8 -0
  107. package/src/index.ts +6 -0
  108. package/src/meta.ts +9 -0
  109. package/src/plugin.ts +9 -0
  110. package/src/translations.ts +34 -0
  111. package/src/types/Game.ts +106 -0
  112. package/src/types/GameCapabilities.ts +19 -0
  113. package/src/types/index.ts +9 -0
  114. package/src/types/types.ts +53 -0
  115. package/src/util/index.ts +5 -0
  116. package/src/util/load-game.ts +41 -0
  117. package/src/vite-env.d.ts +10 -0
package/LICENSE ADDED
@@ -0,0 +1,105 @@
1
+ # Functional Source License, Version 1.1, ALv2 Future License
2
+
3
+ ## Abbreviation
4
+
5
+ FSL-1.1-Apache-2.0
6
+
7
+ ## Notice
8
+
9
+ Copyright 2026 DXOS
10
+
11
+ ## Terms and Conditions
12
+
13
+ ### Licensor ("We")
14
+
15
+ The party offering the Software under these Terms and Conditions.
16
+
17
+ ### The Software
18
+
19
+ The "Software" is each version of the software that we make available under
20
+ these Terms and Conditions, as indicated by our inclusion of these Terms and
21
+ Conditions with the Software.
22
+
23
+ ### License Grant
24
+
25
+ Subject to your compliance with this License Grant and the Patents,
26
+ Redistribution and Trademark clauses below, we hereby grant you the right to
27
+ use, copy, modify, create derivative works, publicly perform, publicly display
28
+ and redistribute the Software for any Permitted Purpose identified below.
29
+
30
+ ### Permitted Purpose
31
+
32
+ A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
33
+ means making the Software available to others in a commercial product or
34
+ service that:
35
+
36
+ 1. substitutes for the Software;
37
+
38
+ 2. substitutes for any other product or service we offer using the Software
39
+ that exists as of the date we make the Software available; or
40
+
41
+ 3. offers the same or substantially similar functionality as the Software.
42
+
43
+ Permitted Purposes specifically include using the Software:
44
+
45
+ 1. for your internal use and access;
46
+
47
+ 2. for non-commercial education;
48
+
49
+ 3. for non-commercial research; and
50
+
51
+ 4. in connection with professional services that you provide to a licensee
52
+ using the Software in accordance with these Terms and Conditions.
53
+
54
+ ### Patents
55
+
56
+ To the extent your use for a Permitted Purpose would necessarily infringe our
57
+ patents, the license grant above includes a license under our patents. If you
58
+ make a claim against any party that the Software infringes or contributes to
59
+ the infringement of any patent, then your patent license to the Software ends
60
+ immediately.
61
+
62
+ ### Redistribution
63
+
64
+ The Terms and Conditions apply to all copies, modifications and derivatives of
65
+ the Software.
66
+
67
+ If you redistribute any copies, modifications or derivatives of the Software,
68
+ you must include a copy of or a link to these Terms and Conditions and not
69
+ remove any copyright notices provided in or with the Software.
70
+
71
+ ### Disclaimer
72
+
73
+ THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
74
+ IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
75
+ PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
76
+
77
+ IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
78
+ SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
79
+ EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
80
+
81
+ ### Trademarks
82
+
83
+ Except for displaying the License Details and identifying us as the origin of
84
+ the Software, you have no right under these Terms and Conditions to use our
85
+ trademarks, trade names, service marks or product names.
86
+
87
+ ## Grant of Future License
88
+
89
+ We hereby irrevocably grant you an additional license to use the Software under
90
+ the Apache License, Version 2.0 that is effective on the second anniversary of
91
+ the date we make the Software available. On or after that date, you may use the
92
+ Software under the Apache License, Version 2.0, in which case the following
93
+ will apply:
94
+
95
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
96
+ this file except in compliance with the License.
97
+
98
+ You may obtain a copy of the License at
99
+
100
+ http://www.apache.org/licenses/LICENSE-2.0
101
+
102
+ Unless required by applicable law or agreed to in writing, software distributed
103
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
104
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
105
+ specific language governing permissions and limitations under the License.
package/PLUGIN.mdl ADDED
@@ -0,0 +1,334 @@
1
+ ---
2
+ id: org.dxos.plugin.game
3
+ name: GamePlugin
4
+ version: 0.1.0
5
+ ---
6
+
7
+ A generic game host plugin for `DXOS` Composer that provides shared infrastructure for
8
+ multi-player, local-first turn-based games. Variant plugins (e.g. plugin-chess,
9
+ plugin-tictactoe) contribute a `GameVariant` capability and game-specific surfaces;
10
+ plugin-game supplies the `Game` ECHO type, create flow, and dispatch scaffolding.
11
+
12
+ ## Extensions
13
+
14
+ The following extension dialects are used in this document.
15
+ Each extension is defined in the Appendix or resolved via its URI.
16
+
17
+ | Term | URI |
18
+ |-------------|--------------------------------|
19
+ | `type` | `org.dxos.mdl.type@1.0` |
20
+ | `feat` | `org.dxos.mdl.feat@1.0` |
21
+ | `test` | `org.dxos.mdl.test@1.0` |
22
+ | `component` | `org.dxos.mdl.component@1.0` |
23
+ | `op` | `org.dxos.mdl.op@1.0` |
24
+
25
+ ## Types
26
+
27
+ ```mdl
28
+ type Player
29
+ fields:
30
+ role: string # variant-defined (e.g. "white", "x"); required
31
+ identity?: string # DID of a human player; absent for AI / empty seats
32
+ name?: string # display label for AI or guest players
33
+ ```
34
+
35
+ ```mdl
36
+ type Game
37
+ fields:
38
+ name?: string
39
+ players?: Player[] # populated during or after creation
40
+ variant: Ref<Unknown> # reference to variant-specific state object (e.g. Chess.State)
41
+ typename: org.dxos.type.game
42
+ version: 0.1.0
43
+ ```
44
+
45
+ ```mdl
46
+ type GameVariant
47
+ desc: |
48
+ Contribution interface from a variant plugin. Registered via
49
+ `Capability.contributes(GameCapabilities.VariantProvider, variant)`.
50
+ fields:
51
+ id: string # stable variant id; matches the variant state typename
52
+ label: string # human-readable name shown in the picker
53
+ icon?: string # Phosphor icon name
54
+ variantSchema: Schema # Effect/Schema for the ECHO state object
55
+ inputSchema?: Schema # optional form schema shown after variant is selected
56
+ roles: string[] # player roles (e.g. ["white", "black"])
57
+ createVariant: Effect # factory that builds the variant ECHO state object
58
+ card?: ComponentType # optional Card surface component
59
+ article?: ComponentType # optional Article/Section surface component
60
+ ```
61
+
62
+ ## Components
63
+
64
+ ```mdl
65
+ component CreateGamePanel
66
+ desc: |
67
+ Two-stage creation panel. Stage 1 shows a searchable variant picker.
68
+ Stage 2 (optional) shows a form rendered from the variant's inputSchema.
69
+ props:
70
+ target: Space | Obj
71
+ onCreateObject: (args: { variantId: string; input?: Record }) => void
72
+ variants?: GameVariant[] # override; defaults to GameCapabilities.VariantProvider
73
+ state:
74
+ selectedId?: string # id of the variant chosen in stage 1
75
+ actions:
76
+ selectVariant(id: string)
77
+ submitForm(input: Record)
78
+ layout: |
79
+ ┌─────────────────────────┐
80
+ │ Stage 1: Variant Picker │
81
+ │ ┌───────────────────┐ │
82
+ │ │ Search input │ │
83
+ │ ├───────────────────┤ │
84
+ │ │ variant row │ │
85
+ │ │ variant row │ │
86
+ │ └───────────────────┘ │
87
+ ├─────────────────────────┤
88
+ │ Stage 2: Input Form │
89
+ │ (rendered from schema) │
90
+ │ [Submit] │
91
+ └─────────────────────────┘
92
+ ```
93
+
94
+ ```mdl
95
+ component GameArticle
96
+ desc: |
97
+ Article / Section surface for Game objects. Resolves the variant via
98
+ `game.variant` ref, finds the matching `GameVariant` by typename, and
99
+ delegates rendering to `variant.article`.
100
+ props:
101
+ game: Game
102
+ role?: string
103
+ attendableId?: string
104
+ state:
105
+ variant: Unknown # live variant state object from the Ref
106
+ match?: GameVariant # resolved GameVariant from capabilities
107
+ layout: |
108
+ ┌─────────────────────────┐
109
+ │ [variant.article] │
110
+ │ (fallback: unsupported │
111
+ │ variant message) │
112
+ └─────────────────────────┘
113
+ ```
114
+
115
+ ```mdl
116
+ component GameCard
117
+ desc: |
118
+ Card surface for Game objects. Follows the same variant-resolution
119
+ logic as GameArticle and delegates to `variant.card`.
120
+ props:
121
+ game: Game
122
+ role?: string
123
+ state:
124
+ variant: Unknown
125
+ match?: GameVariant
126
+ ```
127
+
128
+ ## Operations
129
+
130
+ ```mdl
131
+ op createGame
132
+ desc: |
133
+ Creates a variant state object via `variant.createVariant`, adds it
134
+ to the space as a hidden object, then wraps it in a top-level Game.
135
+ If the second write fails the variant state object is rolled back.
136
+ input:
137
+ variantId: string
138
+ input?: Record # variant-specific form values
139
+ target: Space | Obj
140
+ targetNodeId?: string
141
+ output: Game
142
+ errors:
143
+ UnknownVariant: no registered GameVariant matches variantId
144
+ effects: [echo:write]
145
+ requires: [GameCapabilities.VariantProvider, SpaceOperation.AddObject, SpaceOperation.RemoveObjects]
146
+ note: |
147
+ Invoked by CreateObjectEntry.createObject.
148
+ Variant state is added with `hidden: true` so it does not appear
149
+ as a top-level item in the user's space graph.
150
+ ```
151
+
152
+ ## Features
153
+
154
+ ```mdl
155
+ feat F-1: Variant Registration
156
+
157
+ req F-1.1:
158
+ when: a variant plugin loads
159
+ then: it contributes a GameVariant via `Capability.contributes(GameCapabilities.VariantProvider, …)`
160
+
161
+ req F-1.2:
162
+ when: GameCapabilities.VariantProvider contributions are queried
163
+ then: all registered variants are returned as an ordered list
164
+ ```
165
+
166
+ ```mdl
167
+ feat F-2: Game Creation
168
+
169
+ req F-2.1:
170
+ when: user opens the create-object flow for type `org.dxos.type.game`
171
+ then: CreateGamePanel is shown with a searchable list of available variants
172
+
173
+ req F-2.2:
174
+ when: user selects a variant with no inputSchema
175
+ then: op:createGame is invoked immediately with empty input
176
+
177
+ req F-2.3:
178
+ when: user selects a variant that has an inputSchema
179
+ then: a form rendered from the schema is shown; op:createGame is invoked on submit
180
+
181
+ req F-2.4:
182
+ when: op:createGame completes
183
+ then: a Game object appears in the space graph; variant state is hidden
184
+ ```
185
+
186
+ ```mdl
187
+ feat F-3: Game Article / Card Rendering
188
+
189
+ req F-3.1:
190
+ when: Composer navigates to a Game object
191
+ then: GameArticle resolves `game.variant` and delegates to the matching `variant.article`
192
+
193
+ req F-3.2:
194
+ when: a Game object is shown in a Card layout
195
+ then: GameCard resolves `game.variant` and delegates to `variant.card`
196
+
197
+ req F-3.3:
198
+ when: no matching variant is found for the loaded state object
199
+ then: GameArticle renders an "Unsupported game variant" fallback message
200
+
201
+ req F-3.4:
202
+ when: the variant ref is not yet loaded
203
+ then: neither GameArticle nor GameCard renders (returns null)
204
+ ```
205
+
206
+ ## Acceptance
207
+
208
+ ```mdl
209
+ test T-1: Variant picker shows all contributions
210
+ given: two variant plugins have contributed GameVariants
211
+ when: the user opens the create-game panel
212
+ then:
213
+ - CreateGamePanel lists both variants
214
+ - variants are sorted alphabetically
215
+ ```
216
+
217
+ ```mdl
218
+ test T-2: Create game without form
219
+ given: a variant with no inputSchema is selected
220
+ when: user clicks the variant item
221
+ then:
222
+ - onCreateObject is called immediately with { variantId }
223
+ - no form is shown
224
+ ```
225
+
226
+ ```mdl
227
+ test T-3: Create game with form
228
+ given: a variant with an inputSchema is selected
229
+ when: user fills and submits the form
230
+ then:
231
+ - onCreateObject is called with { variantId, input: <form values> }
232
+ ```
233
+
234
+ ```mdl
235
+ test T-4: Atomic game creation
236
+ given: a valid variantId and target space
237
+ when: op:createGame is invoked
238
+ then:
239
+ - variant state object added to space (hidden)
240
+ - Game object added with game.variant pointing to the state object
241
+ - both writes are visible to other peers via ECHO replication
242
+ ```
243
+
244
+ ```mdl
245
+ test T-5: Rollback on second write failure
246
+ given: variant state is successfully written
247
+ when: the second write (Game object) fails
248
+ then:
249
+ - RemoveObjects is invoked for the variant state
250
+ - no orphaned state object remains in the space
251
+ ```
252
+
253
+ ```mdl
254
+ test T-6: Unknown variant rejected
255
+ given: op:createGame is invoked with an unregistered variantId
256
+ when: the handler runs
257
+ then: UnknownVariant error is returned; no ECHO writes occur
258
+ ```
259
+
260
+ ```mdl
261
+ test T-7: Unsupported variant fallback
262
+ given: a Game whose variant typename has no registered GameVariant
263
+ when: GameArticle renders
264
+ then: "Unsupported game variant" message is shown instead of crashing
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Appendix: Extension Definitions
270
+
271
+ Extension block types used in this document are defined below using
272
+ the core `ext` primitive — the only construct the base language provides.
273
+
274
+ ```mdl
275
+ ext type
276
+ uri: org.dxos.mdl.type@1.0
277
+ desc: A named data structure with typed fields and optional literals.
278
+ fields:
279
+ desc?: Prose
280
+ fields?: FieldMap # name[?]: TypeExpr (# inline comment)
281
+ literals?: UnionList # a | b | c
282
+ extends?: TypeRef[]
283
+ ```
284
+
285
+ ```mdl
286
+ ext feat
287
+ uri: org.dxos.mdl.feat@1.0
288
+ desc: A named feature grouping one or more requirements.
289
+ fields:
290
+ desc?: Prose
291
+ req: RequirementList
292
+ nesting: self # feat blocks may contain feat blocks
293
+ ```
294
+
295
+ ```mdl
296
+ ext test
297
+ uri: org.dxos.mdl.test@1.0
298
+ desc: An acceptance scenario expressed as given / when / then steps.
299
+ fields:
300
+ given?: Step | Step[]
301
+ when?: Step | Step[]
302
+ then: Step | Step[]
303
+ tags?: TagList
304
+ ```
305
+
306
+ ```mdl
307
+ ext component
308
+ uri: org.dxos.mdl.component@1.0
309
+ desc: A UI component with props, internal state, slots, actions, and events.
310
+ fields:
311
+ desc?: Prose
312
+ props?: FieldMap # external inputs (immutable inside component)
313
+ state?: FieldMap # internal reactive state
314
+ slots?: FieldMap # named ReactNode injection points
315
+ actions?: ActionMap # methods the component exposes or handles
316
+ emits?: EventMap # events the component raises to its parent
317
+ layout?: CodeBlock # ASCII sketch of visual structure (non-normative)
318
+ ```
319
+
320
+ ```mdl
321
+ ext op
322
+ uri: org.dxos.mdl.op@1.0
323
+ desc: |
324
+ A named operation with typed inputs, outputs, and declared errors.
325
+ Pure ops have no effects or requires. Effectful ops declare both.
326
+ fields:
327
+ desc?: Prose
328
+ input?: FieldMap # named input parameters
329
+ output?: TypeExpr # return type
330
+ errors?: ErrorMap # name: Prose (when this error occurs)
331
+ effects?: EffectList # echo:read | echo:write | http | fs | ...
332
+ requires?: ServiceList # injected service dependencies
333
+ note?: Prose # implementation guidance (non-normative)
334
+ ```
@@ -0,0 +1,40 @@
1
+ import "./chunk-J5LGTIGS.mjs";
2
+
3
+ // src/containers/GameArticle/GameArticle.tsx
4
+ import React from "react";
5
+ import { useCapabilities } from "@dxos/app-framework/ui";
6
+ import { Obj } from "@dxos/echo";
7
+ import { useObject } from "@dxos/echo-react";
8
+ import { useTranslation } from "@dxos/react-ui";
9
+ import { meta } from "#meta";
10
+ import { GameCapabilities } from "#types";
11
+ var GameArticle = ({ role, attendableId, subject: game }) => {
12
+ const { t } = useTranslation(meta.profile.key);
13
+ const variants = useCapabilities(GameCapabilities.VariantProvider);
14
+ const ref = game.variant;
15
+ useObject(ref);
16
+ const variant = ref?.target;
17
+ if (!variant) {
18
+ return null;
19
+ }
20
+ const variantTypename = Obj.getTypename(variant);
21
+ const match = variants.find((v) => v.id === variantTypename);
22
+ if (!match?.article) {
23
+ return /* @__PURE__ */ React.createElement("div", {
24
+ className: "p-4 text-sm"
25
+ }, t("unsupported-variant.label", {
26
+ defaultValue: "Unsupported game variant"
27
+ }));
28
+ }
29
+ const Component = match.article;
30
+ return /* @__PURE__ */ React.createElement(Component, {
31
+ game,
32
+ variant,
33
+ role,
34
+ attendableId
35
+ });
36
+ };
37
+ export {
38
+ GameArticle as default
39
+ };
40
+ //# sourceMappingURL=GameArticle-AUJ4YDKW.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/containers/GameArticle/GameArticle.tsx"],
4
+ "sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport { useCapabilities } from '@dxos/app-framework/ui';\nimport { type AppSurface } from '@dxos/app-toolkit/ui';\nimport { Obj, type Ref } from '@dxos/echo';\nimport { useObject } from '@dxos/echo-react';\nimport { useTranslation } from '@dxos/react-ui';\n\nimport { meta } from '#meta';\nimport { type Game, GameCapabilities } from '#types';\n\nexport type GameArticleProps = AppSurface.ObjectArticleProps<Game>;\n\nexport const GameArticle = ({ role, attendableId, subject: game }: GameArticleProps) => {\n const { t } = useTranslation(meta.profile.key);\n const variants = useCapabilities(GameCapabilities.VariantProvider);\n const ref = game.variant as Ref.Ref<Obj.Unknown>;\n // Subscribe via useObject so the article re-renders when the variant ref loads or changes.\n // The snapshot is discarded; we pass the live ref target to variant components so they can\n // subscribe to their own properties via useObject(state, 'prop').\n useObject(ref);\n const variant = ref?.target as Obj.Unknown | undefined;\n\n if (!variant) {\n return null;\n }\n\n const variantTypename = Obj.getTypename(variant);\n const match = variants.find((v) => v.id === variantTypename);\n if (!match?.article) {\n return (\n <div className='p-4 text-sm'>{t('unsupported-variant.label', { defaultValue: 'Unsupported game variant' })}</div>\n );\n }\n\n const Component = match.article;\n return <Component game={game} variant={variant} role={role} attendableId={attendableId} />;\n};\n"],
5
+ "mappings": ";;;AAIA,OAAOA,WAAW;AAElB,SAASC,uBAAuB;AAEhC,SAASC,WAAqB;AAC9B,SAASC,iBAAiB;AAC1B,SAASC,sBAAsB;AAE/B,SAASC,YAAY;AACrB,SAAoBC,wBAAwB;AAIrC,IAAMC,cAAc,CAAC,EAAEC,MAAMC,cAAcC,SAASC,KAAI,MAAoB;AACjF,QAAM,EAAEC,EAAC,IAAKR,eAAeC,KAAKQ,QAAQC,GAAG;AAC7C,QAAMC,WAAWd,gBAAgBK,iBAAiBU,eAAe;AACjE,QAAMC,MAAMN,KAAKO;AAIjBf,YAAUc,GAAAA;AACV,QAAMC,UAAUD,KAAKE;AAErB,MAAI,CAACD,SAAS;AACZ,WAAO;EACT;AAEA,QAAME,kBAAkBlB,IAAImB,YAAYH,OAAAA;AACxC,QAAMI,QAAQP,SAASQ,KAAK,CAACC,MAAMA,EAAEC,OAAOL,eAAAA;AAC5C,MAAI,CAACE,OAAOI,SAAS;AACnB,WACE,sBAAA,cAACC,OAAAA;MAAIC,WAAU;OAAehB,EAAE,6BAA6B;MAAEiB,cAAc;IAA2B,CAAA,CAAA;EAE5G;AAEA,QAAMC,YAAYR,MAAMI;AACxB,SAAO,sBAAA,cAACI,WAAAA;IAAUnB;IAAYO;IAAkBV;IAAYC;;AAC9D;",
6
+ "names": ["React", "useCapabilities", "Obj", "useObject", "useTranslation", "meta", "GameCapabilities", "GameArticle", "role", "attendableId", "subject", "game", "t", "profile", "key", "variants", "VariantProvider", "ref", "variant", "target", "variantTypename", "getTypename", "match", "find", "v", "id", "article", "div", "className", "defaultValue", "Component"]
7
+ }
@@ -0,0 +1,32 @@
1
+ import "./chunk-J5LGTIGS.mjs";
2
+
3
+ // src/containers/GameCard/GameCard.tsx
4
+ import React from "react";
5
+ import { useCapabilities } from "@dxos/app-framework/ui";
6
+ import { Obj } from "@dxos/echo";
7
+ import { useObject } from "@dxos/echo-react";
8
+ import { GameCapabilities } from "#types";
9
+ var GameCard = ({ role, subject: game }) => {
10
+ const variants = useCapabilities(GameCapabilities.VariantProvider);
11
+ const ref = game.variant;
12
+ useObject(ref);
13
+ const variant = ref?.target;
14
+ if (!variant) {
15
+ return null;
16
+ }
17
+ const variantTypename = Obj.getTypename(variant);
18
+ const match = variants.find((v) => v.id === variantTypename);
19
+ if (!match?.card) {
20
+ return null;
21
+ }
22
+ const Component = match.card;
23
+ return /* @__PURE__ */ React.createElement(Component, {
24
+ game,
25
+ variant,
26
+ role
27
+ });
28
+ };
29
+ export {
30
+ GameCard as default
31
+ };
32
+ //# sourceMappingURL=GameCard-QBKHIL4Q.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/containers/GameCard/GameCard.tsx"],
4
+ "sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport { useCapabilities } from '@dxos/app-framework/ui';\nimport { type AppSurface } from '@dxos/app-toolkit/ui';\nimport { Obj, type Ref } from '@dxos/echo';\nimport { useObject } from '@dxos/echo-react';\n\nimport { type Game, GameCapabilities } from '#types';\n\nexport type GameCardProps = AppSurface.ObjectCardProps<Game>;\n\nexport const GameCard = ({ role, subject: game }: GameCardProps) => {\n const variants = useCapabilities(GameCapabilities.VariantProvider);\n const ref = game.variant as Ref.Ref<Obj.Unknown>;\n // Subscribe for re-renders; pass the live ref target down to variant components.\n useObject(ref);\n const variant = ref?.target as Obj.Unknown | undefined;\n\n if (!variant) {\n return null;\n }\n\n const variantTypename = Obj.getTypename(variant);\n const match = variants.find((v) => v.id === variantTypename);\n if (!match?.card) {\n return null;\n }\n\n const Component = match.card;\n return <Component game={game} variant={variant} role={role} />;\n};\n"],
5
+ "mappings": ";;;AAIA,OAAOA,WAAW;AAElB,SAASC,uBAAuB;AAEhC,SAASC,WAAqB;AAC9B,SAASC,iBAAiB;AAE1B,SAAoBC,wBAAwB;AAIrC,IAAMC,WAAW,CAAC,EAAEC,MAAMC,SAASC,KAAI,MAAiB;AAC7D,QAAMC,WAAWR,gBAAgBG,iBAAiBM,eAAe;AACjE,QAAMC,MAAMH,KAAKI;AAEjBT,YAAUQ,GAAAA;AACV,QAAMC,UAAUD,KAAKE;AAErB,MAAI,CAACD,SAAS;AACZ,WAAO;EACT;AAEA,QAAME,kBAAkBZ,IAAIa,YAAYH,OAAAA;AACxC,QAAMI,QAAQP,SAASQ,KAAK,CAACC,MAAMA,EAAEC,OAAOL,eAAAA;AAC5C,MAAI,CAACE,OAAOI,MAAM;AAChB,WAAO;EACT;AAEA,QAAMC,YAAYL,MAAMI;AACxB,SAAO,sBAAA,cAACC,WAAAA;IAAUb;IAAYI;IAAkBN;;AAClD;",
6
+ "names": ["React", "useCapabilities", "Obj", "useObject", "GameCapabilities", "GameCard", "role", "subject", "game", "variants", "VariantProvider", "ref", "variant", "target", "variantTypename", "getTypename", "match", "find", "v", "id", "card", "Component"]
7
+ }
@@ -0,0 +1,38 @@
1
+ import "./chunk-J5LGTIGS.mjs";
2
+
3
+ // src/GamePlugin.tsx
4
+ import { Plugin } from "@dxos/app-framework";
5
+ import { AppPlugin } from "@dxos/app-toolkit";
6
+ import { CreateObject, ReactSurface } from "#capabilities";
7
+ import { meta } from "#meta";
8
+ import { translations } from "#translations";
9
+ import { Game } from "#types";
10
+
11
+ // raw-loader:/Users/jdw/Code/dxos/packages/plugins/plugin-game/PLUGIN.mdl
12
+ var PLUGIN_default = '---\nid: org.dxos.plugin.game\nname: GamePlugin\nversion: 0.1.0\n---\n\nA generic game host plugin for `DXOS` Composer that provides shared infrastructure for\nmulti-player, local-first turn-based games. Variant plugins (e.g. plugin-chess,\nplugin-tictactoe) contribute a `GameVariant` capability and game-specific surfaces;\nplugin-game supplies the `Game` ECHO type, create flow, and dispatch scaffolding.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype Player\n fields:\n role: string # variant-defined (e.g. "white", "x"); required\n identity?: string # DID of a human player; absent for AI / empty seats\n name?: string # display label for AI or guest players\n```\n\n```mdl\ntype Game\n fields:\n name?: string\n players?: Player[] # populated during or after creation\n variant: Ref<Unknown> # reference to variant-specific state object (e.g. Chess.State)\n typename: org.dxos.type.game\n version: 0.1.0\n```\n\n```mdl\ntype GameVariant\n desc: |\n Contribution interface from a variant plugin. Registered via\n `Capability.contributes(GameCapabilities.VariantProvider, variant)`.\n fields:\n id: string # stable variant id; matches the variant state typename\n label: string # human-readable name shown in the picker\n icon?: string # Phosphor icon name\n variantSchema: Schema # Effect/Schema for the ECHO state object\n inputSchema?: Schema # optional form schema shown after variant is selected\n roles: string[] # player roles (e.g. ["white", "black"])\n createVariant: Effect # factory that builds the variant ECHO state object\n card?: ComponentType # optional Card surface component\n article?: ComponentType # optional Article/Section surface component\n```\n\n## Components\n\n```mdl\ncomponent CreateGamePanel\n desc: |\n Two-stage creation panel. Stage 1 shows a searchable variant picker.\n Stage 2 (optional) shows a form rendered from the variant\'s inputSchema.\n props:\n target: Space | Obj\n onCreateObject: (args: { variantId: string; input?: Record }) => void\n variants?: GameVariant[] # override; defaults to GameCapabilities.VariantProvider\n state:\n selectedId?: string # id of the variant chosen in stage 1\n actions:\n selectVariant(id: string)\n submitForm(input: Record)\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 Stage 1: Variant Picker \u2502\n \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n \u2502 \u2502 Search input \u2502 \u2502\n \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\n \u2502 \u2502 variant row \u2502 \u2502\n \u2502 \u2502 variant row \u2502 \u2502\n \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502 Stage 2: Input Form \u2502\n \u2502 (rendered from schema) \u2502\n \u2502 [Submit] \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n```mdl\ncomponent GameArticle\n desc: |\n Article / Section surface for Game objects. Resolves the variant via\n `game.variant` ref, finds the matching `GameVariant` by typename, and\n delegates rendering to `variant.article`.\n props:\n game: Game\n role?: string\n attendableId?: string\n state:\n variant: Unknown # live variant state object from the Ref\n match?: GameVariant # resolved GameVariant from capabilities\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 [variant.article] \u2502\n \u2502 (fallback: unsupported \u2502\n \u2502 variant message) \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n```mdl\ncomponent GameCard\n desc: |\n Card surface for Game objects. Follows the same variant-resolution\n logic as GameArticle and delegates to `variant.card`.\n props:\n game: Game\n role?: string\n state:\n variant: Unknown\n match?: GameVariant\n```\n\n## Operations\n\n```mdl\nop createGame\n desc: |\n Creates a variant state object via `variant.createVariant`, adds it\n to the space as a hidden object, then wraps it in a top-level Game.\n If the second write fails the variant state object is rolled back.\n input:\n variantId: string\n input?: Record # variant-specific form values\n target: Space | Obj\n targetNodeId?: string\n output: Game\n errors:\n UnknownVariant: no registered GameVariant matches variantId\n effects: [echo:write]\n requires: [GameCapabilities.VariantProvider, SpaceOperation.AddObject, SpaceOperation.RemoveObjects]\n note: |\n Invoked by CreateObjectEntry.createObject.\n Variant state is added with `hidden: true` so it does not appear\n as a top-level item in the user\'s space graph.\n```\n\n## Features\n\n```mdl\nfeat F-1: Variant Registration\n\n req F-1.1:\n when: a variant plugin loads\n then: it contributes a GameVariant via `Capability.contributes(GameCapabilities.VariantProvider, \u2026)`\n\n req F-1.2:\n when: GameCapabilities.VariantProvider contributions are queried\n then: all registered variants are returned as an ordered list\n```\n\n```mdl\nfeat F-2: Game Creation\n\n req F-2.1:\n when: user opens the create-object flow for type `org.dxos.type.game`\n then: CreateGamePanel is shown with a searchable list of available variants\n\n req F-2.2:\n when: user selects a variant with no inputSchema\n then: op:createGame is invoked immediately with empty input\n\n req F-2.3:\n when: user selects a variant that has an inputSchema\n then: a form rendered from the schema is shown; op:createGame is invoked on submit\n\n req F-2.4:\n when: op:createGame completes\n then: a Game object appears in the space graph; variant state is hidden\n```\n\n```mdl\nfeat F-3: Game Article / Card Rendering\n\n req F-3.1:\n when: Composer navigates to a Game object\n then: GameArticle resolves `game.variant` and delegates to the matching `variant.article`\n\n req F-3.2:\n when: a Game object is shown in a Card layout\n then: GameCard resolves `game.variant` and delegates to `variant.card`\n\n req F-3.3:\n when: no matching variant is found for the loaded state object\n then: GameArticle renders an "Unsupported game variant" fallback message\n\n req F-3.4:\n when: the variant ref is not yet loaded\n then: neither GameArticle nor GameCard renders (returns null)\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Variant picker shows all contributions\n given: two variant plugins have contributed GameVariants\n when: the user opens the create-game panel\n then:\n - CreateGamePanel lists both variants\n - variants are sorted alphabetically\n```\n\n```mdl\ntest T-2: Create game without form\n given: a variant with no inputSchema is selected\n when: user clicks the variant item\n then:\n - onCreateObject is called immediately with { variantId }\n - no form is shown\n```\n\n```mdl\ntest T-3: Create game with form\n given: a variant with an inputSchema is selected\n when: user fills and submits the form\n then:\n - onCreateObject is called with { variantId, input: <form values> }\n```\n\n```mdl\ntest T-4: Atomic game creation\n given: a valid variantId and target space\n when: op:createGame is invoked\n then:\n - variant state object added to space (hidden)\n - Game object added with game.variant pointing to the state object\n - both writes are visible to other peers via ECHO replication\n```\n\n```mdl\ntest T-5: Rollback on second write failure\n given: variant state is successfully written\n when: the second write (Game object) fails\n then:\n - RemoveObjects is invoked for the variant state\n - no orphaned state object remains in the space\n```\n\n```mdl\ntest T-6: Unknown variant rejected\n given: op:createGame is invoked with an unregistered variantId\n when: the handler runs\n then: UnknownVariant error is returned; no ECHO writes occur\n```\n\n```mdl\ntest T-7: Unsupported variant fallback\n given: a Game whose variant typename has no registered GameVariant\n when: GameArticle renders\n then: "Unsupported game variant" message is shown instead of crashing\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive \u2014 the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n';
13
+
14
+ // src/GamePlugin.tsx
15
+ var GamePlugin = Plugin.define(meta).pipe(AppPlugin.addCreateObjectModule({
16
+ activate: CreateObject
17
+ }), AppPlugin.addSchemaModule({
18
+ schema: [
19
+ Game
20
+ ]
21
+ }), AppPlugin.addSurfaceModule({
22
+ activate: ReactSurface
23
+ }), AppPlugin.addTranslationsModule({
24
+ translations
25
+ }), AppPlugin.addPluginAssetModule({
26
+ asset: {
27
+ pluginId: meta.profile.key,
28
+ path: "PLUGIN.mdl",
29
+ content: PLUGIN_default,
30
+ mimeType: "application/x-mdl"
31
+ }
32
+ }), Plugin.make);
33
+ var GamePlugin_default = GamePlugin;
34
+ export {
35
+ GamePlugin,
36
+ GamePlugin_default as default
37
+ };
38
+ //# sourceMappingURL=GamePlugin.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/GamePlugin.tsx", "raw-loader:/Users/jdw/Code/dxos/packages/plugins/plugin-game/PLUGIN.mdl"],
4
+ "sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\nimport { Plugin } from '@dxos/app-framework';\nimport { AppPlugin } from '@dxos/app-toolkit';\n\nimport { CreateObject, ReactSurface } from '#capabilities';\nimport { meta } from '#meta';\nimport { translations } from '#translations';\nimport { Game } from '#types';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../PLUGIN.mdl?raw';\n\nexport const GamePlugin = Plugin.define(meta).pipe(\n AppPlugin.addCreateObjectModule({ activate: CreateObject }),\n AppPlugin.addSchemaModule({ schema: [Game] }),\n AppPlugin.addSurfaceModule({ activate: ReactSurface }),\n AppPlugin.addTranslationsModule({ translations }),\n AppPlugin.addPluginAssetModule({\n asset: { pluginId: meta.profile.key, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },\n }),\n Plugin.make,\n);\n\nexport default GamePlugin;\n", "---\nid: org.dxos.plugin.game\nname: GamePlugin\nversion: 0.1.0\n---\n\nA generic game host plugin for `DXOS` Composer that provides shared infrastructure for\nmulti-player, local-first turn-based games. Variant plugins (e.g. plugin-chess,\nplugin-tictactoe) contribute a `GameVariant` capability and game-specific surfaces;\nplugin-game supplies the `Game` ECHO type, create flow, and dispatch scaffolding.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype Player\n fields:\n role: string # variant-defined (e.g. \"white\", \"x\"); required\n identity?: string # DID of a human player; absent for AI / empty seats\n name?: string # display label for AI or guest players\n```\n\n```mdl\ntype Game\n fields:\n name?: string\n players?: Player[] # populated during or after creation\n variant: Ref<Unknown> # reference to variant-specific state object (e.g. Chess.State)\n typename: org.dxos.type.game\n version: 0.1.0\n```\n\n```mdl\ntype GameVariant\n desc: |\n Contribution interface from a variant plugin. Registered via\n `Capability.contributes(GameCapabilities.VariantProvider, variant)`.\n fields:\n id: string # stable variant id; matches the variant state typename\n label: string # human-readable name shown in the picker\n icon?: string # Phosphor icon name\n variantSchema: Schema # Effect/Schema for the ECHO state object\n inputSchema?: Schema # optional form schema shown after variant is selected\n roles: string[] # player roles (e.g. [\"white\", \"black\"])\n createVariant: Effect # factory that builds the variant ECHO state object\n card?: ComponentType # optional Card surface component\n article?: ComponentType # optional Article/Section surface component\n```\n\n## Components\n\n```mdl\ncomponent CreateGamePanel\n desc: |\n Two-stage creation panel. Stage 1 shows a searchable variant picker.\n Stage 2 (optional) shows a form rendered from the variant's inputSchema.\n props:\n target: Space | Obj\n onCreateObject: (args: { variantId: string; input?: Record }) => void\n variants?: GameVariant[] # override; defaults to GameCapabilities.VariantProvider\n state:\n selectedId?: string # id of the variant chosen in stage 1\n actions:\n selectVariant(id: string)\n submitForm(input: Record)\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 Stage 1: Variant Picker \u2502\n \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n \u2502 \u2502 Search input \u2502 \u2502\n \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\n \u2502 \u2502 variant row \u2502 \u2502\n \u2502 \u2502 variant row \u2502 \u2502\n \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502 Stage 2: Input Form \u2502\n \u2502 (rendered from schema) \u2502\n \u2502 [Submit] \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n```mdl\ncomponent GameArticle\n desc: |\n Article / Section surface for Game objects. Resolves the variant via\n `game.variant` ref, finds the matching `GameVariant` by typename, and\n delegates rendering to `variant.article`.\n props:\n game: Game\n role?: string\n attendableId?: string\n state:\n variant: Unknown # live variant state object from the Ref\n match?: GameVariant # resolved GameVariant from capabilities\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 [variant.article] \u2502\n \u2502 (fallback: unsupported \u2502\n \u2502 variant message) \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n```mdl\ncomponent GameCard\n desc: |\n Card surface for Game objects. Follows the same variant-resolution\n logic as GameArticle and delegates to `variant.card`.\n props:\n game: Game\n role?: string\n state:\n variant: Unknown\n match?: GameVariant\n```\n\n## Operations\n\n```mdl\nop createGame\n desc: |\n Creates a variant state object via `variant.createVariant`, adds it\n to the space as a hidden object, then wraps it in a top-level Game.\n If the second write fails the variant state object is rolled back.\n input:\n variantId: string\n input?: Record # variant-specific form values\n target: Space | Obj\n targetNodeId?: string\n output: Game\n errors:\n UnknownVariant: no registered GameVariant matches variantId\n effects: [echo:write]\n requires: [GameCapabilities.VariantProvider, SpaceOperation.AddObject, SpaceOperation.RemoveObjects]\n note: |\n Invoked by CreateObjectEntry.createObject.\n Variant state is added with `hidden: true` so it does not appear\n as a top-level item in the user's space graph.\n```\n\n## Features\n\n```mdl\nfeat F-1: Variant Registration\n\n req F-1.1:\n when: a variant plugin loads\n then: it contributes a GameVariant via `Capability.contributes(GameCapabilities.VariantProvider, \u2026)`\n\n req F-1.2:\n when: GameCapabilities.VariantProvider contributions are queried\n then: all registered variants are returned as an ordered list\n```\n\n```mdl\nfeat F-2: Game Creation\n\n req F-2.1:\n when: user opens the create-object flow for type `org.dxos.type.game`\n then: CreateGamePanel is shown with a searchable list of available variants\n\n req F-2.2:\n when: user selects a variant with no inputSchema\n then: op:createGame is invoked immediately with empty input\n\n req F-2.3:\n when: user selects a variant that has an inputSchema\n then: a form rendered from the schema is shown; op:createGame is invoked on submit\n\n req F-2.4:\n when: op:createGame completes\n then: a Game object appears in the space graph; variant state is hidden\n```\n\n```mdl\nfeat F-3: Game Article / Card Rendering\n\n req F-3.1:\n when: Composer navigates to a Game object\n then: GameArticle resolves `game.variant` and delegates to the matching `variant.article`\n\n req F-3.2:\n when: a Game object is shown in a Card layout\n then: GameCard resolves `game.variant` and delegates to `variant.card`\n\n req F-3.3:\n when: no matching variant is found for the loaded state object\n then: GameArticle renders an \"Unsupported game variant\" fallback message\n\n req F-3.4:\n when: the variant ref is not yet loaded\n then: neither GameArticle nor GameCard renders (returns null)\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Variant picker shows all contributions\n given: two variant plugins have contributed GameVariants\n when: the user opens the create-game panel\n then:\n - CreateGamePanel lists both variants\n - variants are sorted alphabetically\n```\n\n```mdl\ntest T-2: Create game without form\n given: a variant with no inputSchema is selected\n when: user clicks the variant item\n then:\n - onCreateObject is called immediately with { variantId }\n - no form is shown\n```\n\n```mdl\ntest T-3: Create game with form\n given: a variant with an inputSchema is selected\n when: user fills and submits the form\n then:\n - onCreateObject is called with { variantId, input: <form values> }\n```\n\n```mdl\ntest T-4: Atomic game creation\n given: a valid variantId and target space\n when: op:createGame is invoked\n then:\n - variant state object added to space (hidden)\n - Game object added with game.variant pointing to the state object\n - both writes are visible to other peers via ECHO replication\n```\n\n```mdl\ntest T-5: Rollback on second write failure\n given: variant state is successfully written\n when: the second write (Game object) fails\n then:\n - RemoveObjects is invoked for the variant state\n - no orphaned state object remains in the space\n```\n\n```mdl\ntest T-6: Unknown variant rejected\n given: op:createGame is invoked with an unregistered variantId\n when: the handler runs\n then: UnknownVariant error is returned; no ECHO writes occur\n```\n\n```mdl\ntest T-7: Unsupported variant fallback\n given: a Game whose variant typename has no registered GameVariant\n when: GameArticle renders\n then: \"Unsupported game variant\" message is shown instead of crashing\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive \u2014 the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n"],
5
+ "mappings": ";;;AAIA,SAASA,cAAc;AACvB,SAASC,iBAAiB;AAE1B,SAASC,cAAcC,oBAAoB;AAC3C,SAASC,YAAY;AACrB,SAASC,oBAAoB;AAC7B,SAASC,YAAY;;;ACVrB;;;ADeO,IAAMC,aAAaC,OAAOC,OAAOC,IAAAA,EAAMC,KAC5CC,UAAUC,sBAAsB;EAAEC,UAAUC;AAAa,CAAA,GACzDH,UAAUI,gBAAgB;EAAEC,QAAQ;IAACC;;AAAM,CAAA,GAC3CN,UAAUO,iBAAiB;EAAEL,UAAUM;AAAa,CAAA,GACpDR,UAAUS,sBAAsB;EAAEC;AAAa,CAAA,GAC/CV,UAAUW,qBAAqB;EAC7BC,OAAO;IAAEC,UAAUf,KAAKgB,QAAQC;IAAKC,MAAM;IAAcC,SAASC;IAAYC,UAAU;EAAoB;AAC9G,CAAA,GACAvB,OAAOwB,IAAI;AAGb,IAAA,qBAAezB;",
6
+ "names": ["Plugin", "AppPlugin", "CreateObject", "ReactSurface", "meta", "translations", "Game", "GamePlugin", "Plugin", "define", "meta", "pipe", "AppPlugin", "addCreateObjectModule", "activate", "CreateObject", "addSchemaModule", "schema", "Game", "addSurfaceModule", "ReactSurface", "addTranslationsModule", "translations", "addPluginAssetModule", "asset", "pluginId", "profile", "key", "path", "content", "pluginSpec", "mimeType", "make"]
7
+ }
@@ -0,0 +1,21 @@
1
+ import "./chunk-J5LGTIGS.mjs";
2
+
3
+ // src/GamePlugin.node.tsx
4
+ import { Plugin } from "@dxos/app-framework";
5
+ import { AppPlugin } from "@dxos/app-toolkit";
6
+ import { meta } from "#meta";
7
+ import { translations } from "#translations";
8
+ import { Game } from "#types";
9
+ var GamePlugin = Plugin.define(meta).pipe(AppPlugin.addSchemaModule({
10
+ schema: [
11
+ Game
12
+ ]
13
+ }), AppPlugin.addTranslationsModule({
14
+ translations
15
+ }), Plugin.make);
16
+ var GamePlugin_node_default = GamePlugin;
17
+ export {
18
+ GamePlugin,
19
+ GamePlugin_node_default as default
20
+ };
21
+ //# sourceMappingURL=GamePlugin.node.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/GamePlugin.node.tsx"],
4
+ "sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\nimport { Plugin } from '@dxos/app-framework';\nimport { AppPlugin } from '@dxos/app-toolkit';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\nimport { Game } from '#types';\n\n/**\n * Headless variant of GamePlugin (no React surfaces / CreateObject panel).\n * Used in node contexts (CLI, agents, tests) where rendering is unavailable —\n * the full variant lazy-loads `CreateGamePanel` from `react-ui-search` which\n * downstream bundlers don't resolve cleanly under nested pnpm symlinks.\n */\nexport const GamePlugin = Plugin.define(meta).pipe(\n AppPlugin.addSchemaModule({ schema: [Game] }),\n AppPlugin.addTranslationsModule({ translations }),\n Plugin.make,\n);\n\nexport default GamePlugin;\n"],
5
+ "mappings": ";;;AAIA,SAASA,cAAc;AACvB,SAASC,iBAAiB;AAE1B,SAASC,YAAY;AACrB,SAASC,oBAAoB;AAC7B,SAASC,YAAY;AAQd,IAAMC,aAAaL,OAAOM,OAAOJ,IAAAA,EAAMK,KAC5CN,UAAUO,gBAAgB;EAAEC,QAAQ;IAACL;;AAAM,CAAA,GAC3CH,UAAUS,sBAAsB;EAAEP;AAAa,CAAA,GAC/CH,OAAOW,IAAI;AAGb,IAAA,0BAAeN;",
6
+ "names": ["Plugin", "AppPlugin", "meta", "translations", "Game", "GamePlugin", "define", "pipe", "addSchemaModule", "schema", "addTranslationsModule", "make"]
7
+ }