@xmachines/play-react 1.0.0-beta.16 → 1.0.0-beta.18

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
@@ -1,393 +1,301 @@
1
1
  # @xmachines/play-react
2
2
 
3
- **React renderer consuming signals and UI schema with provider pattern**
3
+ **React renderer for XMachines Play Architecture**
4
4
 
5
- Signal-driven React rendering layer observing actor state with zero React state for business logic.
5
+ Bridges TC39 Signal-driven actors to React's render cycle. Business logic stays in the actor; React is purely a rendering target.
6
6
 
7
7
  ## Overview
8
8
 
9
- `@xmachines/play-react` provides `PlayRenderer` and `useSignalEffect` for building React UIs that passively observe actor signals. This package enables framework-swappable architecture where React is just a rendering target subscribing to signal changes — business logic lives entirely in the actor.
9
+ `@xmachines/play-react` provides `PlayRenderer`, a React component that:
10
10
 
11
- Per [RFC Play v1](https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md), this package implements:
11
+ - Subscribes to `actor.currentView` (TC39 Signal) and re-renders on every state transition
12
+ - Renders the current view's JSON spec via `@json-render/react`
13
+ - Routes action names from spec elements to `actor.send()` via the `actions` prop
14
+ - Manages per-view UI state in an `@xstate/store` atom (automatic or caller-supplied)
12
15
 
13
- - **Signal-Only Reactivity (INV-05):** No useState/useReducer for business logic, signals only
14
- - **Passive Infrastructure (INV-04):** Components observe signals, send events to actor
16
+ Per [RFC Play v1](https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md):
15
17
 
16
- **Key Principle:** React state is never used for business logic. Signals are the source of truth.
17
-
18
- Renderer receives actor via props (provider pattern), not children.
18
+ - **Actor Authority (INV-01):** Guards in the machine decide all state transitions
19
+ - **Passive Infrastructure (INV-04):** React observes signals and dispatches events — never decides
20
+ - **Signal-Only Reactivity (INV-05):** `actor.currentView` signal is the sole render trigger
19
21
 
20
22
  ## Installation
21
23
 
22
24
  ```bash
23
- npm install react@^18.0.0 react-dom@^18.0.0
24
25
  npm install @xmachines/play-react
26
+ npm install @json-render/react @json-render/core # peer deps
27
+ npm install @json-render/xstate @xstate/store # store integration
25
28
  ```
26
29
 
27
30
  ## Current Exports
28
31
 
29
- - `PlayRenderer`
30
- - `useSignalEffect`
31
- - `PlayErrorBoundary`
32
+ - `PlayRenderer` — main renderer component
33
+ - `useActor` — hook for accessing the actor inside a `PlayRenderer` tree
34
+ - `useSignalEffect` — React hook for subscribing to TC39 Signals
35
+ - `PlayErrorBoundary` — error boundary wrapping renderer output
36
+ - `defineRegistry` — re-exported from `@json-render/react`
37
+ - `useStateBinding` — re-exported from `@json-render/react`
38
+ - `ComponentFn` (type) — re-exported from `@json-render/react`
39
+ - `ComponentContext` (type) — re-exported from `@json-render/react`
32
40
  - `PlayRendererProps` (type)
33
- - `PlayErrorBoundaryProps` (type)
34
-
35
- **Peer dependencies:**
36
-
37
- - `react` ^18.0.0 || ^19.0.0 — React runtime
38
- - `react-dom` ^18.0.0 || ^19.0.0 — React DOM renderer
41
+ - `PlayActor` (type)
39
42
 
40
43
  ## Quick Start
41
44
 
42
- ```typescript
43
- import { createRoot } from "react-dom/client";
44
- import { definePlayer } from "@xmachines/play-xstate";
45
- import { defineCatalog } from "@xmachines/play-catalog";
45
+ ```tsx
46
+ import { definePlayer, formatPlayRouteTransitions } from "@xmachines/play-xstate";
46
47
  import { PlayRenderer } from "@xmachines/play-react";
48
+ import { defineCatalog } from "@json-render/core";
49
+ import { defineRegistry } from "@xmachines/play-react";
50
+ import type { ComponentFn } from "@xmachines/play-react";
51
+ import { setup, assign } from "xstate";
47
52
  import { z } from "zod";
48
53
 
49
- // 1. Define catalog (business logic layer)
54
+ // 1. Define catalog the contract between machine spec and UI components
50
55
  const catalog = defineCatalog({
51
- LoginForm: z.object({ error: z.string().optional() }),
52
- Dashboard: z.object({
53
- userId: z.string(),
54
- username: z.string(),
55
- }),
56
+ elements: {
57
+ Login: { props: z.object({ title: z.string() }), description: "Login form" },
58
+ Dashboard: { props: z.object({ username: z.string() }), description: "Dashboard" },
59
+ },
56
60
  });
57
61
 
58
- // 2. Create React components (view layer)
59
- const components = {
60
- LoginForm: ({ error, send }) => (
61
- <form
62
- onSubmit={(e) => {
63
- e.preventDefault();
64
- const data = new FormData(e.currentTarget);
65
- send({
66
- type: "auth.login",
67
- username: data.get("username"),
68
- });
69
- }}
70
- >
71
- {error && <p style={{ color: "red" }}>{error}</p>}
72
- <input name="username" required placeholder="Username" />
73
- <button type="submit">Log In</button>
74
- </form>
75
- ),
76
- Dashboard: ({ userId, username, send }) => (
77
- <div>
78
- <h1>Welcome, {username}!</h1>
79
- <p>User ID: {userId}</p>
80
- <button onClick={() => send({ type: "auth.logout" })}>Log Out</button>
81
- </div>
82
- ),
83
- };
84
-
85
- // 3. Create player actor (business logic runtime)
86
- const createPlayer = definePlayer({ machine: authMachine, catalog });
62
+ // 2. Implement components using ComponentFn — typed against catalog entries
63
+ const Login: ComponentFn<typeof catalog, "Login"> = ({ props, emit }) => (
64
+ <form
65
+ onSubmit={(e) => {
66
+ e.preventDefault();
67
+ emit("submit");
68
+ }}
69
+ >
70
+ <h2>{props.title}</h2>
71
+ <button type="submit">Log In</button>
72
+ </form>
73
+ );
74
+
75
+ const Dashboard: ComponentFn<typeof catalog, "Dashboard"> = ({ props }) => (
76
+ <div>Welcome, {props.username}!</div>
77
+ );
78
+
79
+ // 3. Build registry — wires components to catalog and declares no-op action stubs
80
+ const { registry } = defineRegistry(catalog, {
81
+ components: { Login, Dashboard },
82
+ actions: { login: async () => {}, logout: async () => {} },
83
+ });
84
+
85
+ // 4. Define machine with view metadata
86
+ const machine = setup({
87
+ types: {
88
+ context: {} as {
89
+ isAuthenticated: boolean;
90
+ username: string | null;
91
+ routeParams: Record<string, string>;
92
+ queryParams: Record<string, string>;
93
+ },
94
+ events: {} as
95
+ | { type: "auth.login"; username: string }
96
+ | { type: "auth.logout" }
97
+ | { type: "play.route"; to: string; params?: Record<string, string> },
98
+ },
99
+ }).createMachine(
100
+ formatPlayRouteTransitions({
101
+ id: "app",
102
+ initial: "login",
103
+ context: { isAuthenticated: false, username: null, routeParams: {}, queryParams: {} },
104
+ states: {
105
+ login: {
106
+ id: "login",
107
+ meta: {
108
+ route: "/login",
109
+ view: {
110
+ component: "Login",
111
+ spec: {
112
+ root: "root",
113
+ elements: {
114
+ root: { type: "Login", props: { title: "Sign In" }, children: [] },
115
+ },
116
+ },
117
+ },
118
+ },
119
+ },
120
+ dashboard: {
121
+ id: "dashboard",
122
+ meta: {
123
+ route: "/dashboard",
124
+ view: {
125
+ component: "Dashboard",
126
+ spec: {
127
+ root: "root",
128
+ elements: {
129
+ root: { type: "Dashboard", props: { username: "" }, children: [] },
130
+ },
131
+ },
132
+ },
133
+ },
134
+ },
135
+ },
136
+ on: {
137
+ "auth.login": {
138
+ target: ".dashboard",
139
+ guard: ({ context }) => !context.isAuthenticated,
140
+ actions: assign({
141
+ isAuthenticated: true,
142
+ username: ({ event }) => event.username,
143
+ }),
144
+ },
145
+ "auth.logout": {
146
+ target: ".login",
147
+ guard: ({ context }) => context.isAuthenticated,
148
+ actions: assign({ isAuthenticated: false, username: null }),
149
+ },
150
+ },
151
+ }),
152
+ );
153
+
154
+ // 5. Create actor and render
155
+ const createPlayer = definePlayer({ machine });
87
156
  const actor = createPlayer();
88
157
  actor.start();
89
158
 
90
- // 4. Render UI (actor via props)
91
- const root = createRoot(document.getElementById("app")!);
92
- root.render(<PlayRenderer actor={actor} components={components} />);
159
+ function App() {
160
+ return (
161
+ <PlayRenderer
162
+ actor={actor}
163
+ registry={registry}
164
+ actions={{ login: "auth.login", logout: "auth.logout" }}
165
+ />
166
+ );
167
+ }
93
168
  ```
94
169
 
95
170
  ## API Reference
96
171
 
97
- ### PlayRenderer
172
+ ### `PlayRenderer`
98
173
 
99
- Main renderer component subscribing to actor signals and dynamically rendering catalog components:
174
+ Main component. Subscribes to `actor.currentView` and renders the spec.
100
175
 
101
- ```typescript
102
- interface PlayRendererProps {
103
- actor: AbstractActor<AnyActorLogic> & Viewable;
104
- components: Record<string, React.ElementType>;
105
- fallback?: React.ReactNode;
106
- }
176
+ ```tsx
177
+ <PlayRenderer
178
+ actor={actor} // required
179
+ registry={registry} // required
180
+ actions={{ login: "auth.login" }} // optional
181
+ store={myStore} // optional — controlled mode
182
+ fallback={<p>Loading…</p>} // optional
183
+ />
107
184
  ```
108
185
 
109
- **Props:**
110
-
111
- - `actor` - Actor instance with `currentView` signal
112
- - `components` - Map of component names to React components
113
- - `fallback` - Component shown when `currentView` is null (default: `null`)
114
-
115
- **Behavior:**
186
+ **`actor`** — A `PlayerActor` (or any `AbstractActor & Viewable`). Provides the `currentView` signal.
116
187
 
117
- 1. Subscribes to `actor.currentView` signal via `useSignalEffect`
118
- 2. Looks up component from `components` map using `view.component` string
119
- 3. Renders component with props from `view.props` + `send` function
188
+ **`registry`** Built with `defineRegistry(catalog, { components, actions })` from `@xmachines/play-react`.
120
189
 
121
- **Example:**
190
+ **`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:
122
191
 
123
- ```typescript
124
- <PlayRenderer
192
+ ```tsx
193
+ // Typed: "bad.event" causes a compile error if it is not in the machine's event union
194
+ <PlayRenderer<typeof machine>
125
195
  actor={actor}
126
- components={{
127
- HomePage: ({ send }) => <div>Home</div>,
128
- AboutPage: ({ send }) => <div>About</div>,
129
- }}
130
- fallback={<div>Loading...</div>}
196
+ registry={registry}
197
+ actions={{ login: "auth.login", logout: "auth.logout" }}
131
198
  />
132
199
  ```
133
200
 
134
- ### useSignalEffect()
135
-
136
- Hook for subscribing to signal changes with automatic cleanup:
137
-
138
- ```typescript
139
- useSignalEffect(() => {
140
- const value = signal.get();
141
- // React re-renders when signal changes
142
- });
143
- ```
144
-
145
- **Behavior:**
146
-
147
- - Tracks signal dependencies automatically via `Signal.Computed` wrapper
148
- - Uses `Signal.subtle.Watcher` with microtask batching
149
- - Triggers React state update to force re-render
150
- - Cleans up watcher on unmount with explicit `unwatch`
151
-
152
- **Canonical watcher lifecycle:**
153
-
154
- 1. `notify`
155
- 2. `queueMicrotask`
156
- 3. drain pending work (`getPending` and/or `Computed.get`)
157
- 4. run effect + trigger render
158
- 5. re-arm watcher via `watch()`
201
+ **`store`** (optional) — Controls per-view UI state (form values, `$state` bindings):
159
202
 
160
- Watcher notification is one-shot, so re-arm and explicit cleanup are both required.
203
+ - **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.
204
+ - **Provided (controlled):** The caller owns the store lifecycle. `spec.state` is ignored.
161
205
 
162
- **Example:**
163
-
164
- ```typescript
165
- import { useSignalEffect } from "@xmachines/play-react";
166
- import { useState } from "react";
167
-
168
- function RouteDisplay({ actor }: { actor: AbstractActor<any> }) {
169
- const [route, setRoute] = useState<string | null>(null);
206
+ ```tsx
207
+ import { createAtom } from "@xstate/store";
208
+ import { xstateStoreStateStore } from "@json-render/xstate";
209
+ import type { StateStore } from "@json-render/core";
170
210
 
171
- useSignalEffect(() => {
172
- const currentRoute = actor.currentRoute.get();
173
- setRoute(currentRoute);
174
- });
211
+ const store: StateStore = xstateStoreStateStore({ atom: createAtom({ username: "alice" }) });
175
212
 
176
- return <div>Current Route: {route ?? "None"}</div>;
177
- }
213
+ <PlayRenderer actor={actor} registry={registry} store={store} actions={{ login: "auth.login" }} />;
178
214
  ```
179
215
 
180
- ### PlayErrorBoundary
181
-
182
- React class component error boundary for catching catalog component render errors.
183
-
184
- `PlayRenderer` wraps its render output in `PlayErrorBoundary` automatically. You can also use it directly to wrap any component that may throw during render.
185
-
186
- ```typescript
187
- interface PlayErrorBoundaryProps {
188
- fallback?: React.ReactNode; // UI shown when a child throws (default: null)
189
- children: React.ReactNode;
190
- onError?: (error: Error, info: React.ErrorInfo) => void; // Forward to Sentry, Datadog, etc.
191
- }
192
- ```
216
+ **`fallback`** — Shown when `actor.currentView.get()` is `null` (machine in a no-view state, or during initialisation).
193
217
 
194
- **Props:**
218
+ ---
195
219
 
196
- - `fallback` — ReactNode rendered when a child component throws. Defaults to `null`.
197
- - `onError` — Optional callback forwarded on every caught error. Use for production observability (Sentry, Datadog, custom logging).
220
+ ### `useActor`
198
221
 
199
- **Example:**
222
+ Hook for accessing the actor from inside any component rendered by `PlayRenderer`. No prop drilling needed.
200
223
 
201
224
  ```tsx
202
- import { PlayErrorBoundary } from "@xmachines/play-react";
225
+ import { useActor } from "@xmachines/play-react";
203
226
 
204
- <PlayErrorBoundary
205
- fallback={<div className="error">Something went wrong.</div>}
206
- onError={(error) => Sentry.captureException(error)}
207
- >
208
- <YourCatalogComponent />
209
- </PlayErrorBoundary>;
227
+ // Works in any component rendered inside PlayRenderer:
228
+ function LogoutButton() {
229
+ const actor = useActor();
230
+ return <button onClick={() => actor.send({ type: "auth.logout" })}>Log Out</button>;
231
+ }
210
232
  ```
211
233
 
212
- Works with React 18 and React 19. Uses the standard class component `componentDidCatch` + `getDerivedStateFromError` pattern.
234
+ Throws `"useActor() must be called inside <PlayRenderer>"` if called outside the tree.
213
235
 
214
- ## Examples
236
+ ---
215
237
 
216
- ### Component Receiving Props from Catalog
238
+ ### `useSignalEffect`
217
239
 
218
- ```typescript
219
- import { PlayRenderer } from "@xmachines/play-react";
220
- import { defineCatalog } from "@xmachines/play-catalog";
221
- import { z } from "zod";
222
-
223
- // Define schema in catalog
224
- const catalog = defineCatalog({
225
- UserProfile: z.object({
226
- userId: z.string(),
227
- name: z.string(),
228
- avatar: z.string().url().optional(),
229
- stats: z.object({
230
- posts: z.number(),
231
- followers: z.number(),
232
- }),
233
- }),
234
- });
240
+ Hook for subscribing to TC39 Signals in React components that live outside a `PlayRenderer` tree (e.g. nav bars, status indicators driven by actor state).
235
241
 
236
- // Component receives type-safe props + send
237
- const components = {
238
- UserProfile: ({ userId, name, avatar, stats, send }) => (
239
- <div>
240
- {avatar && <img src={avatar} alt={name} />}
241
- <h1>{name}</h1>
242
- <p>ID: {userId}</p>
243
- <div>
244
- <span>{stats.posts} posts</span>
245
- <span>{stats.followers} followers</span>
246
- </div>
247
- <button
248
- onClick={() =>
249
- send({
250
- type: "profile.edit",
251
- userId,
252
- })
253
- }
254
- >
255
- Edit Profile
256
- </button>
257
- </div>
258
- ),
259
- };
260
-
261
- <PlayRenderer actor={actor} components={components} />;
262
- ```
263
-
264
- ### useSignalEffect for Custom Rendering
265
-
266
- ```typescript
242
+ ```tsx
267
243
  import { useSignalEffect } from "@xmachines/play-react";
268
- import { AbstractActor } from "@xmachines/play-actor";
269
244
 
270
- function CustomRenderer({ actor }: { actor: AbstractActor<any> }) {
271
- const [view, setView] = useState(null);
245
+ function NavBar({ actor }: { actor: ReturnType<typeof createPlayer> }) {
246
+ const [isAuth, setIsAuth] = useState(false);
272
247
 
273
- // Subscribe to currentView signal
274
248
  useSignalEffect(() => {
275
- const currentView = actor.currentView.get();
276
- setView(currentView);
249
+ const snap = actor.state.get();
250
+ setIsAuth((snap.context as { isAuthenticated: boolean }).isAuthenticated);
277
251
  });
278
252
 
279
- if (!view) return <div>No view</div>;
280
-
281
- // Custom rendering logic
282
- if (view.component === "SpecialCase") {
283
- return <SpecialCaseComponent {...view.props} actor={actor} />;
284
- }
285
-
286
- // Fallback to standard rendering
287
- return <DefaultComponent view={view} actor={actor} />;
253
+ return <nav>{isAuth ? <LogoutBtn /> : <LoginBtn />}</nav>;
288
254
  }
289
255
  ```
290
256
 
291
- ### Provider Pattern
257
+ ---
292
258
 
293
- ```typescript
294
- import { PlayTanStackRouterProvider } from "@xmachines/play-tanstack-react-router";
295
- import { PlayRenderer } from "@xmachines/play-react";
259
+ ### `PlayErrorBoundary`
296
260
 
297
- // Renderer receives actor via props (not children)
298
- function App() {
299
- return (
300
- <PlayTanStackRouterProvider
301
- actor={actor}
302
- router={router}
303
- renderer={(currentActor, currentRouter) => {
304
- void currentRouter;
305
- return (
306
- <div>
307
- <Header actor={currentActor} />
308
- <PlayRenderer actor={currentActor} components={components} />
309
- <Footer />
310
- </div>
311
- );
312
- }}
313
- />
314
- );
315
- }
261
+ Class error boundary that wraps the rendered output. Catches errors thrown during component render and logs them without crashing the full page. `PlayRenderer` wraps its own output in this boundary automatically.
316
262
 
317
- // Header component also receives actor
318
- function Header({ actor }: { actor: AbstractActor<any> }) {
319
- const [route, setRoute] = useState<string | null>(null);
320
-
321
- useSignalEffect(() => {
322
- setRoute(actor.currentRoute.get());
323
- });
263
+ ```tsx
264
+ import { PlayErrorBoundary } from "@xmachines/play-react";
324
265
 
325
- return (
326
- <header>
327
- <nav>Current: {route}</nav>
328
- </header>
329
- );
330
- }
266
+ <PlayErrorBoundary fallback={<p>Something went wrong.</p>}>
267
+ <PlayRenderer actor={actor} registry={registry} />
268
+ </PlayErrorBoundary>;
331
269
  ```
332
270
 
333
- ## Architecture
334
-
335
- This package implements **Signal-Only Reactivity (INV-05)** and **Passive Infrastructure (INV-04)**:
336
-
337
- 1. **No Business Logic in React:**
338
- - No useState/useReducer for business state
339
- - No useEffect for side effects
340
- - React only triggers renders, doesn't control state
341
-
342
- 2. **Signals as Source of Truth:**
343
- - `actor.currentView.get()` provides UI structure
344
- - `actor.currentRoute.get()` provides navigation state
345
- - Components observe signals via `useSignalEffect`
271
+ ---
346
272
 
347
- 3. **Event Forwarding:**
348
- - Components receive `send` function via props
349
- - User actions send events to actor (e.g., `{ type: "auth.login" }`)
350
- - Actor guards validate and process events
273
+ ## Route Parameters in Props
351
274
 
352
- 4. **Microtask Batching:**
353
- - `Signal.subtle.Watcher` coalesces rapid signal changes
354
- - Prevents React thrashing from multiple signal updates
355
- - Single React render per microtask batch
275
+ When using `formatPlayRouteTransitions`, URL path parameters flow automatically into component props. Declare an `undefined` slot in the spec to opt in:
356
276
 
357
- 5. **Explicit Disposal Contract:**
358
- - Component teardown must call watcher `unwatch` in cleanup
359
- - Do not rely on GC-only cleanup
360
-
361
- **Pattern:**
362
-
363
- - Renderer receives actor via props (provider pattern)
364
- - Enables composition with navigation, headers, footers
365
- - Supports multiple renderers in same app
366
-
367
- **Architectural Invariants:**
368
-
369
- - **Signal-Only Reactivity (INV-05):** No React state for business logic
370
- - **Passive Infrastructure (INV-04):** Components reflect, never decide
277
+ ```ts
278
+ // spec: { section: undefined, user: "alice" }
279
+ // After play.route to /settings/profile context.routeParams = { section: "profile" }
280
+ // Component receives: { section: "profile", user: "alice" }
281
+ ```
371
282
 
372
- ## Benefits
283
+ Priority: **route param fills `undefined` slots; explicit non-`undefined` spec props always win.**
373
284
 
374
- - **Framework Swappable:** Business logic has zero React imports
375
- - **Type Safety:** Props validated against catalog schemas
376
- - **Simple Testing:** Test actors without React renderer
377
- - **Performance:** Microtask batching reduces unnecessary renders
378
- - **Composability:** Renderer prop enables complex layouts ()
285
+ ---
379
286
 
380
- ## Related Packages
287
+ ## Error Handling
381
288
 
382
- - **[@xmachines/play-xstate](../play-xstate)** - XState adapter providing actors
383
- - **[@xmachines/play-catalog](../play-catalog)** - UI schema validation
384
- - **[@xmachines/play-tanstack-react-router](../play-tanstack-react-router)** - TanStack Router integration
385
- - **[@xmachines/play-actor](../play-actor)** - Actor base
386
- - **[@xmachines/play-signals](../play-signals)** - TC39 Signals primitives
289
+ | Error | Cause | Fix |
290
+ | ------------------------------------------------- | ---------------------------- | ---------------------------------------------------------------- |
291
+ | `useActor() must be called inside <PlayRenderer>` | Hook called outside the tree | Move inside a component rendered by `PlayRenderer` |
292
+ | Component render error | Component throws | `PlayErrorBoundary` catches it; inspect component implementation |
387
293
 
388
- ## License
294
+ ---
389
295
 
390
- Copyright (c) 2016 [Mikael Karon](mailto:mikael@karon.se). All rights reserved.
296
+ ## Architecture Notes
391
297
 
392
- This work is licensed under the terms of the MIT license.
393
- For a copy, see <https://opensource.org/licenses/MIT>.
298
+ - React `useState` is **only** used to trigger re-renders never for business logic
299
+ - `actor.currentView` (TC39 Signal) is the sole render trigger; `PlayRenderer` is a passive observer
300
+ - Per-view UI state lives in an `@xstate/store` atom, not in React state
301
+ - `@json-render/react` drives rendering; `PlayRenderer` is the signal bridge — import `defineRegistry`, `ComponentFn`, `ComponentContext`, and `useStateBinding` from `@xmachines/play-react`
@@ -1 +1 @@
1
- {"version":3,"file":"PlayErrorBoundary.d.ts","sourceRoot":"","sources":["../src/PlayErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,iCAAiC;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,8FAA8F;IAC9F,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC;CACxD;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,iBAAkB,SAAQ,KAAK,CAAC,SAAS,CACrD,sBAAsB,EACtB,sBAAsB,CACtB;gBACY,KAAK,EAAE,sBAAsB;IAKzC,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,sBAAsB;IAI5D,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI;IAK5D,MAAM,IAAI,KAAK,CAAC,SAAS;CAMlC"}
1
+ {"version":3,"file":"PlayErrorBoundary.d.ts","sourceRoot":"","sources":["../src/PlayErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,iCAAiC;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,8FAA8F;IAC9F,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC;CACxD;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,iBAAkB,SAAQ,KAAK,CAAC,SAAS,CACrD,sBAAsB,EACtB,sBAAsB,CACtB;gBACY,KAAK,EAAE,sBAAsB;IAKzC,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,sBAAsB;IAI5D,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI;IAO5D,MAAM,IAAI,KAAK,CAAC,SAAS;CAMlC"}
@@ -4,6 +4,7 @@
4
4
  * @packageDocumentation
5
5
  */
6
6
  import React from "react";
7
+ import { RendererError } from "./errors.js";
7
8
  /**
8
9
  * React class component error boundary for catching catalog component render errors.
9
10
  *
@@ -29,8 +30,10 @@ export class PlayErrorBoundary extends React.Component {
29
30
  return { hasError: true, error };
30
31
  }
31
32
  componentDidCatch(error, info) {
32
- console.error("[PlayErrorBoundary] Component render error:", error, info);
33
33
  this.props.onError?.(error, info);
34
+ throw new RendererError("Component render error caught by PlayErrorBoundary.", {
35
+ cause: error,
36
+ });
34
37
  }
35
38
  render() {
36
39
  if (this.state.hasError) {
@@ -1 +1 @@
1
- {"version":3,"file":"PlayErrorBoundary.js","sourceRoot":"","sources":["../src/PlayErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AA0B1B;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK,CAAC,SAG5C;IACA,YAAY,KAA6B;QACxC,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,IAAI,CAAC,KAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC/C,CAAC;IAED,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC3C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAClC,CAAC;IAEQ,iBAAiB,CAAC,KAAY,EAAE,IAAqB;QAC7D,OAAO,CAAC,KAAK,CAAC,6CAA6C,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC1E,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAEQ,MAAM;QACd,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC;QACpC,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC5B,CAAC;CACD"}
1
+ {"version":3,"file":"PlayErrorBoundary.js","sourceRoot":"","sources":["../src/PlayErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA0B5C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK,CAAC,SAG5C;IACA,YAAY,KAA6B;QACxC,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,IAAI,CAAC,KAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC/C,CAAC;IAED,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC3C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAClC,CAAC;IAEQ,iBAAiB,CAAC,KAAY,EAAE,IAAqB;QAC7D,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAClC,MAAM,IAAI,aAAa,CAAC,qDAAqD,EAAE;YAC9E,KAAK,EAAE,KAAK;SACZ,CAAC,CAAC;IACJ,CAAC;IAEQ,MAAM;QACd,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC;QACpC,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC5B,CAAC;CACD"}
@@ -1,18 +1,24 @@
1
1
  /**
2
2
  * PlayRenderer - Main React renderer component for XMachines Play architecture
3
3
  *
4
+ * Backed by @json-render/react for spec-driven UI rendering.
5
+ *
4
6
  * @packageDocumentation
5
7
  */
6
8
  import React from "react";
7
9
  import type { PlayRendererProps } from "./types.js";
8
10
  /**
9
11
  * Main renderer component that subscribes to actor signals and renders UI
12
+ * via @json-render/react Renderer.
10
13
  *
11
- * Architecture (per RESEARCH.md Pattern 1):
14
+ * Architecture:
12
15
  * - Subscribes to actor.currentView signal via useSignalEffect
13
- * - Dynamically renders catalog components based on view.component string
14
- * - Forwards user events to actor via actor.send()
15
- * - React state only for triggering renders, NOT business logic
16
+ * - Renders view.spec via StateProvider ActionProvider VisibilityProvider → Renderer
17
+ * - Routes action names to actor.send() via the `actions` prop mapping
18
+ * - State store: uses external `store` prop if provided (controlled mode); otherwise
19
+ * creates a fresh @xstate/store atom per view transition seeded from spec.state.
20
+ * The atom resets automatically when the actor transitions to a new view, mirroring
21
+ * the actor's currentView lifecycle.
16
22
  *
17
23
  * Invariant: Actor Authority - Actor decides all state transitions via guards.
18
24
  * Invariant: Passive Infrastructure - Component observes signals and sends events.
@@ -21,37 +27,22 @@ import type { PlayRendererProps } from "./types.js";
21
27
  * @example
22
28
  * ```typescript
23
29
  * import { PlayRenderer } from "@xmachines/play-react";
24
- * import { definePlayer } from "@xmachines/play-xstate";
30
+ * import { defineRegistry } from "@json-render/react";
25
31
  *
26
- * const actor = definePlayer({ machine, catalog })();
27
- * actor.start();
32
+ * const { registry } = defineRegistry(catalog, { components: { ... } });
28
33
  *
29
- * const components = {
30
- * Dashboard: ({ userId, send }) => <div>User: {userId}</div>,
31
- * LoginForm: ({ error, send }) => <form onSubmit={(e) => {
32
- * e.preventDefault();
33
- * send({ type: "intent", name: "login.submit", payload: {...} });
34
- * }}>...</form>
35
- * };
34
+ * // Uncontrolled fresh atom created per view, seeded from spec.state:
35
+ * <PlayRenderer actor={actor} registry={registry} actions={{ login: "auth.login" }} />
36
36
  *
37
- * <PlayRenderer actor={actor} components={components} />
37
+ * // Controlled caller provides and owns the store:
38
+ * import { createAtom } from "@xstate/store";
39
+ * import { xstateStoreStateStore } from "@json-render/xstate";
40
+ * const store = xstateStoreStateStore({ atom: createAtom({ username: "" }) });
41
+ * <PlayRenderer actor={actor} registry={registry} store={store} actions={{ login: "auth.login" }} />
38
42
  * ```
39
43
  *
40
44
  * @param props - Component props
41
45
  * @returns React element rendering current view from actor
42
- *
43
- * @remarks
44
- * **Component lookup:** Dynamically looks up component from `components` map
45
- * using `view.component` string from actor.currentView signal.
46
- *
47
- * **Event forwarding:** Injects `send` function as prop to components. Components
48
- * call `send(event)` to forward intents to actor. Actor guards decide validity.
49
- *
50
- * **Error handling:** If component not found in catalog, logs error and shows
51
- * fallback. This indicates missing component registration, not runtime error.
52
- *
53
- * **CRITICAL:** Never call actor.send() during render - only in event handlers.
54
- * Calling send during render causes infinite render loops.
55
46
  */
56
47
  export declare const PlayRenderer: React.FC<PlayRendererProps>;
57
48
  //# sourceMappingURL=PlayRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PlayRenderer.d.ts","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA+CpD,CAAC"}
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,CAoEpD,CAAC"}
@@ -2,19 +2,36 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  /**
3
3
  * PlayRenderer - Main React renderer component for XMachines Play architecture
4
4
  *
5
+ * Backed by @json-render/react for spec-driven UI rendering.
6
+ *
5
7
  * @packageDocumentation
6
8
  */
7
- import React, { useState } from "react";
9
+ import React, { useState, useRef } from "react";
10
+ import { Renderer, StateProvider, ActionProvider, VisibilityProvider } from "@json-render/react";
11
+ import { createAtom } from "@xstate/store";
12
+ import { xstateStoreStateStore } from "@json-render/xstate";
8
13
  import { useSignalEffect } from "./useSignalEffect.js";
9
14
  import { PlayErrorBoundary } from "./PlayErrorBoundary.js";
15
+ import { ActorContext } from "./useActor.js";
16
+ /**
17
+ * Create a StateStore backed by a fresh @xstate/store atom seeded from the given state.
18
+ * Called internally per view transition when no external store prop is provided.
19
+ */
20
+ function createViewStore(initialState) {
21
+ return xstateStoreStateStore({ atom: createAtom(initialState) });
22
+ }
10
23
  /**
11
24
  * Main renderer component that subscribes to actor signals and renders UI
25
+ * via @json-render/react Renderer.
12
26
  *
13
- * Architecture (per RESEARCH.md Pattern 1):
27
+ * Architecture:
14
28
  * - Subscribes to actor.currentView signal via useSignalEffect
15
- * - Dynamically renders catalog components based on view.component string
16
- * - Forwards user events to actor via actor.send()
17
- * - React state only for triggering renders, NOT business logic
29
+ * - Renders view.spec via StateProvider ActionProvider VisibilityProvider → Renderer
30
+ * - Routes action names to actor.send() via the `actions` prop mapping
31
+ * - State store: uses external `store` prop if provided (controlled mode); otherwise
32
+ * creates a fresh @xstate/store atom per view transition seeded from spec.state.
33
+ * The atom resets automatically when the actor transitions to a new view, mirroring
34
+ * the actor's currentView lifecycle.
18
35
  *
19
36
  * Invariant: Actor Authority - Actor decides all state transitions via guards.
20
37
  * Invariant: Passive Infrastructure - Component observes signals and sends events.
@@ -23,66 +40,64 @@ import { PlayErrorBoundary } from "./PlayErrorBoundary.js";
23
40
  * @example
24
41
  * ```typescript
25
42
  * import { PlayRenderer } from "@xmachines/play-react";
26
- * import { definePlayer } from "@xmachines/play-xstate";
43
+ * import { defineRegistry } from "@json-render/react";
27
44
  *
28
- * const actor = definePlayer({ machine, catalog })();
29
- * actor.start();
45
+ * const { registry } = defineRegistry(catalog, { components: { ... } });
30
46
  *
31
- * const components = {
32
- * Dashboard: ({ userId, send }) => <div>User: {userId}</div>,
33
- * LoginForm: ({ error, send }) => <form onSubmit={(e) => {
34
- * e.preventDefault();
35
- * send({ type: "intent", name: "login.submit", payload: {...} });
36
- * }}>...</form>
37
- * };
47
+ * // Uncontrolled fresh atom created per view, seeded from spec.state:
48
+ * <PlayRenderer actor={actor} registry={registry} actions={{ login: "auth.login" }} />
38
49
  *
39
- * <PlayRenderer actor={actor} components={components} />
50
+ * // Controlled caller provides and owns the store:
51
+ * import { createAtom } from "@xstate/store";
52
+ * import { xstateStoreStateStore } from "@json-render/xstate";
53
+ * const store = xstateStoreStateStore({ atom: createAtom({ username: "" }) });
54
+ * <PlayRenderer actor={actor} registry={registry} store={store} actions={{ login: "auth.login" }} />
40
55
  * ```
41
56
  *
42
57
  * @param props - Component props
43
58
  * @returns React element rendering current view from actor
44
- *
45
- * @remarks
46
- * **Component lookup:** Dynamically looks up component from `components` map
47
- * using `view.component` string from actor.currentView signal.
48
- *
49
- * **Event forwarding:** Injects `send` function as prop to components. Components
50
- * call `send(event)` to forward intents to actor. Actor guards decide validity.
51
- *
52
- * **Error handling:** If component not found in catalog, logs error and shows
53
- * fallback. This indicates missing component registration, not runtime error.
54
- *
55
- * **CRITICAL:** Never call actor.send() during render - only in event handlers.
56
- * Calling send during render causes infinite render loops.
57
59
  */
58
- export const PlayRenderer = ({ actor, components, fallback = null, }) => {
60
+ export const PlayRenderer = ({ actor, registry, store: externalStore, fallback = null, actions = {}, }) => {
59
61
  // React state for triggering re-renders (NOT business logic state)
60
62
  // Signal is source of truth, useState is just React's render trigger
61
63
  const [view, setView] = useState(() => actor.currentView.get());
64
+ // Internal store ref — tracks the current per-view atom store.
65
+ // Keyed to view identity: recreated whenever the view changes (new spec.state seed).
66
+ // Ignored when externalStore is provided.
67
+ const internalStoreRef = useRef(null);
68
+ const lastViewRef = useRef(null);
62
69
  // Subscribe to signal changes
63
70
  useSignalEffect(() => {
64
71
  const currentView = actor.currentView.get();
65
72
  setView(currentView);
66
73
  });
67
- // No view in current state
74
+ // No view in current state — render fallback
68
75
  if (!view) {
69
76
  return _jsx(_Fragment, { children: fallback });
70
77
  }
71
- // Handle null/undefined components catalog gracefully
72
- if (!components) {
73
- console.error(`Components catalog is ${components === null ? "null" : "undefined"}. ` +
74
- `Cannot render component "${view.component}".`);
75
- return _jsx(_Fragment, { children: fallback });
78
+ // Resolve the store to use for StateProvider:
79
+ // - External (controlled): use as-is, caller manages lifecycle
80
+ // - Internal: create a fresh atom when the view changes (new route/state)
81
+ let store;
82
+ if (externalStore) {
83
+ store = externalStore;
76
84
  }
77
- // Look up component from catalog
78
- const Component = components[view.component];
79
- if (!Component) {
80
- console.error(`Component "${view.component}" not found in catalog. ` +
81
- `Available components: ${Object.keys(components).join(", ")}`);
82
- return _jsx(_Fragment, { children: fallback });
85
+ else {
86
+ // Recreate the internal store when the view identity changes
87
+ // (view is a new object on every transition per deriveCurrentView)
88
+ if (internalStoreRef.current === null || lastViewRef.current !== view) {
89
+ const initialState = view.spec?.state ?? {};
90
+ internalStoreRef.current = createViewStore(initialState);
91
+ lastViewRef.current = view;
92
+ }
93
+ store = internalStoreRef.current;
83
94
  }
84
- // Render with props from actor + send function
85
- // bind(actor) ensures 'this' context is correct when components call send()
86
- return (_jsx(PlayErrorBoundary, { fallback: fallback, children: _jsx(Component, { ...view.props, send: actor.send.bind(actor) }) }));
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, children: _jsx(StateProvider, { store: store, children: _jsx(ActionProvider, { handlers: handlers, children: _jsx(VisibilityProvider, { children: _jsx(Renderer, { spec: view.spec ?? null, registry: registry }) }) }) }) }) }));
87
102
  };
88
103
  //# sourceMappingURL=PlayRenderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PlayRenderer.js","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":";AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAI3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAM,CAAC,MAAM,YAAY,GAAgC,CAAC,EACzD,KAAK,EACL,UAAU,EACV,QAAQ,GAAG,IAAI,GACf,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,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,2BAA2B;IAC3B,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,OAAO,4BAAG,QAAQ,GAAI,CAAC;IACxB,CAAC;IAED,sDAAsD;IACtD,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,CACZ,yBAAyB,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,IAAI;YACtE,4BAA4B,IAAI,CAAC,SAAS,IAAI,CAC/C,CAAC;QACF,OAAO,4BAAG,QAAQ,GAAI,CAAC;IACxB,CAAC;IAED,iCAAiC;IACjC,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAE7C,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CACZ,cAAc,IAAI,CAAC,SAAS,0BAA0B;YACrD,yBAAyB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9D,CAAC;QACF,OAAO,4BAAG,QAAQ,GAAI,CAAC;IACxB,CAAC;IAED,+CAA+C;IAC/C,4EAA4E;IAC5E,OAAO,CACN,KAAC,iBAAiB,IAAC,QAAQ,EAAE,QAAQ,YACpC,KAAC,SAAS,OAAK,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAI,GACxC,CACpB,CAAC;AACH,CAAC,CAAC"}
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,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEjG,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;AAG3D,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,CAAC,MAAM,YAAY,GAAgC,CAAC,EACzD,KAAK,EACL,QAAQ,EACR,KAAK,EAAE,aAAa,EACpB,QAAQ,GAAG,IAAI,EACf,OAAO,GAAG,EAAE,GACZ,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,qDAAqD;IACrD,0EAA0E;IAC1E,MAAM,QAAQ,GAAkC,MAAM,CAAC,WAAW,CACjE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC;QACxD,UAAU;QACV,KAAK,EAAE,SAAkC,EAAE,EAAE,EAAE,CAC9C,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAAC;KAC3C,CAAC,CACF,CAAC;IAEF,OAAO,CACN,KAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAkB,YAC/C,KAAC,iBAAiB,IAAC,QAAQ,EAAE,QAAQ,YACpC,KAAC,aAAa,IAAC,KAAK,EAAE,KAAK,YAC1B,KAAC,cAAc,IAAC,QAAQ,EAAE,QAAQ,YACjC,KAAC,kBAAkB,cAClB,KAAC,QAAQ,IAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE,QAAQ,EAAE,QAAQ,GAAI,GACrC,GACL,GACF,GACG,GACG,CACxB,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { PlayError } from "@xmachines/play";
2
+ /**
3
+ * Thrown by `PlayErrorBoundary.componentDidCatch()` when a React component inside
4
+ * the boundary throws during rendering.
5
+ *
6
+ * The original render error is set as `cause`. The `onError` prop callback fires
7
+ * before this error is thrown, allowing observability tools to record the incident.
8
+ *
9
+ * **Error code:** `PLAY_RENDERER_RENDER_ERROR`
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { RendererError } from "@xmachines/play-react/errors";
14
+ *
15
+ * // In a parent error boundary or global handler:
16
+ * if (err instanceof RendererError) {
17
+ * // err.cause is the original component error
18
+ * reportRenderError(err.cause as Error);
19
+ * }
20
+ * ```
21
+ */
22
+ export declare class RendererError extends PlayError {
23
+ constructor(message: string, options?: ErrorOptions);
24
+ }
25
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,aAAc,SAAQ,SAAS;gBAC/B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAInD"}
package/dist/errors.js ADDED
@@ -0,0 +1,28 @@
1
+ import { PlayError } from "@xmachines/play";
2
+ /**
3
+ * Thrown by `PlayErrorBoundary.componentDidCatch()` when a React component inside
4
+ * the boundary throws during rendering.
5
+ *
6
+ * The original render error is set as `cause`. The `onError` prop callback fires
7
+ * before this error is thrown, allowing observability tools to record the incident.
8
+ *
9
+ * **Error code:** `PLAY_RENDERER_RENDER_ERROR`
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { RendererError } from "@xmachines/play-react/errors";
14
+ *
15
+ * // In a parent error boundary or global handler:
16
+ * if (err instanceof RendererError) {
17
+ * // err.cause is the original component error
18
+ * reportRenderError(err.cause as Error);
19
+ * }
20
+ * ```
21
+ */
22
+ export class RendererError extends PlayError {
23
+ constructor(message, options) {
24
+ super("PlayRenderer", "PLAY_RENDERER_RENDER_ERROR", message, options);
25
+ this.name = "RendererError";
26
+ }
27
+ }
28
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,aAAc,SAAQ,SAAS;IAC3C,YAAY,OAAe,EAAE,OAAsB;QAClD,KAAK,CAAC,cAAc,EAAE,4BAA4B,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC7B,CAAC;CACD"}
package/dist/index.d.ts CHANGED
@@ -2,19 +2,27 @@
2
2
  * @xmachines/play-react - React renderer for XMachines Play architecture
3
3
  *
4
4
  * Provides a thin React rendering layer that passively observes actor signals
5
- * and renders UI components from catalog definitions. This package enables
5
+ * and renders UI components via @json-render/react. This package enables
6
6
  * framework-swappable architecture where React is just a rendering target
7
7
  * that subscribes to signal changes.
8
8
  *
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
13
+ * `ComponentContext` from `@json-render/react` so consumers import everything
14
+ * from `@xmachines/play-react` rather than `@json-render/react` directly.
15
+ *
12
16
  * @packageDocumentation
13
17
  * @module @xmachines/play-react
14
18
  */
15
19
  export { PlayRenderer } from "./PlayRenderer.js";
16
20
  export { useSignalEffect } from "./useSignalEffect.js";
17
21
  export { PlayErrorBoundary } from "./PlayErrorBoundary.js";
22
+ export { useActor } from "./useActor.js";
23
+ export { defineRegistry, useStateBinding, useBoundProp } from "@json-render/react";
24
+ export type { ComponentFn, ComponentContext } from "@json-render/react";
18
25
  export type { PlayRendererProps } from "./types.js";
19
26
  export type { PlayErrorBoundaryProps, PlayErrorBoundaryState } from "./PlayErrorBoundary.js";
27
+ export type { PlayActor } from "./useActor.js";
20
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAG3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,YAAY,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,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,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"}
package/dist/index.js CHANGED
@@ -2,13 +2,17 @@
2
2
  * @xmachines/play-react - React renderer for XMachines Play architecture
3
3
  *
4
4
  * Provides a thin React rendering layer that passively observes actor signals
5
- * and renders UI components from catalog definitions. This package enables
5
+ * and renders UI components via @json-render/react. This package enables
6
6
  * framework-swappable architecture where React is just a rendering target
7
7
  * that subscribes to signal changes.
8
8
  *
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
13
+ * `ComponentContext` from `@json-render/react` so consumers import everything
14
+ * from `@xmachines/play-react` rather than `@json-render/react` directly.
15
+ *
12
16
  * @packageDocumentation
13
17
  * @module @xmachines/play-react
14
18
  */
@@ -16,4 +20,8 @@
16
20
  export { PlayRenderer } from "./PlayRenderer.js";
17
21
  export { useSignalEffect } from "./useSignalEffect.js";
18
22
  export { PlayErrorBoundary } from "./PlayErrorBoundary.js";
23
+ export { useActor } from "./useActor.js";
24
+ // Re-export from @json-render/react so consumers import everything from @xmachines/play-react.
25
+ // React's useContext works anywhere in the call tree — no wrapper needed.
26
+ export { defineRegistry, useStateBinding, useBoundProp } from "@json-render/react";
19
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;;;;;;;;;;;;;GAaG;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"}
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"}
package/dist/types.d.ts CHANGED
@@ -3,22 +3,52 @@
3
3
  *
4
4
  * @packageDocumentation
5
5
  */
6
+ import type { ComponentRegistry } from "@json-render/react";
7
+ import type { StateStore } from "@json-render/core";
6
8
  import type { AbstractActor, Viewable } from "@xmachines/play-actor";
7
9
  import type React from "react";
8
- import type { AnyActorLogic } from "xstate";
10
+ import type { AnyActorLogic, EventFromLogic } from "xstate";
9
11
  /**
10
12
  * Props for PlayRenderer component
11
13
  *
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.
17
+ *
12
18
  * @property actor - Actor instance with currentView signal (requires Viewable capability)
13
- * @property components - Map of component names to React components
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.
14
24
  * @property fallback - Optional component shown when currentView is null
25
+ * @property actions - Maps json-render action names to XState event type strings.
26
+ * Values are constrained to `EventFromLogic<TLogic>["type"]` — passing a non-existent
27
+ * event type string is a compile error when TLogic is specified.
15
28
  */
16
- export interface PlayRendererProps {
29
+ export interface PlayRendererProps<TLogic extends AnyActorLogic = AnyActorLogic> {
17
30
  /** Actor instance with currentView signal (requires Viewable capability) */
18
- actor: AbstractActor<AnyActorLogic> & Viewable;
19
- /** Map of component names to React components */
20
- components: Record<string, React.ElementType>;
31
+ actor: AbstractActor<TLogic> & Viewable;
32
+ /** ComponentRegistry from defineRegistry() in @json-render/react */
33
+ registry: ComponentRegistry;
34
+ /**
35
+ * Optional external StateStore (e.g. from `xstateStoreStateStore` in @json-render/xstate).
36
+ * When provided, PlayRenderer operates in controlled mode — spec.state is ignored and
37
+ * this store is the single source of truth for UI state (form values, etc.).
38
+ * When omitted, a fresh @xstate/store atom is created internally per view transition,
39
+ * seeded from spec.state. The atom resets automatically when the actor transitions to a
40
+ * new view, mirroring the actor's currentView lifecycle.
41
+ */
42
+ store?: StateStore;
21
43
  /** Optional component shown when currentView is null */
22
44
  fallback?: React.ReactNode;
45
+ /**
46
+ * Maps json-render action names to XState event type strings.
47
+ * Values are constrained to valid event types for TLogic — wrong event type strings
48
+ * are caught at compile time when TLogic is specified.
49
+ */
50
+ actions?: {
51
+ [actionName: string]: EventFromLogic<TLogic>["type"];
52
+ };
23
53
  }
24
54
  //# 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,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;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IACjC,4EAA4E;IAC5E,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC;IAE/C,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAE9C,wDAAwD;IACxD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B"}
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;;;;;;;;;;;;;;;;;GAiBG;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,wDAAwD;IACxD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B;;;;OAIG;IACH,OAAO,CAAC,EAAE;QAAE,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;KAAE,CAAC;CACnE"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * useActor — React hook for accessing the raw actor inside a PlayRenderer tree.
3
+ *
4
+ * Components rendered inside PlayRenderer can call useActor() to get direct
5
+ * access to the actor instance without prop drilling.
6
+ *
7
+ * @throws {Error} If called outside a PlayRenderer tree
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { useActor } from "@xmachines/play-react";
12
+ *
13
+ * function MyComponent() {
14
+ * const actor = useActor();
15
+ * return <button onClick={() => actor.send({ type: "SUBMIT" })}>Submit</button>;
16
+ * }
17
+ * ```
18
+ *
19
+ * @packageDocumentation
20
+ */
21
+ import type { AbstractActor } from "@xmachines/play-actor";
22
+ import type { AnyActorLogic } from "xstate";
23
+ export type PlayActor = AbstractActor<AnyActorLogic>;
24
+ export declare const ActorContext: import("react").Context<PlayActor | null>;
25
+ export declare function useActor(): PlayActor;
26
+ //# sourceMappingURL=useActor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActor.d.ts","sourceRoot":"","sources":["../src/useActor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C,MAAM,MAAM,SAAS,GAAG,aAAa,CAAC,aAAa,CAAC,CAAC;AAErD,eAAO,MAAM,YAAY,2CAAwC,CAAC;AAElE,wBAAgB,QAAQ,IAAI,SAAS,CAIpC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * useActor — React hook for accessing the raw actor inside a PlayRenderer tree.
3
+ *
4
+ * Components rendered inside PlayRenderer can call useActor() to get direct
5
+ * access to the actor instance without prop drilling.
6
+ *
7
+ * @throws {Error} If called outside a PlayRenderer tree
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { useActor } from "@xmachines/play-react";
12
+ *
13
+ * function MyComponent() {
14
+ * const actor = useActor();
15
+ * return <button onClick={() => actor.send({ type: "SUBMIT" })}>Submit</button>;
16
+ * }
17
+ * ```
18
+ *
19
+ * @packageDocumentation
20
+ */
21
+ import { createContext, useContext } from "react";
22
+ export const ActorContext = createContext(null);
23
+ export function useActor() {
24
+ const actor = useContext(ActorContext);
25
+ if (!actor)
26
+ throw new Error("useActor() must be called inside <PlayRenderer>");
27
+ return actor;
28
+ }
29
+ //# sourceMappingURL=useActor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActor.js","sourceRoot":"","sources":["../src/useActor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAMlD,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAmB,IAAI,CAAC,CAAC;AAElE,MAAM,UAAU,QAAQ;IACvB,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IACvC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IAC/E,OAAO,KAAK,CAAC;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmachines/play-react",
3
- "version": "1.0.0-beta.16",
3
+ "version": "1.0.0-beta.18",
4
4
  "description": "React renderer for XMachines Play architecture with signal-driven rendering",
5
5
  "keywords": [
6
6
  "actor",
@@ -37,6 +37,7 @@
37
37
  "build": "tsc --build",
38
38
  "clean": "rm -rf dist *.tsbuildinfo node_modules/.vite node_modules/.vite-temp",
39
39
  "typecheck": "tsc --noEmit",
40
+ "typecheck:test": "tsc --noEmit -p tsconfig.test.json",
40
41
  "typecheck:browser": "tsc --noEmit -p tsconfig.browser.json",
41
42
  "test": "vitest",
42
43
  "test:vitest": "vitest run",
@@ -44,17 +45,20 @@
44
45
  "prepublishOnly": "npm run build"
45
46
  },
46
47
  "dependencies": {
47
- "@xmachines/play-actor": "1.0.0-beta.16",
48
- "@xmachines/play-catalog": "1.0.0-beta.16",
49
- "@xmachines/play-signals": "1.0.0-beta.16"
48
+ "@xmachines/play-actor": "1.0.0-beta.18",
49
+ "@xmachines/play-signals": "1.0.0-beta.18"
50
50
  },
51
51
  "devDependencies": {
52
+ "@json-render/core": "^0.16.0",
53
+ "@json-render/react": "^0.16.0",
54
+ "@json-render/xstate": "^0.16.0",
52
55
  "@testing-library/jest-dom": "^6.9.1",
53
56
  "@testing-library/react": "^16.3.2",
54
57
  "@types/node": "^25.5.0",
55
58
  "@types/react": "^19.2.14",
56
59
  "@types/react-dom": "^19.2.3",
57
- "@xmachines/shared": "1.0.0-beta.16",
60
+ "@xmachines/shared": "1.0.0-beta.18",
61
+ "@xstate/store": ">=3.17.0",
58
62
  "jsdom": "^29.0.1",
59
63
  "react": "^19.2.4",
60
64
  "react-dom": "^19.2.4",
@@ -62,6 +66,10 @@
62
66
  "vitest": "^4.1.2"
63
67
  },
64
68
  "peerDependencies": {
69
+ "@json-render/core": "^0.16.0",
70
+ "@json-render/react": "^0.16.0",
71
+ "@json-render/xstate": "^0.16.0",
72
+ "@xstate/store": ">=3.17.0",
65
73
  "react": "^18.0.0 || ^19.0.0",
66
74
  "react-dom": "^18.0.0 || ^19.0.0"
67
75
  }