@xmachines/play-react 1.0.0-beta.30 → 1.0.0-beta.31

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 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
- - `useStateBinding` — re-exported from `@json-render/react`
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 no-op action stubs
84
- const { registry } = defineRegistry(catalog, {
83
+ // 3. Build registry — wires components to catalog and declares action handlers
84
+ const registryResult = defineRegistry(catalog, {
85
85
  components: { Login, Dashboard },
86
- actions: { login: async () => {}, logout: async () => {} },
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
- registry={registry} // required
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
- **`registry`** — Built with `defineRegistry(catalog, { components, actions })` from `@xmachines/play-react`.
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} registry={registry} store={store} actions={{ login: "auth.login" }} />;
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 { registry } = defineRegistry(catalog, {
217
+ const registryResult = defineRegistry(catalog, {
228
218
  components: { Login, Dashboard },
229
- actions: { login: async () => {}, logout: async () => {} },
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} registry={registry} />
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 `useStateBinding` from `@xmachines/play-react`
324
+ - `@json-render/react` drives rendering; `PlayRenderer` is the signal bridge — import `defineRegistry`, `ComponentFn`, `ComponentContext`, and `useBoundProp` from `@xmachines/play-react`
@@ -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 action names to actor.send() via the `actions` prop mapping
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 { registry } = defineRegistry(catalog, { components: { ... } });
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} registry={registry} actions={{ login: "auth.login" }} />
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} registry={registry} store={store} actions={{ login: "auth.login" }} />
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;AAOhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAYpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAqEpD,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"}
@@ -1,4 +1,4 @@
1
- import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
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 action names to actor.send() via the `actions` prop mapping
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 { registry } = defineRegistry(catalog, { components: { ... } });
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} registry={registry} actions={{ login: "auth.login" }} />
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} registry={registry} store={store} actions={{ login: "auth.login" }} />
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, registry, store: externalStore, fallback = null, onError, actions = {}, }) => {
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
- // Build ActionProvider handlers from actions mapping
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
@@ -1 +1 @@
1
- {"version":3,"file":"PlayRenderer.js","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PlayRenderer.js","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":"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"}
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`, `useStateBinding`, `ComponentFn`, and
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, useStateBinding, useBoundProp } from "@json-render/react";
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";
@@ -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,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnF,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"}
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`, `useStateBinding`, `ComponentFn`, and
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, useStateBinding, useBoundProp } from "@json-render/react";
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,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
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 { ComponentRegistry } from "@json-render/react";
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, EventFromLogic } from "xstate";
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. When a specific machine type is provided, the `actions` values
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 registry - ComponentRegistry from defineRegistry() in @json-render/react
20
- * @property store - Optional external StateStore (e.g. from @json-render/xstate). When
21
- * provided, PlayRenderer operates in controlled mode spec.state is ignored and the
22
- * store is the single source of truth for UI state. When omitted, a fresh
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
- /** ComponentRegistry from defineRegistry() in @json-render/react */
35
- registry: ComponentRegistry;
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. The atom resets automatically when the actor transitions to a
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} registry={registry} onError={Sentry.captureException} />
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
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,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,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,iBAAiB,CAAC,MAAM,SAAS,aAAa,GAAG,aAAa;IAC9E,4EAA4E;IAC5E,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;IAExC,oEAAoE;IACpE,QAAQ,EAAE,iBAAiB,CAAC;IAE5B;;;;;;;OAOG;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;IAExD;;;;OAIG;IACH,OAAO,CAAC,EAAE;QAAE,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;KAAE,CAAC;CACnE"}
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.30",
3
+ "version": "1.0.0-beta.31",
4
4
  "description": "React renderer for XMachines Play architecture with signal-driven rendering",
5
5
  "keywords": [
6
6
  "actor",
@@ -45,9 +45,9 @@
45
45
  "prepublishOnly": "npm run build"
46
46
  },
47
47
  "dependencies": {
48
- "@xmachines/play": "1.0.0-beta.30",
49
- "@xmachines/play-actor": "1.0.0-beta.30",
50
- "@xmachines/play-signals": "1.0.0-beta.30"
48
+ "@xmachines/play": "1.0.0-beta.31",
49
+ "@xmachines/play-actor": "1.0.0-beta.31",
50
+ "@xmachines/play-signals": "1.0.0-beta.31"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@json-render/core": "^0.16.0",
@@ -58,7 +58,7 @@
58
58
  "@types/node": "^25.5.0",
59
59
  "@types/react": "^19.2.14",
60
60
  "@types/react-dom": "^19.2.3",
61
- "@xmachines/shared": "1.0.0-beta.30",
61
+ "@xmachines/shared": "1.0.0-beta.31",
62
62
  "@xstate/store": ">=3.17.0",
63
63
  "jsdom": "^29.0.1",
64
64
  "oxfmt": "^0.43.0",