@xmachines/docs 1.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -0
  3. package/api/@xmachines/play/README.md +130 -0
  4. package/api/@xmachines/play/type-aliases/PlayEvent.md +81 -0
  5. package/api/@xmachines/play-actor/README.md +247 -0
  6. package/api/@xmachines/play-actor/classes/AbstractActor.md +520 -0
  7. package/api/@xmachines/play-actor/interfaces/Routable.md +29 -0
  8. package/api/@xmachines/play-actor/interfaces/ViewMetadata.md +17 -0
  9. package/api/@xmachines/play-actor/interfaces/Viewable.md +12 -0
  10. package/api/@xmachines/play-catalog/README.md +331 -0
  11. package/api/@xmachines/play-catalog/functions/defineCatalog.md +98 -0
  12. package/api/@xmachines/play-catalog/functions/defineComponents.md +134 -0
  13. package/api/@xmachines/play-catalog/type-aliases/Catalog.md +48 -0
  14. package/api/@xmachines/play-catalog/type-aliases/ComponentsFor.md +20 -0
  15. package/api/@xmachines/play-catalog/type-aliases/InferComponentProps.md +65 -0
  16. package/api/@xmachines/play-catalog/type-aliases/NoExtraKeys.md +17 -0
  17. package/api/@xmachines/play-react/README.md +423 -0
  18. package/api/@xmachines/play-react/classes/PlayErrorBoundary.md +613 -0
  19. package/api/@xmachines/play-react/functions/useSignalEffect.md +68 -0
  20. package/api/@xmachines/play-react/interfaces/PlayErrorBoundaryProps.md +15 -0
  21. package/api/@xmachines/play-react/interfaces/PlayErrorBoundaryState.md +14 -0
  22. package/api/@xmachines/play-react/interfaces/PlayRendererProps.md +15 -0
  23. package/api/@xmachines/play-react/variables/PlayRenderer.md +64 -0
  24. package/api/@xmachines/play-react-router/README.md +198 -0
  25. package/api/@xmachines/play-react-router/classes/ReactRouterBridge.md +321 -0
  26. package/api/@xmachines/play-react-router/classes/RouteMap.md +137 -0
  27. package/api/@xmachines/play-react-router/functions/PlayRouterProvider.md +19 -0
  28. package/api/@xmachines/play-react-router/functions/createRouteMapFromTree.md +35 -0
  29. package/api/@xmachines/play-react-router/interfaces/PlayRouteEvent.md +119 -0
  30. package/api/@xmachines/play-react-router/interfaces/PlayRouterProviderProps.md +14 -0
  31. package/api/@xmachines/play-react-router/interfaces/RouteMapping.md +17 -0
  32. package/api/@xmachines/play-react-router/interfaces/RouterBridge.md +104 -0
  33. package/api/@xmachines/play-react-router-demo/README.md +137 -0
  34. package/api/@xmachines/play-router/README.md +502 -0
  35. package/api/@xmachines/play-router/classes/BaseRouteMap.md +142 -0
  36. package/api/@xmachines/play-router/classes/RouterBridgeBase.md +300 -0
  37. package/api/@xmachines/play-router/functions/buildRouteTree.md +27 -0
  38. package/api/@xmachines/play-router/functions/connectRouter.md +67 -0
  39. package/api/@xmachines/play-router/functions/crawlMachine.md +92 -0
  40. package/api/@xmachines/play-router/functions/createBrowserHistory.md +47 -0
  41. package/api/@xmachines/play-router/functions/createRouteMap.md +53 -0
  42. package/api/@xmachines/play-router/functions/createRouter.md +76 -0
  43. package/api/@xmachines/play-router/functions/detectDuplicateRoutes.md +32 -0
  44. package/api/@xmachines/play-router/functions/extractMachineRoutes.md +64 -0
  45. package/api/@xmachines/play-router/functions/extractRoute.md +45 -0
  46. package/api/@xmachines/play-router/functions/findRouteById.md +37 -0
  47. package/api/@xmachines/play-router/functions/findRouteByPath.md +39 -0
  48. package/api/@xmachines/play-router/functions/getNavigableRoutes.md +35 -0
  49. package/api/@xmachines/play-router/functions/getRoutableRoutes.md +39 -0
  50. package/api/@xmachines/play-router/functions/routeExists.md +26 -0
  51. package/api/@xmachines/play-router/functions/validateRouteFormat.md +29 -0
  52. package/api/@xmachines/play-router/functions/validateStateExists.md +29 -0
  53. package/api/@xmachines/play-router/interfaces/BaseRouteMapping.md +27 -0
  54. package/api/@xmachines/play-router/interfaces/BrowserHistory.md +172 -0
  55. package/api/@xmachines/play-router/interfaces/BrowserWindow.md +69 -0
  56. package/api/@xmachines/play-router/interfaces/ConnectRouterOptions.md +13 -0
  57. package/api/@xmachines/play-router/interfaces/PlayRouteEvent.md +119 -0
  58. package/api/@xmachines/play-router/interfaces/RouteInfo.md +19 -0
  59. package/api/@xmachines/play-router/interfaces/RouteMap.md +56 -0
  60. package/api/@xmachines/play-router/interfaces/RouteNode.md +21 -0
  61. package/api/@xmachines/play-router/interfaces/RouteObject.md +21 -0
  62. package/api/@xmachines/play-router/interfaces/RouteTree.md +20 -0
  63. package/api/@xmachines/play-router/interfaces/RouterBridge.md +104 -0
  64. package/api/@xmachines/play-router/interfaces/StateVisit.md +15 -0
  65. package/api/@xmachines/play-router/interfaces/VanillaRouter.md +28 -0
  66. package/api/@xmachines/play-router/type-aliases/RouteMetadata.md +11 -0
  67. package/api/@xmachines/play-router-demo/README.md +137 -0
  68. package/api/@xmachines/play-signals/README.md +176 -0
  69. package/api/@xmachines/play-signals/interfaces/ComputedOptions.md +34 -0
  70. package/api/@xmachines/play-signals/interfaces/SignalComputed.md +49 -0
  71. package/api/@xmachines/play-signals/interfaces/SignalOptions.md +35 -0
  72. package/api/@xmachines/play-signals/interfaces/SignalState.md +68 -0
  73. package/api/@xmachines/play-signals/interfaces/SignalWatcher.md +97 -0
  74. package/api/@xmachines/play-signals/namespaces/Signal/README.md +22 -0
  75. package/api/@xmachines/play-signals/namespaces/Signal/classes/Computed.md +52 -0
  76. package/api/@xmachines/play-signals/namespaces/Signal/classes/State.md +72 -0
  77. package/api/@xmachines/play-signals/namespaces/Signal/interfaces/Options.md +19 -0
  78. package/api/@xmachines/play-signals/namespaces/Signal/namespaces/subtle/README.md +21 -0
  79. package/api/@xmachines/play-signals/namespaces/Signal/namespaces/subtle/classes/Watcher.md +85 -0
  80. package/api/@xmachines/play-signals/namespaces/Signal/namespaces/subtle/functions/currentComputed.md +13 -0
  81. package/api/@xmachines/play-signals/namespaces/Signal/namespaces/subtle/functions/hasSinks.md +19 -0
  82. package/api/@xmachines/play-signals/namespaces/Signal/namespaces/subtle/functions/hasSources.md +19 -0
  83. package/api/@xmachines/play-signals/namespaces/Signal/namespaces/subtle/functions/introspectSinks.md +19 -0
  84. package/api/@xmachines/play-signals/namespaces/Signal/namespaces/subtle/functions/introspectSources.md +19 -0
  85. package/api/@xmachines/play-signals/namespaces/Signal/namespaces/subtle/functions/untrack.md +25 -0
  86. package/api/@xmachines/play-signals/namespaces/Signal/namespaces/subtle/variables/unwatched.md +9 -0
  87. package/api/@xmachines/play-signals/namespaces/Signal/namespaces/subtle/variables/watched.md +9 -0
  88. package/api/@xmachines/play-signals/namespaces/Signal/variables/isComputed.md +19 -0
  89. package/api/@xmachines/play-signals/namespaces/Signal/variables/isState.md +19 -0
  90. package/api/@xmachines/play-signals/namespaces/Signal/variables/isWatcher.md +19 -0
  91. package/api/@xmachines/play-signals/type-aliases/WatcherNotify.md +32 -0
  92. package/api/@xmachines/play-solid/README.md +311 -0
  93. package/api/@xmachines/play-solid/interfaces/PlayRendererProps.md +15 -0
  94. package/api/@xmachines/play-solid/variables/PlayRenderer.md +70 -0
  95. package/api/@xmachines/play-solid-router/README.md +666 -0
  96. package/api/@xmachines/play-solid-router/classes/RouteMap.md +150 -0
  97. package/api/@xmachines/play-solid-router/classes/SolidRouterBridge.md +347 -0
  98. package/api/@xmachines/play-solid-router/functions/PlayRouterProvider.md +19 -0
  99. package/api/@xmachines/play-solid-router/functions/createRouteMap.md +32 -0
  100. package/api/@xmachines/play-solid-router/interfaces/AbstractActor.md +486 -0
  101. package/api/@xmachines/play-solid-router/interfaces/PlayRouteEvent.md +119 -0
  102. package/api/@xmachines/play-solid-router/interfaces/PlayRouterProviderProps.md +14 -0
  103. package/api/@xmachines/play-solid-router/interfaces/RouteMapping.md +14 -0
  104. package/api/@xmachines/play-solid-router/interfaces/RouterBridge.md +104 -0
  105. package/api/@xmachines/play-solid-router/type-aliases/RoutableActor.md +9 -0
  106. package/api/@xmachines/play-solid-router/type-aliases/SolidRouterHooks.md +51 -0
  107. package/api/@xmachines/play-solid-router-demo/README.md +127 -0
  108. package/api/@xmachines/play-tanstack-react-router/README.md +226 -0
  109. package/api/@xmachines/play-tanstack-react-router/classes/RouteMap.md +137 -0
  110. package/api/@xmachines/play-tanstack-react-router/classes/TanStackReactRouterBridge.md +348 -0
  111. package/api/@xmachines/play-tanstack-react-router/functions/PlayRouterProvider.md +19 -0
  112. package/api/@xmachines/play-tanstack-react-router/functions/createRouteMap.md +53 -0
  113. package/api/@xmachines/play-tanstack-react-router/functions/createRouteMapFromTree.md +35 -0
  114. package/api/@xmachines/play-tanstack-react-router/functions/extractParams.md +38 -0
  115. package/api/@xmachines/play-tanstack-react-router/functions/extractQueryParams.md +33 -0
  116. package/api/@xmachines/play-tanstack-react-router/interfaces/PlayRouteEvent.md +119 -0
  117. package/api/@xmachines/play-tanstack-react-router/interfaces/PlayRouterProviderProps.md +14 -0
  118. package/api/@xmachines/play-tanstack-react-router/interfaces/RouteMapping.md +17 -0
  119. package/api/@xmachines/play-tanstack-react-router/interfaces/RouteNavigateEvent.md +26 -0
  120. package/api/@xmachines/play-tanstack-react-router/interfaces/RouterBridge.md +104 -0
  121. package/api/@xmachines/play-tanstack-react-router/type-aliases/TanStackRouterInstance.md +9 -0
  122. package/api/@xmachines/play-tanstack-react-router/type-aliases/TanStackRouterLike.md +78 -0
  123. package/api/@xmachines/play-tanstack-react-router/variables/extractMachineRoutes.md +64 -0
  124. package/api/@xmachines/play-tanstack-react-router-demo/README.md +126 -0
  125. package/api/@xmachines/play-tanstack-solid-router/README.md +285 -0
  126. package/api/@xmachines/play-tanstack-solid-router/classes/RouteMap.md +150 -0
  127. package/api/@xmachines/play-tanstack-solid-router/classes/SolidRouterBridge.md +343 -0
  128. package/api/@xmachines/play-tanstack-solid-router/functions/PlayRouterProvider.md +19 -0
  129. package/api/@xmachines/play-tanstack-solid-router/functions/createRouteMap.md +32 -0
  130. package/api/@xmachines/play-tanstack-solid-router/interfaces/PlayRouteEvent.md +119 -0
  131. package/api/@xmachines/play-tanstack-solid-router/interfaces/PlayRouterProviderProps.md +14 -0
  132. package/api/@xmachines/play-tanstack-solid-router/interfaces/RouteMapping.md +23 -0
  133. package/api/@xmachines/play-tanstack-solid-router/interfaces/RouterBridge.md +104 -0
  134. package/api/@xmachines/play-tanstack-solid-router/type-aliases/RoutableActor.md +9 -0
  135. package/api/@xmachines/play-tanstack-solid-router/type-aliases/TanStackRouterInstance.md +9 -0
  136. package/api/@xmachines/play-tanstack-solid-router/type-aliases/TanStackRouterLike.md +78 -0
  137. package/api/@xmachines/play-tanstack-solid-router-demo/README.md +126 -0
  138. package/api/@xmachines/play-vue/README.md +292 -0
  139. package/api/@xmachines/play-vue/interfaces/PlayRendererProps.md +14 -0
  140. package/api/@xmachines/play-vue/variables/PlayRenderer.md +9 -0
  141. package/api/@xmachines/play-vue-router/README.md +604 -0
  142. package/api/@xmachines/play-vue-router/classes/RouteMap.md +209 -0
  143. package/api/@xmachines/play-vue-router/classes/VueBaseRouteMap.md +201 -0
  144. package/api/@xmachines/play-vue-router/classes/VueRouterBridge.md +360 -0
  145. package/api/@xmachines/play-vue-router/functions/createRouteMap.md +19 -0
  146. package/api/@xmachines/play-vue-router/interfaces/PlayRouteEvent.md +119 -0
  147. package/api/@xmachines/play-vue-router/interfaces/RouteMapping.md +15 -0
  148. package/api/@xmachines/play-vue-router/interfaces/RouterBridge.md +104 -0
  149. package/api/@xmachines/play-vue-router/type-aliases/RoutableActor.md +9 -0
  150. package/api/@xmachines/play-vue-router/variables/PlayRouterProvider.md +67 -0
  151. package/api/@xmachines/play-vue-router-demo/README.md +133 -0
  152. package/api/@xmachines/play-xstate/README.md +512 -0
  153. package/api/@xmachines/play-xstate/classes/PlayerActor.md +527 -0
  154. package/api/@xmachines/play-xstate/functions/buildRouteUrl.md +43 -0
  155. package/api/@xmachines/play-xstate/functions/composeGuards.md +79 -0
  156. package/api/@xmachines/play-xstate/functions/composeGuardsOr.md +67 -0
  157. package/api/@xmachines/play-xstate/functions/definePlayer.md +127 -0
  158. package/api/@xmachines/play-xstate/functions/deriveRoute.md +109 -0
  159. package/api/@xmachines/play-xstate/functions/eventMatches.md +40 -0
  160. package/api/@xmachines/play-xstate/functions/formatPlayRouteTransitions.md +54 -0
  161. package/api/@xmachines/play-xstate/functions/hasContext.md +42 -0
  162. package/api/@xmachines/play-xstate/functions/isAbsoluteRoute.md +41 -0
  163. package/api/@xmachines/play-xstate/functions/mergeViewProps.md +26 -0
  164. package/api/@xmachines/play-xstate/functions/negateGuard.md +61 -0
  165. package/api/@xmachines/play-xstate/functions/stateMatches.md +25 -0
  166. package/api/@xmachines/play-xstate/functions/validateComponentBinding.md +39 -0
  167. package/api/@xmachines/play-xstate/functions/validateViewProps.md +80 -0
  168. package/api/@xmachines/play-xstate/interfaces/CatalogEntry.md +16 -0
  169. package/api/@xmachines/play-xstate/interfaces/PlayerConfig.md +24 -0
  170. package/api/@xmachines/play-xstate/interfaces/PlayerOptions.md +26 -0
  171. package/api/@xmachines/play-xstate/interfaces/RouteContext.md +22 -0
  172. package/api/@xmachines/play-xstate/type-aliases/Catalog.md +21 -0
  173. package/api/@xmachines/play-xstate/type-aliases/ComposedGuard.md +14 -0
  174. package/api/@xmachines/play-xstate/type-aliases/Guard.md +34 -0
  175. package/api/@xmachines/play-xstate/type-aliases/GuardArray.md +20 -0
  176. package/api/@xmachines/play-xstate/type-aliases/PlayerFactory.md +29 -0
  177. package/api/@xmachines/play-xstate/type-aliases/RouteMachineConfig.md +45 -0
  178. package/api/@xmachines/play-xstate/type-aliases/RouteStateNode.md +51 -0
  179. package/api/@xmachines/play-xstate/type-aliases/ValidationResult.md +17 -0
  180. package/api/@xmachines/play-xstate/type-aliases/ViewMergeContext.md +35 -0
  181. package/api/@xmachines/shared/README.md +379 -0
  182. package/api/@xmachines/shared/functions/defineXmVitestConfig.md +29 -0
  183. package/api/@xmachines/shared/functions/xmAliases.md +24 -0
  184. package/api/README.md +25 -0
  185. package/api/llms.txt +26 -0
  186. package/examples/README.md +63 -0
  187. package/examples/basic-state-machine.md +70 -0
  188. package/examples/form-validation.md +167 -0
  189. package/examples/multi-router-integration.md +277 -0
  190. package/examples/routing-patterns.md +260 -0
  191. package/examples/traffic-light.md +99 -0
  192. package/guides/README.md +29 -0
  193. package/guides/getting-started.md +223 -0
  194. package/guides/installation.md +323 -0
  195. package/index.d.ts +3 -0
  196. package/index.js +4 -0
  197. package/package.json +54 -0
@@ -0,0 +1,17 @@
1
+ [Documentation](../../../README.md) / [@xmachines/play-catalog](../README.md) / NoExtraKeys
2
+
3
+ # Type Alias: NoExtraKeys\<TComponents, TCatalog\>
4
+
5
+ ```ts
6
+ type NoExtraKeys<TComponents, TCatalog> = TComponents &
7
+ Record<Exclude<keyof TComponents, keyof TCatalog>, never>;
8
+ ```
9
+
10
+ Defined in: [define-components.ts:14](https://gitlab.com/xmachin-es/xmachines-js/-/blob/00a28432ed57807112288436d1ae4387d9f06919/packages/play-catalog/src/define-components.ts#L14)
11
+
12
+ ## Type Parameters
13
+
14
+ | Type Parameter |
15
+ | ------------------------------------------------------ |
16
+ | `TComponents` |
17
+ | `TCatalog` _extends_ `Record`\<`string`, `z.ZodType`\> |
@@ -0,0 +1,423 @@
1
+ [Documentation](../../README.md) / @xmachines/play-react
2
+
3
+ # @xmachines/play-react
4
+
5
+ **React renderer consuming signals and UI schema with provider pattern**
6
+
7
+ Signal-driven React rendering layer observing actor state with zero React state for business logic.
8
+
9
+ ## Overview
10
+
11
+ `@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.
12
+
13
+ Per [RFC Play v1](https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md), this package implements:
14
+
15
+ - **Signal-Only Reactivity (INV-05):** No useState/useReducer for business logic, signals only
16
+ - **Passive Infrastructure (INV-04):** Components observe signals, send events to actor
17
+
18
+ **Key Principle:** React state is never used for business logic. Signals are the source of truth.
19
+
20
+ Renderer receives actor via props (provider pattern), not children.
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install react@^18.0.0 react-dom@^18.0.0
26
+ npm install @xmachines/play-react
27
+ ```
28
+
29
+ ## Current Exports
30
+
31
+ - `PlayRenderer`
32
+ - `useSignalEffect`
33
+ - `PlayErrorBoundary`
34
+ - `PlayRendererProps` (type)
35
+ - `PlayErrorBoundaryProps` (type)
36
+
37
+ **Peer dependencies:**
38
+
39
+ - `react` ^18.0.0 || ^19.0.0 — React runtime
40
+ - `react-dom` ^18.0.0 || ^19.0.0 — React DOM renderer
41
+
42
+ ## Quick Start
43
+
44
+ ```typescript
45
+ import { createRoot } from "react-dom/client";
46
+ import { definePlayer } from "@xmachines/play-xstate";
47
+ import { defineCatalog } from "@xmachines/play-catalog";
48
+ import { PlayRenderer } from "@xmachines/play-react";
49
+ import { z } from "zod";
50
+
51
+ // 1. Define catalog (business logic layer)
52
+ const catalog = defineCatalog({
53
+ LoginForm: z.object({ error: z.string().optional() }),
54
+ Dashboard: z.object({
55
+ userId: z.string(),
56
+ username: z.string(),
57
+ }),
58
+ });
59
+
60
+ // 2. Create React components (view layer)
61
+ const components = {
62
+ LoginForm: ({ error, send }) => (
63
+ <form
64
+ onSubmit={(e) => {
65
+ e.preventDefault();
66
+ const data = new FormData(e.currentTarget);
67
+ send({
68
+ type: "auth.login",
69
+ username: data.get("username"),
70
+ });
71
+ }}
72
+ >
73
+ {error && <p style={{ color: "red" }}>{error}</p>}
74
+ <input name="username" required placeholder="Username" />
75
+ <button type="submit">Log In</button>
76
+ </form>
77
+ ),
78
+ Dashboard: ({ userId, username, send }) => (
79
+ <div>
80
+ <h1>Welcome, {username}!</h1>
81
+ <p>User ID: {userId}</p>
82
+ <button onClick={() => send({ type: "auth.logout" })}>Log Out</button>
83
+ </div>
84
+ ),
85
+ };
86
+
87
+ // 3. Create player actor (business logic runtime)
88
+ const createPlayer = definePlayer({ machine: authMachine, catalog });
89
+ const actor = createPlayer();
90
+ actor.start();
91
+
92
+ // 4. Render UI (actor via props)
93
+ const root = createRoot(document.getElementById("app")!);
94
+ root.render(<PlayRenderer actor={actor} components={components} />);
95
+ ```
96
+
97
+ ## API Reference
98
+
99
+ ### PlayRenderer
100
+
101
+ Main renderer component subscribing to actor signals and dynamically rendering catalog components:
102
+
103
+ ```typescript
104
+ interface PlayRendererProps {
105
+ actor: AbstractActor<AnyActorLogic> & Viewable;
106
+ components: Record<string, React.ElementType>;
107
+ fallback?: React.ReactNode;
108
+ }
109
+ ```
110
+
111
+ **Props:**
112
+
113
+ - `actor` - Actor instance with `currentView` signal
114
+ - `components` - Map of component names to React components
115
+ - `fallback` - Component shown when `currentView` is null (default: `null`)
116
+
117
+ **Behavior:**
118
+
119
+ 1. Subscribes to `actor.currentView` signal via `useSignalEffect`
120
+ 2. Looks up component from `components` map using `view.component` string
121
+ 3. Renders component with props from `view.props` + `send` function
122
+
123
+ **Example:**
124
+
125
+ ```typescript
126
+ <PlayRenderer
127
+ actor={actor}
128
+ components={{
129
+ HomePage: ({ send }) => <div>Home</div>,
130
+ AboutPage: ({ send }) => <div>About</div>,
131
+ }}
132
+ fallback={<div>Loading...</div>}
133
+ />
134
+ ```
135
+
136
+ ### useSignalEffect()
137
+
138
+ Hook for subscribing to signal changes with automatic cleanup:
139
+
140
+ ```typescript
141
+ useSignalEffect(() => {
142
+ const value = signal.get();
143
+ // React re-renders when signal changes
144
+ });
145
+ ```
146
+
147
+ **Behavior:**
148
+
149
+ - Tracks signal dependencies automatically via `Signal.Computed` wrapper
150
+ - Uses `Signal.subtle.Watcher` with microtask batching
151
+ - Triggers React state update to force re-render
152
+ - Cleans up watcher on unmount with explicit `unwatch`
153
+
154
+ **Canonical watcher lifecycle:**
155
+
156
+ 1. `notify`
157
+ 2. `queueMicrotask`
158
+ 3. drain pending work (`getPending` and/or `Computed.get`)
159
+ 4. run effect + trigger render
160
+ 5. re-arm watcher via `watch()`
161
+
162
+ Watcher notification is one-shot, so re-arm and explicit cleanup are both required.
163
+
164
+ **Example:**
165
+
166
+ ```typescript
167
+ import { useSignalEffect } from "@xmachines/play-react";
168
+ import { useState } from "react";
169
+
170
+ function RouteDisplay({ actor }: { actor: AbstractActor<any> }) {
171
+ const [route, setRoute] = useState<string | null>(null);
172
+
173
+ useSignalEffect(() => {
174
+ const currentRoute = actor.currentRoute.get();
175
+ setRoute(currentRoute);
176
+ });
177
+
178
+ return <div>Current Route: {route ?? "None"}</div>;
179
+ }
180
+ ```
181
+
182
+ ### PlayErrorBoundary
183
+
184
+ React class component error boundary for catching catalog component render errors.
185
+
186
+ `PlayRenderer` wraps its render output in `PlayErrorBoundary` automatically. You can also use it directly to wrap any component that may throw during render.
187
+
188
+ ```typescript
189
+ interface PlayErrorBoundaryProps {
190
+ fallback?: React.ReactNode; // UI shown when a child throws (default: null)
191
+ children: React.ReactNode;
192
+ onError?: (error: Error, info: React.ErrorInfo) => void; // Forward to Sentry, Datadog, etc.
193
+ }
194
+ ```
195
+
196
+ **Props:**
197
+
198
+ - `fallback` — ReactNode rendered when a child component throws. Defaults to `null`.
199
+ - `onError` — Optional callback forwarded on every caught error. Use for production observability (Sentry, Datadog, custom logging).
200
+
201
+ **Example:**
202
+
203
+ ```tsx
204
+ import { PlayErrorBoundary } from "@xmachines/play-react";
205
+
206
+ <PlayErrorBoundary
207
+ fallback={<div className="error">Something went wrong.</div>}
208
+ onError={(error) => Sentry.captureException(error)}
209
+ >
210
+ <YourCatalogComponent />
211
+ </PlayErrorBoundary>;
212
+ ```
213
+
214
+ Works with React 18 and React 19. Uses the standard class component `componentDidCatch` + `getDerivedStateFromError` pattern.
215
+
216
+ ## Examples
217
+
218
+ ### Component Receiving Props from Catalog
219
+
220
+ ```typescript
221
+ import { PlayRenderer } from "@xmachines/play-react";
222
+ import { defineCatalog } from "@xmachines/play-catalog";
223
+ import { z } from "zod";
224
+
225
+ // Define schema in catalog
226
+ const catalog = defineCatalog({
227
+ UserProfile: z.object({
228
+ userId: z.string(),
229
+ name: z.string(),
230
+ avatar: z.string().url().optional(),
231
+ stats: z.object({
232
+ posts: z.number(),
233
+ followers: z.number(),
234
+ }),
235
+ }),
236
+ });
237
+
238
+ // Component receives type-safe props + send
239
+ const components = {
240
+ UserProfile: ({ userId, name, avatar, stats, send }) => (
241
+ <div>
242
+ {avatar && <img src={avatar} alt={name} />}
243
+ <h1>{name}</h1>
244
+ <p>ID: {userId}</p>
245
+ <div>
246
+ <span>{stats.posts} posts</span>
247
+ <span>{stats.followers} followers</span>
248
+ </div>
249
+ <button
250
+ onClick={() =>
251
+ send({
252
+ type: "profile.edit",
253
+ userId,
254
+ })
255
+ }
256
+ >
257
+ Edit Profile
258
+ </button>
259
+ </div>
260
+ ),
261
+ };
262
+
263
+ <PlayRenderer actor={actor} components={components} />;
264
+ ```
265
+
266
+ ### useSignalEffect for Custom Rendering
267
+
268
+ ```typescript
269
+ import { useSignalEffect } from "@xmachines/play-react";
270
+ import { AbstractActor } from "@xmachines/play-actor";
271
+
272
+ function CustomRenderer({ actor }: { actor: AbstractActor<any> }) {
273
+ const [view, setView] = useState(null);
274
+
275
+ // Subscribe to currentView signal
276
+ useSignalEffect(() => {
277
+ const currentView = actor.currentView.get();
278
+ setView(currentView);
279
+ });
280
+
281
+ if (!view) return <div>No view</div>;
282
+
283
+ // Custom rendering logic
284
+ if (view.component === "SpecialCase") {
285
+ return <SpecialCaseComponent {...view.props} actor={actor} />;
286
+ }
287
+
288
+ // Fallback to standard rendering
289
+ return <DefaultComponent view={view} actor={actor} />;
290
+ }
291
+ ```
292
+
293
+ ### Provider Pattern
294
+
295
+ ```typescript
296
+ import { PlayTanStackRouterProvider } from "@xmachines/play-tanstack-react-router";
297
+ import { PlayRenderer } from "@xmachines/play-react";
298
+
299
+ // Renderer receives actor via props (not children)
300
+ function App() {
301
+ return (
302
+ <PlayTanStackRouterProvider
303
+ actor={actor}
304
+ router={router}
305
+ renderer={(currentActor, currentRouter) => {
306
+ void currentRouter;
307
+ return (
308
+ <div>
309
+ <Header actor={currentActor} />
310
+ <PlayRenderer actor={currentActor} components={components} />
311
+ <Footer />
312
+ </div>
313
+ );
314
+ }}
315
+ />
316
+ );
317
+ }
318
+
319
+ // Header component also receives actor
320
+ function Header({ actor }: { actor: AbstractActor<any> }) {
321
+ const [route, setRoute] = useState<string | null>(null);
322
+
323
+ useSignalEffect(() => {
324
+ setRoute(actor.currentRoute.get());
325
+ });
326
+
327
+ return (
328
+ <header>
329
+ <nav>Current: {route}</nav>
330
+ </header>
331
+ );
332
+ }
333
+ ```
334
+
335
+ ## Architecture
336
+
337
+ This package implements **Signal-Only Reactivity (INV-05)** and **Passive Infrastructure (INV-04)**:
338
+
339
+ 1. **No Business Logic in React:**
340
+ - No useState/useReducer for business state
341
+ - No useEffect for side effects
342
+ - React only triggers renders, doesn't control state
343
+
344
+ 2. **Signals as Source of Truth:**
345
+ - `actor.currentView.get()` provides UI structure
346
+ - `actor.currentRoute.get()` provides navigation state
347
+ - Components observe signals via `useSignalEffect`
348
+
349
+ 3. **Event Forwarding:**
350
+ - Components receive `send` function via props
351
+ - User actions send events to actor (e.g., `{ type: "auth.login" }`)
352
+ - Actor guards validate and process events
353
+
354
+ 4. **Microtask Batching:**
355
+ - `Signal.subtle.Watcher` coalesces rapid signal changes
356
+ - Prevents React thrashing from multiple signal updates
357
+ - Single React render per microtask batch
358
+
359
+ 5. **Explicit Disposal Contract:**
360
+ - Component teardown must call watcher `unwatch` in cleanup
361
+ - Do not rely on GC-only cleanup
362
+
363
+ **Pattern:**
364
+
365
+ - Renderer receives actor via props (provider pattern)
366
+ - Enables composition with navigation, headers, footers
367
+ - Supports multiple renderers in same app
368
+
369
+ **Architectural Invariants:**
370
+
371
+ - **Signal-Only Reactivity (INV-05):** No React state for business logic
372
+ - **Passive Infrastructure (INV-04):** Components reflect, never decide
373
+
374
+ ## Benefits
375
+
376
+ - **Framework Swappable:** Business logic has zero React imports
377
+ - **Type Safety:** Props validated against catalog schemas
378
+ - **Simple Testing:** Test actors without React renderer
379
+ - **Performance:** Microtask batching reduces unnecessary renders
380
+ - **Composability:** Renderer prop enables complex layouts ()
381
+
382
+ ## Related Packages
383
+
384
+ - **[@xmachines/play-xstate](../play-xstate/README.md)** - XState adapter providing actors
385
+ - **[@xmachines/play-catalog](../play-catalog/README.md)** - UI schema validation
386
+ - **[@xmachines/play-tanstack-react-router](../play-tanstack-react-router/README.md)** - TanStack Router integration
387
+ - **[@xmachines/play-actor](../play-actor/README.md)** - Actor base
388
+ - **[@xmachines/play-signals](../play-signals/README.md)** - TC39 Signals primitives
389
+
390
+ ## License
391
+
392
+ Copyright (c) 2016 [Mikael Karon](mailto:mikael@karon.se). All rights reserved.
393
+
394
+ This work is licensed under the terms of the MIT license.
395
+ For a copy, see <https://opensource.org/licenses/MIT>.
396
+
397
+ @xmachines/play-react - React renderer for XMachines Play architecture
398
+
399
+ Provides a thin React rendering layer that passively observes actor signals
400
+ and renders UI components from catalog definitions. This package enables
401
+ framework-swappable architecture where React is just a rendering target
402
+ that subscribes to signal changes.
403
+
404
+ **Key principle:** React state is NEVER used for business logic—only for
405
+ triggering React's render cycle. Signals are the source of truth.
406
+
407
+ ## Classes
408
+
409
+ - [PlayErrorBoundary](classes/PlayErrorBoundary.md)
410
+
411
+ ## Interfaces
412
+
413
+ - [PlayErrorBoundaryProps](interfaces/PlayErrorBoundaryProps.md)
414
+ - [PlayErrorBoundaryState](interfaces/PlayErrorBoundaryState.md)
415
+ - [PlayRendererProps](interfaces/PlayRendererProps.md)
416
+
417
+ ## Variables
418
+
419
+ - [PlayRenderer](variables/PlayRenderer.md)
420
+
421
+ ## Functions
422
+
423
+ - [useSignalEffect](functions/useSignalEffect.md)