@xmachines/play-react 1.0.0-beta.3 → 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.
@@ -0,0 +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;;;;;;;;;;;;;;;;;;;GAmBG;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;IAI5D,MAAM,IAAI,KAAK,CAAC,SAAS;CAMlC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * PlayErrorBoundary - React error boundary for catching catalog component render errors
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import React from "react";
7
+ /**
8
+ * React class component error boundary for catching catalog component render errors.
9
+ *
10
+ * Wraps catalog component renders so failures are caught and forwarded to standard
11
+ * React error boundary protocol. Consumers can attach the `onError` prop to forward
12
+ * errors to production observability tools (Sentry, Datadog, etc.).
13
+ *
14
+ * **React 19 safety (Phase 29):** `componentDidCatch` calls `onError` for observability
15
+ * but does NOT re-throw. `getDerivedStateFromError` already sets the fallback state —
16
+ * re-throwing from `componentDidCatch` can unmount the entire React 19 root.
17
+ *
18
+ * Per CONS-14: Class component pattern works with all React versions (18 and 19).
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <PlayErrorBoundary fallback={<div>Something went wrong</div>} onError={Sentry.captureException}>
23
+ * <CatalogComponent {...props} />
24
+ * </PlayErrorBoundary>
25
+ * ```
26
+ */
27
+ export class PlayErrorBoundary extends React.Component {
28
+ constructor(props) {
29
+ super(props);
30
+ this.state = { hasError: false, error: null };
31
+ }
32
+ static getDerivedStateFromError(error) {
33
+ return { hasError: true, error };
34
+ }
35
+ componentDidCatch(error, info) {
36
+ this.props.onError?.(error, info);
37
+ }
38
+ render() {
39
+ if (this.state.hasError) {
40
+ return this.props.fallback ?? null;
41
+ }
42
+ return this.props.children;
43
+ }
44
+ }
45
+ //# sourceMappingURL=PlayErrorBoundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlayErrorBoundary.js","sourceRoot":"","sources":["../src/PlayErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AA0B1B;;;;;;;;;;;;;;;;;;;GAmBG;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;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,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 actions via registryResult.handlers() real async functions dispatching to actor.
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,29 @@ 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";
25
- *
26
- * const actor = definePlayer({ machine, catalog })();
27
- * actor.start();
28
- *
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
- * };
36
- *
37
- * <PlayRenderer actor={actor} components={components} />
30
+ * import { defineRegistry } from "@json-render/react";
31
+ *
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
+ * });
40
+ *
41
+ * // Uncontrolled — fresh atom created per view, seeded from spec.state:
42
+ * <PlayRenderer actor={actor} registryResult={registryResult} />
43
+ *
44
+ * // Controlled — caller provides and owns the store:
45
+ * import { createAtom } from "@xstate/store";
46
+ * import { xstateStoreStateStore } from "@json-render/xstate";
47
+ * const store = xstateStoreStateStore({ atom: createAtom({ username: "" }) });
48
+ * <PlayRenderer actor={actor} registryResult={registryResult} store={store} />
38
49
  * ```
39
50
  *
40
51
  * @param props - Component props
41
52
  * @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
53
  */
56
54
  export declare const PlayRenderer: React.FC<PlayRendererProps>;
57
55
  //# 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;AAExC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA2CpD,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,19 +1,54 @@
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
  *
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, useStateStore, } from "@json-render/react";
11
+ import { createAtom } from "@xstate/store";
12
+ import { xstateStoreStateStore } from "@json-render/xstate";
8
13
  import { useSignalEffect } from "./useSignalEffect.js";
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
+ }
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
+ }
9
40
  /**
10
41
  * Main renderer component that subscribes to actor signals and renders UI
42
+ * via @json-render/react Renderer.
11
43
  *
12
- * Architecture (per RESEARCH.md Pattern 1):
44
+ * Architecture:
13
45
  * - Subscribes to actor.currentView signal via useSignalEffect
14
- * - Dynamically renders catalog components based on view.component string
15
- * - Forwards user events to actor via actor.send()
16
- * - React state only for triggering renders, NOT business logic
46
+ * - Renders view.spec via StateProvider ActionProvider VisibilityProvider → Renderer
47
+ * - Routes actions via registryResult.handlers() real async functions dispatching to actor.
48
+ * - State store: uses external `store` prop if provided (controlled mode); otherwise
49
+ * creates a fresh @xstate/store atom per view transition seeded from spec.state.
50
+ * The atom resets automatically when the actor transitions to a new view, mirroring
51
+ * the actor's currentView lifecycle.
17
52
  *
18
53
  * Invariant: Actor Authority - Actor decides all state transitions via guards.
19
54
  * Invariant: Passive Infrastructure - Component observes signals and sends events.
@@ -22,66 +57,65 @@ import { useSignalEffect } from "./useSignalEffect.js";
22
57
  * @example
23
58
  * ```typescript
24
59
  * import { PlayRenderer } from "@xmachines/play-react";
25
- * import { definePlayer } from "@xmachines/play-xstate";
60
+ * import { defineRegistry } from "@json-render/react";
26
61
  *
27
- * const actor = definePlayer({ machine, catalog })();
28
- * actor.start();
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
+ * });
29
70
  *
30
- * const components = {
31
- * Dashboard: ({ userId, send }) => <div>User: {userId}</div>,
32
- * LoginForm: ({ error, send }) => <form onSubmit={(e) => {
33
- * e.preventDefault();
34
- * send({ type: "intent", name: "login.submit", payload: {...} });
35
- * }}>...</form>
36
- * };
71
+ * // Uncontrolled fresh atom created per view, seeded from spec.state:
72
+ * <PlayRenderer actor={actor} registryResult={registryResult} />
37
73
  *
38
- * <PlayRenderer actor={actor} components={components} />
74
+ * // Controlled caller provides and owns the store:
75
+ * import { createAtom } from "@xstate/store";
76
+ * import { xstateStoreStateStore } from "@json-render/xstate";
77
+ * const store = xstateStoreStateStore({ atom: createAtom({ username: "" }) });
78
+ * <PlayRenderer actor={actor} registryResult={registryResult} store={store} />
39
79
  * ```
40
80
  *
41
81
  * @param props - Component props
42
82
  * @returns React element rendering current view from actor
43
- *
44
- * @remarks
45
- * **Component lookup:** Dynamically looks up component from `components` map
46
- * using `view.component` string from actor.currentView signal.
47
- *
48
- * **Event forwarding:** Injects `send` function as prop to components. Components
49
- * call `send(event)` to forward intents to actor. Actor guards decide validity.
50
- *
51
- * **Error handling:** If component not found in catalog, logs error and shows
52
- * fallback. This indicates missing component registration, not runtime error.
53
- *
54
- * **CRITICAL:** Never call actor.send() during render - only in event handlers.
55
- * Calling send during render causes infinite render loops.
56
83
  */
57
- export const PlayRenderer = ({ actor, components, fallback = null, }) => {
84
+ export const PlayRenderer = ({ actor, registryResult, store: externalStore, fallback = null, onError, }) => {
58
85
  // React state for triggering re-renders (NOT business logic state)
59
86
  // Signal is source of truth, useState is just React's render trigger
60
87
  const [view, setView] = useState(() => actor.currentView.get());
88
+ // Internal store ref — tracks the current per-view atom store.
89
+ // Keyed to view identity: recreated whenever the view changes (new spec.state seed).
90
+ // Ignored when externalStore is provided.
91
+ const internalStoreRef = useRef(null);
92
+ const lastViewRef = useRef(null);
61
93
  // Subscribe to signal changes
62
94
  useSignalEffect(() => {
63
95
  const currentView = actor.currentView.get();
64
96
  setView(currentView);
65
97
  });
66
- // No view in current state
98
+ // No view in current state — render fallback
67
99
  if (!view) {
68
100
  return _jsx(_Fragment, { children: fallback });
69
101
  }
70
- // Handle null/undefined components catalog gracefully
71
- if (!components) {
72
- console.error(`Components catalog is ${components === null ? "null" : "undefined"}. ` +
73
- `Cannot render component "${view.component}".`);
74
- return _jsx(_Fragment, { children: fallback });
102
+ // Resolve the store to use for StateProvider:
103
+ // - External (controlled): use as-is, caller manages lifecycle
104
+ // - Internal: create a fresh atom when the view changes (new route/state)
105
+ let store;
106
+ if (externalStore) {
107
+ store = externalStore;
75
108
  }
76
- // Look up component from catalog
77
- const Component = components[view.component];
78
- if (!Component) {
79
- console.error(`Component "${view.component}" not found in catalog. ` +
80
- `Available components: ${Object.keys(components).join(", ")}`);
81
- return _jsx(_Fragment, { children: fallback });
109
+ else {
110
+ // Recreate the internal store when the view identity changes
111
+ // (view is a new object on every transition per deriveCurrentView)
112
+ if (internalStoreRef.current === null || lastViewRef.current !== view) {
113
+ const initialState = view.spec?.state ?? {};
114
+ internalStoreRef.current = createViewStore(initialState);
115
+ lastViewRef.current = view;
116
+ }
117
+ store = internalStoreRef.current;
82
118
  }
83
- // Render with props from actor + send function
84
- // bind(actor) ensures 'this' context is correct when components call send()
85
- return _jsx(Component, { ...view.props, send: actor.send.bind(actor) });
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 }) }) }) }));
86
120
  };
87
121
  //# 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;AAGvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC;IAEhE,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,KAAC,SAAS,OAAK,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAI,CAAC;AACpE,CAAC,CAAC"}
1
+ {"version":3,"file":"PlayRenderer.js","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { PlayError } from "@xmachines/play";
2
+ /**
3
+ * Error class for renderer-level errors in the Play architecture.
4
+ *
5
+ * **Note (Phase 29):** `PlayErrorBoundary.componentDidCatch()` no longer throws
6
+ * this error. Re-throwing from `componentDidCatch` can unmount the entire React 19
7
+ * root. `RendererError` is retained for programmatic use in custom error handlers
8
+ * and parent boundaries — it is no longer emitted by the built-in boundary itself.
9
+ *
10
+ * **Error code:** `PLAY_RENDERER_RENDER_ERROR`
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { RendererError } from "@xmachines/play-react/errors";
15
+ *
16
+ * // Create a RendererError programmatically in a custom boundary:
17
+ * throw new RendererError("Custom render failure", { cause: originalError });
18
+ * ```
19
+ */
20
+ export declare class RendererError extends PlayError {
21
+ constructor(message: string, options?: ErrorOptions);
22
+ }
23
+ //# 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;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,aAAc,SAAQ,SAAS;gBAC/B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAInD"}
package/dist/errors.js ADDED
@@ -0,0 +1,26 @@
1
+ import { PlayError } from "@xmachines/play";
2
+ /**
3
+ * Error class for renderer-level errors in the Play architecture.
4
+ *
5
+ * **Note (Phase 29):** `PlayErrorBoundary.componentDidCatch()` no longer throws
6
+ * this error. Re-throwing from `componentDidCatch` can unmount the entire React 19
7
+ * root. `RendererError` is retained for programmatic use in custom error handlers
8
+ * and parent boundaries — it is no longer emitted by the built-in boundary itself.
9
+ *
10
+ * **Error code:** `PLAY_RENDERER_RENDER_ERROR`
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { RendererError } from "@xmachines/play-react/errors";
15
+ *
16
+ * // Create a RendererError programmatically in a custom boundary:
17
+ * throw new RendererError("Custom render failure", { cause: originalError });
18
+ * ```
19
+ */
20
+ export class RendererError extends PlayError {
21
+ constructor(message, options) {
22
+ super("PlayRenderer", "PLAY_RENDERER_RENDER_ERROR", message, options);
23
+ this.name = "RendererError";
24
+ }
25
+ }
26
+ //# 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;;;;;;;;;;;;;;;;;GAiBG;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,17 +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`, `useBoundProp`, `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";
21
+ export { PlayErrorBoundary } from "./PlayErrorBoundary.js";
22
+ export { useActor } from "./useActor.js";
23
+ export { defineRegistry, useBoundProp } from "@json-render/react";
24
+ export type { ComponentFn, ComponentContext } from "@json-render/react";
17
25
  export type { PlayRendererProps } from "./types.js";
26
+ export type { PlayErrorBoundaryProps, PlayErrorBoundaryState } from "./PlayErrorBoundary.js";
27
+ export type { PlayActor } from "./useActor.js";
18
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;AAGvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,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
@@ -2,17 +2,26 @@
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`, `useBoundProp`, `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
  // Main exports
16
20
  export { PlayRenderer } from "./PlayRenderer.js";
17
21
  export { useSignalEffect } from "./useSignalEffect.js";
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, useBoundProp } from "@json-render/react";
18
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"}
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,22 +3,66 @@
3
3
  *
4
4
  * @packageDocumentation
5
5
  */
6
+ import type { DefineRegistryResult } 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
10
  import type { AnyActorLogic } 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.
16
+ *
12
17
  * @property actor - Actor instance with currentView signal (requires Viewable capability)
13
- * @property components - Map of component names to React components
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).
14
22
  * @property fallback - Optional component shown when currentView is null
23
+ * @property onError - Optional callback invoked when a catalog component throws during render.
15
24
  */
16
- export interface PlayRendererProps {
25
+ export interface PlayRendererProps<TLogic extends AnyActorLogic = AnyActorLogic> {
17
26
  /** 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>;
21
- /** Optional component shown when currentView is null */
27
+ actor: AbstractActor<TLogic> & Viewable;
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;
46
+ /**
47
+ * Optional external StateStore (e.g. from `xstateStoreStateStore` in @json-render/xstate).
48
+ * When provided, PlayRenderer operates in controlled mode — spec.state is ignored and
49
+ * this store is the single source of truth for UI state (form values, etc.).
50
+ * When omitted, a fresh @xstate/store atom is created internally per view transition,
51
+ * seeded from spec.state.
52
+ */
53
+ store?: StateStore;
54
+ /** Optional component shown when currentView is null or a catalog component throws */
22
55
  fallback?: React.ReactNode;
56
+ /**
57
+ * Optional error handler callback invoked when a catalog component throws during render.
58
+ * Forwarded directly to the internal `PlayErrorBoundary` — use to integrate with
59
+ * production observability tools (Sentry, Datadog, etc.).
60
+ *
61
+ * @example
62
+ * ```tsx
63
+ * <PlayRenderer actor={actor} registryResult={registryResult} onError={Sentry.captureException} />
64
+ * ```
65
+ */
66
+ onError?: (error: Error, info: React.ErrorInfo) => void;
23
67
  }
24
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,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,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"}
@@ -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"}
@@ -51,6 +51,12 @@
51
51
  * **Implementation note:** We wrap the callback in Signal.Computed because
52
52
  * Signal.subtle.Watcher cannot automatically track arbitrary function calls.
53
53
  * The Computed handles dependency tracking, and the Watcher monitors it.
54
+ *
55
+ * **Memory safety (Phase 29):**
56
+ * - `disposed` flag prevents post-cleanup callback execution: if cleanup is
57
+ * called before a pending microtask fires, the microtask returns early.
58
+ * - `needsEnqueue` guard dedups rapid synchronous signal changes: only one
59
+ * microtask is ever queued per batch of synchronous mutations.
54
60
  */
55
61
  export declare const useSignalEffect: (callback: () => void | (() => void)) => void;
56
62
  //# sourceMappingURL=useSignalEffect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSignalEffect.d.ts","sourceRoot":"","sources":["../src/useSignalEffect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,KAAG,IAuDrE,CAAC"}
1
+ {"version":3,"file":"useSignalEffect.d.ts","sourceRoot":"","sources":["../src/useSignalEffect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,KAAG,IA+DrE,CAAC"}