@xmachines/play-react 1.0.0-beta.30 → 1.0.0-beta.32
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 +28 -30
- package/dist/PlayRenderer.d.ts +11 -4
- package/dist/PlayRenderer.d.ts.map +1 -1
- package/dist/PlayRenderer.js +32 -14
- package/dist/PlayRenderer.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +27 -26
- package/dist/types.d.ts.map +1 -1
- package/package.json +22 -22
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ without muting console output.
|
|
|
38
38
|
- `useSignalEffect` — React hook for subscribing to TC39 Signals
|
|
39
39
|
- `PlayErrorBoundary` — error boundary wrapping renderer output
|
|
40
40
|
- `defineRegistry` — re-exported from `@json-render/react`
|
|
41
|
-
- `
|
|
41
|
+
- `useBoundProp` — re-exported from `@json-render/react`
|
|
42
42
|
- `ComponentFn` (type) — re-exported from `@json-render/react`
|
|
43
43
|
- `ComponentContext` (type) — re-exported from `@json-render/react`
|
|
44
44
|
- `PlayRendererProps` (type)
|
|
@@ -80,10 +80,18 @@ const Dashboard: ComponentFn<typeof catalog, "Dashboard"> = ({ props }) => (
|
|
|
80
80
|
<div>Welcome, {props.username}!</div>
|
|
81
81
|
);
|
|
82
82
|
|
|
83
|
-
// 3. Build registry — wires components to catalog and declares
|
|
84
|
-
const
|
|
83
|
+
// 3. Build registry — wires components to catalog and declares action handlers
|
|
84
|
+
const registryResult = defineRegistry(catalog, {
|
|
85
85
|
components: { Login, Dashboard },
|
|
86
|
-
actions: {
|
|
86
|
+
actions: {
|
|
87
|
+
login: async (params) => {
|
|
88
|
+
if (!params) return;
|
|
89
|
+
actor.send({ type: "auth.login", username: params.username });
|
|
90
|
+
},
|
|
91
|
+
logout: async () => {
|
|
92
|
+
actor.send({ type: "auth.logout" });
|
|
93
|
+
},
|
|
94
|
+
},
|
|
87
95
|
});
|
|
88
96
|
|
|
89
97
|
// 4. Define machine with view metadata
|
|
@@ -161,13 +169,7 @@ const actor = createPlayer();
|
|
|
161
169
|
actor.start();
|
|
162
170
|
|
|
163
171
|
function App() {
|
|
164
|
-
return
|
|
165
|
-
<PlayRenderer
|
|
166
|
-
actor={actor}
|
|
167
|
-
registry={registry}
|
|
168
|
-
actions={{ login: "auth.login", logout: "auth.logout" }}
|
|
169
|
-
/>
|
|
170
|
-
);
|
|
172
|
+
return <PlayRenderer actor={actor} registryResult={registryResult} />;
|
|
171
173
|
}
|
|
172
174
|
```
|
|
173
175
|
|
|
@@ -180,8 +182,7 @@ Main component. Subscribes to `actor.currentView` and renders the spec.
|
|
|
180
182
|
```tsx
|
|
181
183
|
<PlayRenderer
|
|
182
184
|
actor={actor} // required
|
|
183
|
-
|
|
184
|
-
actions={{ login: "auth.login" }} // optional
|
|
185
|
+
registryResult={registryResult} // required
|
|
185
186
|
store={myStore} // optional — controlled mode
|
|
186
187
|
fallback={<p>Loading…</p>} // optional
|
|
187
188
|
/>
|
|
@@ -189,22 +190,11 @@ Main component. Subscribes to `actor.currentView` and renders the spec.
|
|
|
189
190
|
|
|
190
191
|
**`actor`** — A `PlayerActor` (or any `AbstractActor & Viewable`). Provides the `currentView` signal.
|
|
191
192
|
|
|
192
|
-
**`
|
|
193
|
+
**`registryResult`** — Full result from `defineRegistry(catalog, { components, actions })`. Pass the entire return value — contains both the component registry and action handlers. Action handlers are real async functions dispatching to the actor, not string event-type maps.
|
|
193
194
|
|
|
194
195
|
`defineRegistry` also accepts `onRenderError(error, elementType)`, which receives errors
|
|
195
196
|
caught by `@json-render/react`'s inner element boundary before the default logger is used.
|
|
196
197
|
|
|
197
|
-
**`actions`** — Maps json-render action names (from spec `on` bindings) to XState event type strings. Values are type-checked against `EventFromLogic<TLogic>["type"]` when `TLogic` is specified:
|
|
198
|
-
|
|
199
|
-
```tsx
|
|
200
|
-
// Typed: "bad.event" causes a compile error if it is not in the machine's event union
|
|
201
|
-
<PlayRenderer<typeof machine>
|
|
202
|
-
actor={actor}
|
|
203
|
-
registry={registry}
|
|
204
|
-
actions={{ login: "auth.login", logout: "auth.logout" }}
|
|
205
|
-
/>
|
|
206
|
-
```
|
|
207
|
-
|
|
208
198
|
**`store`** (optional) — Controls per-view UI state (form values, `$state` bindings):
|
|
209
199
|
|
|
210
200
|
- **Omitted (uncontrolled, default):** A fresh `@xstate/store` atom is created per view transition, seeded from `view.spec.state`. The atom resets automatically on each state transition.
|
|
@@ -217,16 +207,24 @@ import type { StateStore } from "@json-render/core";
|
|
|
217
207
|
|
|
218
208
|
const store: StateStore = xstateStoreStateStore({ atom: createAtom({ username: "alice" }) });
|
|
219
209
|
|
|
220
|
-
<PlayRenderer actor={actor}
|
|
210
|
+
<PlayRenderer actor={actor} registryResult={registryResult} store={store} />;
|
|
221
211
|
```
|
|
222
212
|
|
|
223
213
|
**Inner render errors** — You can intercept catalog component render failures without
|
|
224
214
|
overriding the outer `PlayErrorBoundary`:
|
|
225
215
|
|
|
226
216
|
```tsx
|
|
227
|
-
const
|
|
217
|
+
const registryResult = defineRegistry(catalog, {
|
|
228
218
|
components: { Login, Dashboard },
|
|
229
|
-
actions: {
|
|
219
|
+
actions: {
|
|
220
|
+
login: async (params) => {
|
|
221
|
+
if (!params) return;
|
|
222
|
+
actor.send({ type: "auth.login", username: params.username });
|
|
223
|
+
},
|
|
224
|
+
logout: async () => {
|
|
225
|
+
actor.send({ type: "auth.logout" });
|
|
226
|
+
},
|
|
227
|
+
},
|
|
230
228
|
onRenderError(error, elementType) {
|
|
231
229
|
reportExpectedRenderError(error, elementType);
|
|
232
230
|
},
|
|
@@ -289,7 +287,7 @@ import { PlayErrorBoundary } from "@xmachines/play-react";
|
|
|
289
287
|
fallback={<p>Something went wrong.</p>}
|
|
290
288
|
onError={(err, info) => Sentry.captureException(err, { extra: info })}
|
|
291
289
|
>
|
|
292
|
-
<PlayRenderer actor={actor}
|
|
290
|
+
<PlayRenderer actor={actor} registryResult={registryResult} />
|
|
293
291
|
</PlayErrorBoundary>;
|
|
294
292
|
```
|
|
295
293
|
|
|
@@ -323,4 +321,4 @@ Priority: **route param fills `undefined` slots; explicit non-`undefined` spec p
|
|
|
323
321
|
- React `useState` is **only** used to trigger re-renders — never for business logic
|
|
324
322
|
- `actor.currentView` (TC39 Signal) is the sole render trigger; `PlayRenderer` is a passive observer
|
|
325
323
|
- Per-view UI state lives in an `@xstate/store` atom, not in React state
|
|
326
|
-
- `@json-render/react` drives rendering; `PlayRenderer` is the signal bridge — import `defineRegistry`, `ComponentFn`, `ComponentContext`, and `
|
|
324
|
+
- `@json-render/react` drives rendering; `PlayRenderer` is the signal bridge — import `defineRegistry`, `ComponentFn`, `ComponentContext`, and `useBoundProp` from `@xmachines/play-react`
|
package/dist/PlayRenderer.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type { PlayRendererProps } from "./types.js";
|
|
|
14
14
|
* Architecture:
|
|
15
15
|
* - Subscribes to actor.currentView signal via useSignalEffect
|
|
16
16
|
* - Renders view.spec via StateProvider → ActionProvider → VisibilityProvider → Renderer
|
|
17
|
-
* - Routes
|
|
17
|
+
* - Routes actions via registryResult.handlers() — real async functions dispatching to actor.
|
|
18
18
|
* - State store: uses external `store` prop if provided (controlled mode); otherwise
|
|
19
19
|
* creates a fresh @xstate/store atom per view transition seeded from spec.state.
|
|
20
20
|
* The atom resets automatically when the actor transitions to a new view, mirroring
|
|
@@ -29,16 +29,23 @@ import type { PlayRendererProps } from "./types.js";
|
|
|
29
29
|
* import { PlayRenderer } from "@xmachines/play-react";
|
|
30
30
|
* import { defineRegistry } from "@json-render/react";
|
|
31
31
|
*
|
|
32
|
-
* const
|
|
32
|
+
* const registryResult = defineRegistry(catalog, {
|
|
33
|
+
* components: { Login, Dashboard },
|
|
34
|
+
* actions: {
|
|
35
|
+
* login: async ({ username }) => actor.send({ type: 'auth.login', username }),
|
|
36
|
+
* logout: async () => actor.send({ type: 'auth.logout' }),
|
|
37
|
+
* route: async ({ to, params }) => actor.send({ type: 'play.route', to, params }),
|
|
38
|
+
* },
|
|
39
|
+
* });
|
|
33
40
|
*
|
|
34
41
|
* // Uncontrolled — fresh atom created per view, seeded from spec.state:
|
|
35
|
-
* <PlayRenderer actor={actor}
|
|
42
|
+
* <PlayRenderer actor={actor} registryResult={registryResult} />
|
|
36
43
|
*
|
|
37
44
|
* // Controlled — caller provides and owns the store:
|
|
38
45
|
* import { createAtom } from "@xstate/store";
|
|
39
46
|
* import { xstateStoreStateStore } from "@json-render/xstate";
|
|
40
47
|
* const store = xstateStoreStateStore({ atom: createAtom({ username: "" }) });
|
|
41
|
-
* <PlayRenderer actor={actor}
|
|
48
|
+
* <PlayRenderer actor={actor} registryResult={registryResult} store={store} />
|
|
42
49
|
* ```
|
|
43
50
|
*
|
|
44
51
|
* @param props - Component props
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayRenderer.d.ts","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA2B,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"PlayRenderer.d.ts","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA2B,MAAM,OAAO,CAAC;AAchD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAiDpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAuDpD,CAAC"}
|
package/dist/PlayRenderer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* PlayRenderer - Main React renderer component for XMachines Play architecture
|
|
4
4
|
*
|
|
@@ -7,7 +7,7 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
|
7
7
|
* @packageDocumentation
|
|
8
8
|
*/
|
|
9
9
|
import React, { useState, useRef } from "react";
|
|
10
|
-
import { Renderer, StateProvider, ActionProvider, VisibilityProvider } from "@json-render/react";
|
|
10
|
+
import { Renderer, StateProvider, ActionProvider, VisibilityProvider, useStateStore, } from "@json-render/react";
|
|
11
11
|
import { createAtom } from "@xstate/store";
|
|
12
12
|
import { xstateStoreStateStore } from "@json-render/xstate";
|
|
13
13
|
import { useSignalEffect } from "./useSignalEffect.js";
|
|
@@ -20,6 +20,23 @@ import { ActorContext } from "./useActor.js";
|
|
|
20
20
|
function createViewStore(initialState) {
|
|
21
21
|
return xstateStoreStateStore({ atom: createAtom(initialState) });
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Inner component that runs inside StateProvider so it can access StateStore context
|
|
25
|
+
* via useStateStore(). Resolves action handlers from registryResult.handlers() using
|
|
26
|
+
* the live StateProvider set/getSnapshot functions, then renders ActionProvider.
|
|
27
|
+
*/
|
|
28
|
+
function PlayRendererInner({ registryResult, spec, }) {
|
|
29
|
+
const stateCtx = useStateStore();
|
|
30
|
+
// Build a SetState adapter: the handlers factory expects an updater-function pattern
|
|
31
|
+
// ((prev) => next), while stateCtx provides path-based set/update. This adapter
|
|
32
|
+
// bridges the two so action functions can use setState if needed.
|
|
33
|
+
const setStateAdapter = (updater) => {
|
|
34
|
+
const prev = stateCtx.getSnapshot();
|
|
35
|
+
stateCtx.update(updater(prev));
|
|
36
|
+
};
|
|
37
|
+
const handlers = registryResult.handlers(() => setStateAdapter, () => stateCtx.getSnapshot());
|
|
38
|
+
return (_jsx(ActionProvider, { handlers: handlers, children: _jsx(VisibilityProvider, { children: _jsx(Renderer, { spec: spec, registry: registryResult.registry }) }) }));
|
|
39
|
+
}
|
|
23
40
|
/**
|
|
24
41
|
* Main renderer component that subscribes to actor signals and renders UI
|
|
25
42
|
* via @json-render/react Renderer.
|
|
@@ -27,7 +44,7 @@ function createViewStore(initialState) {
|
|
|
27
44
|
* Architecture:
|
|
28
45
|
* - Subscribes to actor.currentView signal via useSignalEffect
|
|
29
46
|
* - Renders view.spec via StateProvider → ActionProvider → VisibilityProvider → Renderer
|
|
30
|
-
* - Routes
|
|
47
|
+
* - Routes actions via registryResult.handlers() — real async functions dispatching to actor.
|
|
31
48
|
* - State store: uses external `store` prop if provided (controlled mode); otherwise
|
|
32
49
|
* creates a fresh @xstate/store atom per view transition seeded from spec.state.
|
|
33
50
|
* The atom resets automatically when the actor transitions to a new view, mirroring
|
|
@@ -42,22 +59,29 @@ function createViewStore(initialState) {
|
|
|
42
59
|
* import { PlayRenderer } from "@xmachines/play-react";
|
|
43
60
|
* import { defineRegistry } from "@json-render/react";
|
|
44
61
|
*
|
|
45
|
-
* const
|
|
62
|
+
* const registryResult = defineRegistry(catalog, {
|
|
63
|
+
* components: { Login, Dashboard },
|
|
64
|
+
* actions: {
|
|
65
|
+
* login: async ({ username }) => actor.send({ type: 'auth.login', username }),
|
|
66
|
+
* logout: async () => actor.send({ type: 'auth.logout' }),
|
|
67
|
+
* route: async ({ to, params }) => actor.send({ type: 'play.route', to, params }),
|
|
68
|
+
* },
|
|
69
|
+
* });
|
|
46
70
|
*
|
|
47
71
|
* // Uncontrolled — fresh atom created per view, seeded from spec.state:
|
|
48
|
-
* <PlayRenderer actor={actor}
|
|
72
|
+
* <PlayRenderer actor={actor} registryResult={registryResult} />
|
|
49
73
|
*
|
|
50
74
|
* // Controlled — caller provides and owns the store:
|
|
51
75
|
* import { createAtom } from "@xstate/store";
|
|
52
76
|
* import { xstateStoreStateStore } from "@json-render/xstate";
|
|
53
77
|
* const store = xstateStoreStateStore({ atom: createAtom({ username: "" }) });
|
|
54
|
-
* <PlayRenderer actor={actor}
|
|
78
|
+
* <PlayRenderer actor={actor} registryResult={registryResult} store={store} />
|
|
55
79
|
* ```
|
|
56
80
|
*
|
|
57
81
|
* @param props - Component props
|
|
58
82
|
* @returns React element rendering current view from actor
|
|
59
83
|
*/
|
|
60
|
-
export const PlayRenderer = ({ actor,
|
|
84
|
+
export const PlayRenderer = ({ actor, registryResult, store: externalStore, fallback = null, onError, }) => {
|
|
61
85
|
// React state for triggering re-renders (NOT business logic state)
|
|
62
86
|
// Signal is source of truth, useState is just React's render trigger
|
|
63
87
|
const [view, setView] = useState(() => actor.currentView.get());
|
|
@@ -92,12 +116,6 @@ export const PlayRenderer = ({ actor, registry, store: externalStore, fallback =
|
|
|
92
116
|
}
|
|
93
117
|
store = internalStoreRef.current;
|
|
94
118
|
}
|
|
95
|
-
|
|
96
|
-
// Each handler calls actor.send() with the configured event type + params
|
|
97
|
-
const handlers = Object.fromEntries(Object.entries(actions).map(([actionName, eventType]) => [
|
|
98
|
-
actionName,
|
|
99
|
-
async (params = {}) => actor.send({ type: eventType, ...params }),
|
|
100
|
-
]));
|
|
101
|
-
return (_jsx(ActorContext.Provider, { value: actor, children: _jsx(PlayErrorBoundary, { fallback: fallback, ...(onError && { onError }), children: _jsx(StateProvider, { store: store, children: _jsx(ActionProvider, { handlers: handlers, children: _jsx(VisibilityProvider, { children: _jsx(Renderer, { spec: view.spec ?? null, registry: registry }) }) }) }) }) }));
|
|
119
|
+
return (_jsx(ActorContext.Provider, { value: actor, children: _jsx(PlayErrorBoundary, { fallback: fallback, ...(onError && { onError }), children: _jsx(StateProvider, { store: store, children: _jsx(PlayRendererInner, { registryResult: registryResult, spec: view.spec ?? null }) }) }) }));
|
|
102
120
|
};
|
|
103
121
|
//# sourceMappingURL=PlayRenderer.js.map
|
package/dist/PlayRenderer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayRenderer.js","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":";AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAChD,OAAO,
|
|
1
|
+
{"version":3,"file":"PlayRenderer.js","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":";AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAChD,OAAO,EACN,QAAQ,EACR,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,aAAa,GACb,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAI3D,OAAO,EAAE,YAAY,EAAkB,MAAM,eAAe,CAAC;AAE7D;;;GAGG;AACH,SAAS,eAAe,CAAC,YAAqC;IAC7D,OAAO,qBAAqB,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;AAClE,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,EAC1B,cAAc,EACd,IAAI,GAIJ;IACA,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IAEjC,qFAAqF;IACrF,gFAAgF;IAChF,kEAAkE;IAClE,MAAM,eAAe,GAAa,CAAC,OAAO,EAAE,EAAE;QAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QACpC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAkC,cAAc,CAAC,QAAQ,CACtE,GAAG,EAAE,CAAC,eAAe,EACrB,GAAG,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,CAC5B,CAAC;IAEF,OAAO,CACN,KAAC,cAAc,IAAC,QAAQ,EAAE,QAAQ,YACjC,KAAC,kBAAkB,cAClB,KAAC,QAAQ,IAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,CAAC,QAAQ,GAAI,GACvC,GACL,CACjB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,CAAC,MAAM,YAAY,GAAgC,CAAC,EACzD,KAAK,EACL,cAAc,EACd,KAAK,EAAE,aAAa,EACpB,QAAQ,GAAG,IAAI,EACf,OAAO,GACP,EAAE,EAAE;IACJ,mEAAmE;IACnE,qEAAqE;IACrE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAsB,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC;IAErF,+DAA+D;IAC/D,qFAAqF;IACrF,0CAA0C;IAC1C,MAAM,gBAAgB,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,MAAM,CAAsB,IAAI,CAAC,CAAC;IAEtD,8BAA8B;IAC9B,eAAe,CAAC,GAAG,EAAE;QACpB,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC;QAC5C,OAAO,CAAC,WAAW,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,6CAA6C;IAC7C,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,OAAO,4BAAG,QAAQ,GAAI,CAAC;IACxB,CAAC;IAED,8CAA8C;IAC9C,+DAA+D;IAC/D,0EAA0E;IAC1E,IAAI,KAAiB,CAAC;IACtB,IAAI,aAAa,EAAE,CAAC;QACnB,KAAK,GAAG,aAAa,CAAC;IACvB,CAAC;SAAM,CAAC;QACP,6DAA6D;QAC7D,mEAAmE;QACnE,IAAI,gBAAgB,CAAC,OAAO,KAAK,IAAI,IAAI,WAAW,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YACvE,MAAM,YAAY,GAAI,IAAI,CAAC,IAAI,EAAE,KAAiC,IAAI,EAAE,CAAC;YACzE,gBAAgB,CAAC,OAAO,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;YACzD,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;QAC5B,CAAC;QACD,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC;IAClC,CAAC;IAED,OAAO,CACN,KAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAkB,YAC/C,KAAC,iBAAiB,IAAC,QAAQ,EAAE,QAAQ,KAAM,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC,YAClE,KAAC,aAAa,IAAC,KAAK,EAAE,KAAK,YAE1B,KAAC,iBAAiB,IAAC,cAAc,EAAE,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,GAAI,GAC/D,GACG,GACG,CACxB,CAAC;AACH,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* **Key principle:** React state is NEVER used for business logic—only for
|
|
10
10
|
* triggering React's render cycle. Signals are the source of truth.
|
|
11
11
|
*
|
|
12
|
-
* Re-exports `defineRegistry`, `
|
|
12
|
+
* Re-exports `defineRegistry`, `useBoundProp`, `ComponentFn`, and
|
|
13
13
|
* `ComponentContext` from `@json-render/react` so consumers import everything
|
|
14
14
|
* from `@xmachines/play-react` rather than `@json-render/react` directly.
|
|
15
15
|
*
|
|
@@ -20,7 +20,7 @@ export { PlayRenderer } from "./PlayRenderer.js";
|
|
|
20
20
|
export { useSignalEffect } from "./useSignalEffect.js";
|
|
21
21
|
export { PlayErrorBoundary } from "./PlayErrorBoundary.js";
|
|
22
22
|
export { useActor } from "./useActor.js";
|
|
23
|
-
export { defineRegistry,
|
|
23
|
+
export { defineRegistry, useBoundProp } from "@json-render/react";
|
|
24
24
|
export type { ComponentFn, ComponentContext } from "@json-render/react";
|
|
25
25
|
export type { PlayRendererProps } from "./types.js";
|
|
26
26
|
export type { PlayErrorBoundaryProps, PlayErrorBoundaryState } from "./PlayErrorBoundary.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGxE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,YAAY,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAC7F,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* **Key principle:** React state is NEVER used for business logic—only for
|
|
10
10
|
* triggering React's render cycle. Signals are the source of truth.
|
|
11
11
|
*
|
|
12
|
-
* Re-exports `defineRegistry`, `
|
|
12
|
+
* Re-exports `defineRegistry`, `useBoundProp`, `ComponentFn`, and
|
|
13
13
|
* `ComponentContext` from `@json-render/react` so consumers import everything
|
|
14
14
|
* from `@xmachines/play-react` rather than `@json-render/react` directly.
|
|
15
15
|
*
|
|
@@ -23,5 +23,5 @@ export { PlayErrorBoundary } from "./PlayErrorBoundary.js";
|
|
|
23
23
|
export { useActor } from "./useActor.js";
|
|
24
24
|
// Re-export from @json-render/react so consumers import everything from @xmachines/play-react.
|
|
25
25
|
// React's useContext works anywhere in the call tree — no wrapper needed.
|
|
26
|
-
export { defineRegistry,
|
|
26
|
+
export { defineRegistry, useBoundProp } from "@json-render/react";
|
|
27
27
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAe;AACf,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,+FAA+F;AAC/F,0EAA0E;AAC1E,OAAO,EAAE,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAe;AACf,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,+FAA+F;AAC/F,0EAA0E;AAC1E,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -3,43 +3,52 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { DefineRegistryResult } from "@json-render/react";
|
|
7
7
|
import type { StateStore } from "@json-render/core";
|
|
8
8
|
import type { AbstractActor, Viewable } from "@xmachines/play-actor";
|
|
9
9
|
import type React from "react";
|
|
10
|
-
import type { AnyActorLogic
|
|
10
|
+
import type { AnyActorLogic } from "xstate";
|
|
11
11
|
/**
|
|
12
12
|
* Props for PlayRenderer component
|
|
13
13
|
*
|
|
14
14
|
* @typeParam TLogic - The XState actor logic type. Defaults to `AnyActorLogic` for
|
|
15
|
-
* non-generic usage.
|
|
16
|
-
* are constrained to the event type strings that the machine actually accepts.
|
|
15
|
+
* non-generic usage.
|
|
17
16
|
*
|
|
18
17
|
* @property actor - Actor instance with currentView signal (requires Viewable capability)
|
|
19
|
-
* @property
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* @xstate/store atom is created internally per view transition, seeded from spec.state.
|
|
18
|
+
* @property registryResult - Full result from defineRegistry() in @json-render/react.
|
|
19
|
+
* Contains both the component registry and the action handlers factory. Action handlers
|
|
20
|
+
* are real async functions dispatching to the actor (not string-mapped event types).
|
|
21
|
+
* @property store - Optional external StateStore (controlled mode).
|
|
24
22
|
* @property fallback - Optional component shown when currentView is null
|
|
25
23
|
* @property onError - Optional callback invoked when a catalog component throws during render.
|
|
26
|
-
* Receives the error and React ErrorInfo — use to forward to Sentry, Datadog, etc.
|
|
27
|
-
* @property actions - Maps json-render action names to XState event type strings.
|
|
28
|
-
* Values are constrained to `EventFromLogic<TLogic>["type"]` — passing a non-existent
|
|
29
|
-
* event type string is a compile error when TLogic is specified.
|
|
30
24
|
*/
|
|
31
25
|
export interface PlayRendererProps<TLogic extends AnyActorLogic = AnyActorLogic> {
|
|
32
26
|
/** Actor instance with currentView signal (requires Viewable capability) */
|
|
33
27
|
actor: AbstractActor<TLogic> & Viewable;
|
|
34
|
-
/**
|
|
35
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Full result from defineRegistry() — contains component registry and action handlers.
|
|
30
|
+
* Action handlers are async functions that dispatch to the actor (not string-mapped
|
|
31
|
+
* event type stubs). Replaces the old `registry` + `actions` prop pair.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* const registryResult = defineRegistry(catalog, {
|
|
36
|
+
* components: { Login, Dashboard },
|
|
37
|
+
* actions: {
|
|
38
|
+
* login: async ({ username }) => actor.send({ type: 'auth.login', username }),
|
|
39
|
+
* logout: async () => actor.send({ type: 'auth.logout' }),
|
|
40
|
+
* },
|
|
41
|
+
* });
|
|
42
|
+
* <PlayRenderer actor={actor} registryResult={registryResult} />
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
registryResult: DefineRegistryResult;
|
|
36
46
|
/**
|
|
37
47
|
* Optional external StateStore (e.g. from `xstateStoreStateStore` in @json-render/xstate).
|
|
38
48
|
* When provided, PlayRenderer operates in controlled mode — spec.state is ignored and
|
|
39
49
|
* this store is the single source of truth for UI state (form values, etc.).
|
|
40
50
|
* When omitted, a fresh @xstate/store atom is created internally per view transition,
|
|
41
|
-
* seeded from spec.state.
|
|
42
|
-
* new view, mirroring the actor's currentView lifecycle.
|
|
51
|
+
* seeded from spec.state.
|
|
43
52
|
*/
|
|
44
53
|
store?: StateStore;
|
|
45
54
|
/** Optional component shown when currentView is null or a catalog component throws */
|
|
@@ -51,17 +60,9 @@ export interface PlayRendererProps<TLogic extends AnyActorLogic = AnyActorLogic>
|
|
|
51
60
|
*
|
|
52
61
|
* @example
|
|
53
62
|
* ```tsx
|
|
54
|
-
* <PlayRenderer actor={actor}
|
|
63
|
+
* <PlayRenderer actor={actor} registryResult={registryResult} onError={Sentry.captureException} />
|
|
55
64
|
* ```
|
|
56
65
|
*/
|
|
57
66
|
onError?: (error: Error, info: React.ErrorInfo) => void;
|
|
58
|
-
/**
|
|
59
|
-
* Maps json-render action names to XState event type strings.
|
|
60
|
-
* Values are constrained to valid event types for TLogic — wrong event type strings
|
|
61
|
-
* are caught at compile time when TLogic is specified.
|
|
62
|
-
*/
|
|
63
|
-
actions?: {
|
|
64
|
-
[actionName: string]: EventFromLogic<TLogic>["type"];
|
|
65
|
-
};
|
|
66
67
|
}
|
|
67
68
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,iBAAiB,CAAC,MAAM,SAAS,aAAa,GAAG,aAAa;IAC9E,4EAA4E;IAC5E,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;IAExC;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,EAAE,oBAAoB,CAAC;IAErC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IAEnB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC;CACxD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmachines/play-react",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.32",
|
|
4
4
|
"description": "React renderer for XMachines Play architecture with signal-driven rendering",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"actor",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsc --build",
|
|
39
|
-
"clean": "rm -rf dist *.tsbuildinfo coverage .vitest-attachments test/browser/__screenshots__
|
|
39
|
+
"clean": "rm -rf dist *.tsbuildinfo coverage .vitest-attachments test/browser/__screenshots__",
|
|
40
40
|
"lint": "oxlint .",
|
|
41
41
|
"format": "oxfmt .",
|
|
42
42
|
"test": "vitest",
|
|
@@ -45,35 +45,35 @@
|
|
|
45
45
|
"prepublishOnly": "npm run build"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@xmachines/play": "1.0.0-beta.
|
|
49
|
-
"@xmachines/play-actor": "1.0.0-beta.
|
|
50
|
-
"@xmachines/play-signals": "1.0.0-beta.
|
|
48
|
+
"@xmachines/play": "1.0.0-beta.32",
|
|
49
|
+
"@xmachines/play-actor": "1.0.0-beta.32",
|
|
50
|
+
"@xmachines/play-signals": "1.0.0-beta.32"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@json-render/core": "^0.
|
|
54
|
-
"@json-render/react": "^0.
|
|
55
|
-
"@json-render/xstate": "^0.
|
|
53
|
+
"@json-render/core": "^0.18.0",
|
|
54
|
+
"@json-render/react": "^0.18.0",
|
|
55
|
+
"@json-render/xstate": "^0.18.0",
|
|
56
56
|
"@testing-library/jest-dom": "^6.9.1",
|
|
57
57
|
"@testing-library/react": "^16.3.2",
|
|
58
|
-
"@types/node": "^25.
|
|
58
|
+
"@types/node": "^25.6.0",
|
|
59
59
|
"@types/react": "^19.2.14",
|
|
60
60
|
"@types/react-dom": "^19.2.3",
|
|
61
|
-
"@xmachines/shared": "1.0.0-beta.
|
|
62
|
-
"@xstate/store": "
|
|
63
|
-
"jsdom": "^29.0.
|
|
64
|
-
"oxfmt": "^0.
|
|
65
|
-
"oxlint": "^1.
|
|
66
|
-
"react": "^19.2.
|
|
67
|
-
"react-dom": "^19.2.
|
|
68
|
-
"typescript": "^5.9.3 || ^6.0.
|
|
69
|
-
"vitest": "^4.1.
|
|
61
|
+
"@xmachines/shared": "1.0.0-beta.32",
|
|
62
|
+
"@xstate/store": "^3.17.0",
|
|
63
|
+
"jsdom": "^29.0.2",
|
|
64
|
+
"oxfmt": "^0.45.0",
|
|
65
|
+
"oxlint": "^1.60.0",
|
|
66
|
+
"react": "^19.2.5",
|
|
67
|
+
"react-dom": "^19.2.5",
|
|
68
|
+
"typescript": "^5.9.3 || ^6.0.3",
|
|
69
|
+
"vitest": "^4.1.4",
|
|
70
70
|
"xstate": "^5.30.0"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@json-render/core": "^0.
|
|
74
|
-
"@json-render/react": "^0.
|
|
75
|
-
"@json-render/xstate": "^0.
|
|
76
|
-
"@xstate/store": "
|
|
73
|
+
"@json-render/core": "^0.18.0",
|
|
74
|
+
"@json-render/react": "^0.18.0",
|
|
75
|
+
"@json-render/xstate": "^0.18.0",
|
|
76
|
+
"@xstate/store": "^3.17.0",
|
|
77
77
|
"react": "^18.0.0 || ^19.0.0",
|
|
78
78
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
79
79
|
"xstate": "^5.30.0"
|