@xmachines/play-dom 1.0.0-beta.45 → 1.0.0-beta.47
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 +140 -232
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,106 +1,24 @@
|
|
|
1
|
+
<!-- generated-by: gsd-doc-writer -->
|
|
2
|
+
|
|
1
3
|
# @xmachines/play-dom
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
Vanilla DOM renderer for XMachines Play architecture with signal-driven rendering.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
Part of the [xmachines-js monorepo](../../README.md).
|
|
6
8
|
|
|
7
9
|
## Installation
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
|
-
npm install @xmachines/play-dom
|
|
12
|
+
npm install @xmachines/play-dom
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
| Export | Description |
|
|
16
|
-
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
-
| `createPlayUI(registryResult, options?)` | Batteries-included factory: returns `mount(actor, container) → disconnect` — parallel to `PlayUIProvider` in framework packages |
|
|
18
|
-
| `createRenderer(catalog, componentMap)` | One-call factory: returns `mount(actor, container, options?) → disconnect` |
|
|
19
|
-
| `connectRenderer(options)` | Functional API: connect actor → DOM with full options |
|
|
20
|
-
| `defineRegistry(catalog, { components, actions })` | Build a catalog-typed `DomRegistry` with typed action handlers |
|
|
21
|
-
| `UIProviderOptions` | Standard options interface: `validationFunctions`, `navigate`, `functions`, `onRenderError`, `fallback` |
|
|
22
|
-
| `PlayRenderer` | Class-based renderer — `connect()` / `disconnect()` lifecycle |
|
|
23
|
-
| `schema` | DOM schema for `defineCatalog` (mirrors `@json-render/react/schema`) |
|
|
24
|
-
| `ComponentFn<C, K>` | Catalog-typed DOM component function type |
|
|
25
|
-
| `ComponentContext<C, K>` | Context passed to each `ComponentFn` — `props`, `on`, `emit`, `children`, `bindings`, `ctx` |
|
|
26
|
-
| `ActionFn<C, K>` | `(params, setState, state) => Promise<void>` — catalog-typed action handler |
|
|
27
|
-
| `SetState` | `(updater: prev => next) => void` — write to the local state store |
|
|
28
|
-
| `BaseComponentProps<P>` | Base type for catalog component prop definitions |
|
|
29
|
-
| `CatalogHasActions<C>` | Conditional type: `true` when catalog declares actions |
|
|
30
|
-
| `renderSpec(spec, store, registry, send, handlers)` | Pure Spec → DOM renderer (advanced use) |
|
|
31
|
-
|
|
32
|
-
## Quick Start — `createPlayUI`
|
|
33
|
-
|
|
34
|
-
The standard entry point — mirrors `PlayUIProvider` in framework packages. Pass a `registryResult` (from `defineRegistry`) plus optional `UIProviderOptions`, and get back a `mount` function:
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import { defineRegistry, createPlayUI, schema } from "@xmachines/play-dom";
|
|
38
|
-
import { defineCatalog } from "@json-render/core";
|
|
39
|
-
import { z } from "zod";
|
|
40
|
-
import type { ComponentFn } from "@xmachines/play-dom";
|
|
15
|
+
**Peer dependencies:**
|
|
41
16
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
components: {
|
|
45
|
-
Home: { props: z.object({ title: z.string() }) },
|
|
46
|
-
Login: { props: z.object({ title: z.string(), username: z.string().optional() }) },
|
|
47
|
-
},
|
|
48
|
-
actions: {
|
|
49
|
-
login: { params: z.object({ username: z.string() }) },
|
|
50
|
-
logout: {},
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// 2. Build registry with typed action handlers
|
|
55
|
-
const registryResult = defineRegistry(catalog, {
|
|
56
|
-
components: {
|
|
57
|
-
Home: ({ props }) => {
|
|
58
|
-
const el = document.createElement("section");
|
|
59
|
-
el.textContent = props.title;
|
|
60
|
-
return el;
|
|
61
|
-
},
|
|
62
|
-
Login: ({ props, on }) => {
|
|
63
|
-
const section = document.createElement("section");
|
|
64
|
-
const button = document.createElement("button");
|
|
65
|
-
button.textContent = "Log In";
|
|
66
|
-
const submit = on("submit");
|
|
67
|
-
button.addEventListener("click", () => submit.emit());
|
|
68
|
-
section.append(button);
|
|
69
|
-
return section;
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
actions: {
|
|
73
|
-
login: async (params) => {
|
|
74
|
-
if (!params) return;
|
|
75
|
-
actor.send({ type: "auth.login", username: params.username });
|
|
76
|
-
},
|
|
77
|
-
logout: async () => actor.send({ type: "auth.logout" }),
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// 3. Create the UI factory (once, at module level)
|
|
82
|
-
const ui = createPlayUI(registryResult, {
|
|
83
|
-
onRenderError: (error, elementType) => console.warn(`<${elementType}> crashed:`, error),
|
|
84
|
-
fallback: (() => {
|
|
85
|
-
const el = document.createElement("p");
|
|
86
|
-
el.textContent = "Loading…";
|
|
87
|
-
return el;
|
|
88
|
-
})(),
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// 4. Mount when actor and container are ready
|
|
92
|
-
const actor = createPlayer()();
|
|
93
|
-
actor.start();
|
|
94
|
-
|
|
95
|
-
const disconnect = ui.mount(actor, document.getElementById("app")!);
|
|
96
|
-
|
|
97
|
-
// Cleanup:
|
|
98
|
-
disconnect();
|
|
17
|
+
```bash
|
|
18
|
+
npm install xstate @xstate/store @json-render/core @json-render/xstate
|
|
99
19
|
```
|
|
100
20
|
|
|
101
|
-
## Quick Start
|
|
102
|
-
|
|
103
|
-
The lower-level one-call pattern — takes a catalog and component map directly:
|
|
21
|
+
## Quick Start
|
|
104
22
|
|
|
105
23
|
```typescript
|
|
106
24
|
import { createRenderer, schema } from "@xmachines/play-dom";
|
|
@@ -108,7 +26,7 @@ import { defineCatalog } from "@json-render/core";
|
|
|
108
26
|
import { z } from "zod";
|
|
109
27
|
import type { ComponentFn } from "@xmachines/play-dom";
|
|
110
28
|
|
|
111
|
-
// 1. Define catalog
|
|
29
|
+
// 1. Define a catalog
|
|
112
30
|
const catalog = defineCatalog(schema, {
|
|
113
31
|
components: {
|
|
114
32
|
Home: { props: z.object({ title: z.string() }) },
|
|
@@ -119,210 +37,200 @@ const catalog = defineCatalog(schema, {
|
|
|
119
37
|
logout: {},
|
|
120
38
|
},
|
|
121
39
|
});
|
|
122
|
-
type AppCatalog = typeof catalog;
|
|
123
40
|
|
|
124
41
|
// 2. Implement components
|
|
125
|
-
const Home: ComponentFn<
|
|
42
|
+
const Home: ComponentFn<typeof catalog, "Home"> = ({ props }) => {
|
|
126
43
|
const el = document.createElement("section");
|
|
127
44
|
el.textContent = props.title;
|
|
128
45
|
return el;
|
|
129
46
|
};
|
|
130
47
|
|
|
131
|
-
const Login: ComponentFn<
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
input.value = props.username ?? "";
|
|
135
|
-
input.addEventListener("input", () =>
|
|
136
|
-
ctx.store.update((s) => ({ ...s, username: input.value })),
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
const button = document.createElement("button");
|
|
140
|
-
button.textContent = "Log In";
|
|
48
|
+
const Login: ComponentFn<typeof catalog, "Login"> = ({ props, on }) => {
|
|
49
|
+
const el = document.createElement("section");
|
|
50
|
+
const btn = document.createElement("button");
|
|
141
51
|
const submit = on("submit");
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return section;
|
|
52
|
+
btn.addEventListener("click", () => submit.emit());
|
|
53
|
+
el.append(btn);
|
|
54
|
+
return el;
|
|
146
55
|
};
|
|
147
56
|
|
|
148
|
-
// 3.
|
|
57
|
+
// 3. Build the factory once (module scope)
|
|
149
58
|
const mount = createRenderer(catalog, { Home, Login });
|
|
150
59
|
|
|
151
60
|
// 4. Mount when actor and container are ready
|
|
152
|
-
const actor = createPlayer()();
|
|
153
|
-
actor.start();
|
|
154
|
-
|
|
155
61
|
const disconnect = mount(actor, document.getElementById("app")!);
|
|
156
62
|
|
|
157
|
-
// Cleanup
|
|
63
|
+
// 5. Cleanup on teardown
|
|
158
64
|
disconnect();
|
|
159
65
|
```
|
|
160
66
|
|
|
161
|
-
##
|
|
67
|
+
## Usage
|
|
162
68
|
|
|
163
|
-
|
|
69
|
+
### `createRenderer` — one-call factory (recommended)
|
|
70
|
+
|
|
71
|
+
`createRenderer` is the simplest integration path. Call it once at module scope with your catalog and component map, then call the returned `mount` function for each actor/container pair.
|
|
164
72
|
|
|
165
73
|
```typescript
|
|
166
|
-
import {
|
|
74
|
+
import { createRenderer, schema } from "@xmachines/play-dom";
|
|
167
75
|
import { defineCatalog } from "@json-render/core";
|
|
168
|
-
import { z } from "zod";
|
|
169
76
|
|
|
170
77
|
const catalog = defineCatalog(schema, {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
78
|
+
/* ... */
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const mount = createRenderer(catalog, { MyComponent });
|
|
82
|
+
|
|
83
|
+
const disconnect = mount(actor, document.getElementById("app")!);
|
|
84
|
+
// Returns a cleanup function — call it to stop rendering and clear the container.
|
|
85
|
+
disconnect();
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `createPlayUI` — batteries-included factory with full options
|
|
89
|
+
|
|
90
|
+
Use `createPlayUI` when you need render error handling, a fallback element, or a shared `registryResult` that you also need to reference programmatically (e.g. for `executeAction`).
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { defineRegistry, createPlayUI, schema } from "@xmachines/play-dom";
|
|
94
|
+
import { defineCatalog } from "@json-render/core";
|
|
95
|
+
|
|
96
|
+
const catalog = defineCatalog(schema, {
|
|
97
|
+
/* ... */
|
|
178
98
|
});
|
|
179
99
|
|
|
180
|
-
// Action handlers receive (params, setState, state)
|
|
181
|
-
// - params: resolved from the spec's on.submit.params (e.g. { $state: "/username" })
|
|
182
|
-
// - setState: write to the local state store (e.g. to clear a form)
|
|
183
|
-
// - state: current local state store snapshot
|
|
184
100
|
const registryResult = defineRegistry(catalog, {
|
|
185
|
-
components: {
|
|
186
|
-
Home: ({ props }) => {
|
|
187
|
-
const el = document.createElement("section");
|
|
188
|
-
el.textContent = props.title;
|
|
189
|
-
return el;
|
|
190
|
-
},
|
|
191
|
-
},
|
|
101
|
+
components: { Home, Login },
|
|
192
102
|
actions: {
|
|
193
103
|
login: async (params, setState) => {
|
|
194
|
-
|
|
195
|
-
actor.send({ type: "auth.login", username: params.username });
|
|
196
|
-
setState((prev) => ({ ...prev, username: "" })); // clear the form
|
|
104
|
+
/* ... */
|
|
197
105
|
},
|
|
198
106
|
logout: async () => actor.send({ type: "auth.logout" }),
|
|
199
107
|
},
|
|
200
108
|
});
|
|
201
109
|
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
registryResult, // wires setState/state from xstate store automatically
|
|
206
|
-
container: document.getElementById("app")!,
|
|
110
|
+
const mount = createPlayUI(registryResult, {
|
|
111
|
+
onRenderError: console.error,
|
|
112
|
+
fallback: document.getElementById("loading")!,
|
|
207
113
|
});
|
|
114
|
+
|
|
115
|
+
const disconnect = mount(actor, document.getElementById("app")!);
|
|
116
|
+
disconnect();
|
|
208
117
|
```
|
|
209
118
|
|
|
210
|
-
|
|
119
|
+
### `PlayRenderer` — class-based lifecycle control
|
|
211
120
|
|
|
212
|
-
|
|
121
|
+
Use `PlayRenderer` directly when you need explicit `connect()` / `disconnect()` lifecycle control, or when integrating into a system that manages the renderer's lifetime externally.
|
|
213
122
|
|
|
214
123
|
```typescript
|
|
215
|
-
|
|
216
|
-
props, // catalog-typed props for this component
|
|
217
|
-
children, // Node[] — rendered child nodes
|
|
218
|
-
on, // (eventName) => EventHandle — get emit() for catalog-declared events
|
|
219
|
-
emit, // (eventName) => void — fire an event directly
|
|
220
|
-
bindings, // Record<string, string> — $bindState paths for two-way bindings
|
|
221
|
-
ctx, // DomRenderContext — store, send, handlers, loading, functions
|
|
222
|
-
}) => {
|
|
223
|
-
const el = document.createElement("div");
|
|
224
|
-
el.append(...children);
|
|
225
|
-
return el;
|
|
226
|
-
};
|
|
227
|
-
```
|
|
124
|
+
import { PlayRenderer, defineRegistry, schema } from "@xmachines/play-dom";
|
|
228
125
|
|
|
229
|
-
|
|
126
|
+
const registryResult = defineRegistry(catalog, { components, actions });
|
|
230
127
|
|
|
231
|
-
|
|
128
|
+
const renderer = new PlayRenderer(container, actor, registryResult.registry, { registryResult });
|
|
232
129
|
|
|
233
|
-
|
|
234
|
-
|
|
130
|
+
renderer.connect();
|
|
131
|
+
// Later:
|
|
132
|
+
renderer.disconnect();
|
|
235
133
|
```
|
|
236
134
|
|
|
237
|
-
|
|
135
|
+
**Controlled store mode** — supply an external `StateStore` so the renderer shares state with other parts of your app:
|
|
238
136
|
|
|
239
137
|
```typescript
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
input.value = props.username ?? "";
|
|
243
|
-
// Write back to the store on user input
|
|
244
|
-
input.addEventListener("input", () => {
|
|
245
|
-
ctx.store.update((s) => ({ ...s, username: input.value }));
|
|
246
|
-
});
|
|
247
|
-
return input;
|
|
248
|
-
};
|
|
249
|
-
```
|
|
138
|
+
import { createAtom } from "@xstate/store";
|
|
139
|
+
import { xstateStoreStateStore } from "@json-render/xstate";
|
|
250
140
|
|
|
251
|
-
|
|
141
|
+
const atom = createAtom({ username: "" });
|
|
142
|
+
const store = xstateStoreStateStore({ atom });
|
|
252
143
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
submit.emit(); // resolves params from store, calls action handler
|
|
259
|
-
});
|
|
260
|
-
}
|
|
144
|
+
const renderer = new PlayRenderer(container, actor, registryResult.registry, {
|
|
145
|
+
registryResult,
|
|
146
|
+
store,
|
|
147
|
+
});
|
|
148
|
+
renderer.connect();
|
|
261
149
|
```
|
|
262
150
|
|
|
263
|
-
### `
|
|
151
|
+
### `connectRenderer` — functional API (backward-compatible)
|
|
152
|
+
|
|
153
|
+
`connectRenderer` is the original functional API, equivalent to creating a `PlayRenderer` and calling `connect()` in one step. Prefer `createRenderer` or `createPlayUI` for new code.
|
|
264
154
|
|
|
265
155
|
```typescript
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
156
|
+
import { connectRenderer, defineRegistry, schema } from "@xmachines/play-dom";
|
|
157
|
+
|
|
158
|
+
const registryResult = defineRegistry(catalog, {
|
|
159
|
+
components: {
|
|
160
|
+
Home: ({ props }) => {
|
|
161
|
+
const el = document.createElement("section");
|
|
162
|
+
el.textContent = props.title;
|
|
163
|
+
return el;
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
actions: {
|
|
167
|
+
logout: async () => actor.send({ type: "auth.logout" }),
|
|
168
|
+
},
|
|
169
|
+
});
|
|
277
170
|
|
|
278
|
-
|
|
171
|
+
const disconnect = connectRenderer({
|
|
172
|
+
actor,
|
|
173
|
+
registry: registryResult.registry,
|
|
174
|
+
registryResult,
|
|
175
|
+
container: document.getElementById("app")!,
|
|
176
|
+
});
|
|
279
177
|
|
|
280
|
-
|
|
178
|
+
// Later:
|
|
179
|
+
disconnect();
|
|
180
|
+
```
|
|
281
181
|
|
|
282
|
-
|
|
283
|
-
| ------------------------------------------------------ | ------------------------------------------------------------------------------------- |
|
|
284
|
-
| `visible` | Boolean or `{ $state: "/path" }` — hide element when false |
|
|
285
|
-
| `on.submit` / `on.click` | Action binding — `{ action: "login", params: { username: { $state: "/username" } } }` |
|
|
286
|
-
| `repeat: { statePath, key? }` | Render children once per item in the state array at `statePath` |
|
|
287
|
-
| `watch: { "/path": actionBinding }` | Fire action when store path changes after mount |
|
|
288
|
-
| `props.username: { $bindState: "/username" }` | Two-way binding — read from store, write back via `ctx.store.update()` |
|
|
289
|
-
| `props.value: { $state: "/value" }` | Read-only store reference |
|
|
290
|
-
| `props.label: { $computed: "computeFn", args: [...] }` | Computed prop via `functions` map |
|
|
182
|
+
## API Summary
|
|
291
183
|
|
|
292
|
-
|
|
184
|
+
### XMachines Layer
|
|
293
185
|
|
|
294
|
-
|
|
295
|
-
|
|
186
|
+
| Export | Kind | Description |
|
|
187
|
+
| ---------------------------------------- | -------- | --------------------------------------------------------------------------- |
|
|
188
|
+
| `createRenderer(catalog, components)` | function | One-call factory — returns `mount(actor, container, options?) → disconnect` |
|
|
189
|
+
| `createPlayUI(registryResult, options?)` | function | Batteries-included factory — returns `MountFn` |
|
|
190
|
+
| `PlayRenderer` | class | Class-based renderer with `connect()` / `disconnect()` lifecycle |
|
|
191
|
+
| `connectRenderer(options)` | function | Functional API; backward-compatible alternative to `PlayRenderer` |
|
|
192
|
+
| `defineRegistry(catalog, options)` | function | Build a catalog-typed `DomRegistry` with typed handlers |
|
|
193
|
+
| `renderSpec(...)` | function | Pure Spec → DOM renderer (low-level) |
|
|
194
|
+
| `schema` | const | The `@json-render/dom` schema — pass to `defineCatalog()` |
|
|
296
195
|
|
|
297
|
-
|
|
196
|
+
### Key Types
|
|
298
197
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
198
|
+
| Type | Description |
|
|
199
|
+
| ------------------------ | -------------------------------------------------------------------------------------- |
|
|
200
|
+
| `ComponentFn<C, K>` | Catalog-typed component function — returns `HTMLElement \| Text \| null` |
|
|
201
|
+
| `ComponentContext<C, K>` | Context passed to each component: `props`, `children`, `emit`, `on`, `bindings`, `ctx` |
|
|
202
|
+
| `ActionFn<C, K>` | Catalog-typed action function — receives `(params, setState, state)` |
|
|
203
|
+
| `EventHandle` | Handle returned by `on(eventName)` — has `emit()`, `shouldPreventDefault`, `bound` |
|
|
204
|
+
| `SetState` | State updater: `(prev => next) => void` |
|
|
205
|
+
| `DefineRegistryResult` | Result from `defineRegistry` — has `registry`, `handlers`, `executeAction` |
|
|
206
|
+
| `PlayDomOptions` | Options for `PlayRenderer` and `connectRenderer` |
|
|
207
|
+
| `BaseComponentProps<P>` | Catalog-agnostic component props for shared component libraries |
|
|
208
|
+
| `DomRegistry` | Raw registry type: `Record<string, DomComponentRenderer>` |
|
|
209
|
+
| `DomSchema` | Type of the `schema` export |
|
|
302
210
|
|
|
303
|
-
|
|
304
|
-
renderer.disconnect(); // stops watching, clears container
|
|
211
|
+
### Error Classes
|
|
305
212
|
|
|
306
|
-
|
|
307
|
-
|
|
213
|
+
| Class | Error Code | Description |
|
|
214
|
+
| ----------------------- | --------------------------------- | ---------------------------------------- |
|
|
215
|
+
| `MissingCatalogError` | `PLAY_RENDERER_MISSING_CATALOG` | Components map was `null` or `undefined` |
|
|
216
|
+
| `MissingComponentError` | `PLAY_RENDERER_MISSING_COMPONENT` | Component name not found in catalog |
|
|
308
217
|
|
|
309
|
-
##
|
|
218
|
+
## Rendering Behaviour
|
|
310
219
|
|
|
311
|
-
|
|
220
|
+
- **Initial render is synchronous** — the container is populated before `connect()` or `connectRenderer()` returns.
|
|
221
|
+
- **Signal-driven re-renders are microtask-deferred** — `watchSignal` schedules updates on the next microtask queue tick.
|
|
222
|
+
- **Null view** clears the container. A `fallback` element can be shown on initial mount when the view is `null`; it is **not** re-appended if the view later transitions back to `null` after a non-null view.
|
|
223
|
+
- **Double `connect()` is safe** — calling `connect()` on an already-connected renderer auto-disconnects first.
|
|
224
|
+
- **`disconnect()` clears the container** and unsubscribes all signal and store watchers.
|
|
312
225
|
|
|
313
|
-
|
|
314
|
-
| ---------------- | ------------------------------- | ----------------------------------------------------------------------------- |
|
|
315
|
-
| `actor` | `AbstractActor & Viewable` | Actor providing `currentView` signal |
|
|
316
|
-
| `registry` | `DomRegistry` | Component renderer map from `defineRegistry` |
|
|
317
|
-
| `registryResult` | `DefineRegistryResult` | Preferred — auto-wires `setState`/`state` from xstate store |
|
|
318
|
-
| `handlers` | `Record<string, ActionHandler>` | Pre-resolved handlers (legacy / advanced) |
|
|
319
|
-
| `container` | `HTMLElement` | DOM element to render into |
|
|
320
|
-
| `fallback` | `HTMLElement \| null` | Shown on initial mount when view is `null` (initial mount only) |
|
|
321
|
-
| `store` | `StateStore` | External store — controlled mode, overrides `spec.state` |
|
|
322
|
-
| `loading` | `boolean` | Streaming mode — suppresses missing-child warnings, exposes `ctx.ctx.loading` |
|
|
226
|
+
## Testing
|
|
323
227
|
|
|
324
|
-
|
|
228
|
+
```bash
|
|
229
|
+
# Run all tests (jsdom environment)
|
|
230
|
+
npm test
|
|
231
|
+
|
|
232
|
+
# Run with coverage
|
|
233
|
+
npm run test:coverage
|
|
234
|
+
```
|
|
325
235
|
|
|
326
|
-
|
|
327
|
-
- [DOM Router adapter](../play-dom-router/README.md)
|
|
328
|
-
- [Play RFC](../../packages/docs/rfc/play.md)
|
|
236
|
+
Tests live in `test/` and use [Vitest](https://vitest.dev/) with a jsdom environment. Coverage thresholds: 80% lines, functions, branches, and statements.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmachines/play-dom",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.47",
|
|
4
4
|
"description": "Vanilla DOM renderer for XMachines Play architecture with signal-driven rendering",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"actor",
|
|
@@ -43,15 +43,15 @@
|
|
|
43
43
|
"prepublishOnly": "npm run build"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@xmachines/play": "1.0.0-beta.
|
|
47
|
-
"@xmachines/play-actor": "1.0.0-beta.
|
|
48
|
-
"@xmachines/play-signals": "1.0.0-beta.
|
|
46
|
+
"@xmachines/play": "1.0.0-beta.47",
|
|
47
|
+
"@xmachines/play-actor": "1.0.0-beta.47",
|
|
48
|
+
"@xmachines/play-signals": "1.0.0-beta.47"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@json-render/core": "^0.18.0",
|
|
52
52
|
"@json-render/xstate": "^0.18.0",
|
|
53
53
|
"@types/node": "^25.6.0",
|
|
54
|
-
"@xmachines/shared": "1.0.0-beta.
|
|
54
|
+
"@xmachines/shared": "1.0.0-beta.47",
|
|
55
55
|
"@xstate/store": "^3.17.0",
|
|
56
56
|
"oxfmt": "^0.45.0",
|
|
57
57
|
"oxlint": "^1.60.0",
|