@xmachines/play-react 1.0.0-beta.4 → 1.0.0-beta.40

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,82 @@
1
+ /**
2
+ * ActorProvider — escape hatch primitive for actor lifecycle management.
3
+ *
4
+ * Owns: actor bridging, signal subscription (useSignalEffect), per-view StateStore
5
+ * lifecycle (controlled/uncontrolled), handler resolution via inner component pattern
6
+ * (uses useStateStore()), StateProvider wrap, PlayErrorBoundary wrap, onRenderError injection.
7
+ *
8
+ * Standard usage: prefer <PlayUIProvider> unless you need to compose providers manually.
9
+ *
10
+ * @packageDocumentation
11
+ */
12
+ import React from "react";
13
+ import type { DefineRegistryResult, ComponentRegistry } from "@json-render/react";
14
+ import type { BaseActorProviderProps, BaseViewContextValue } from "@xmachines/play-actor";
15
+ /**
16
+ * Props for the ActorProvider component.
17
+ *
18
+ * @public
19
+ */
20
+ export interface ActorProviderProps extends BaseActorProviderProps<DefineRegistryResult> {
21
+ /** Optional component shown when currentView is null or a catalog component throws */
22
+ fallback?: React.ReactNode;
23
+ /** Optional error handler callback invoked when a catalog component throws during render */
24
+ onError?: (error: Error, info: React.ErrorInfo) => void;
25
+ /** Child components to render inside the provider tree */
26
+ children: React.ReactNode;
27
+ }
28
+ /**
29
+ * Value provided by ViewContext (accessible via usePlayView()).
30
+ *
31
+ * @public
32
+ */
33
+ export interface ViewContextValue extends BaseViewContextValue<ComponentRegistry> {
34
+ }
35
+ /**
36
+ * Hook to access the current view spec, handlers, and registry.
37
+ *
38
+ * Must be called inside <ActorProvider> or <PlayUIProvider>.
39
+ *
40
+ * @throws {Error} If called outside an ActorProvider/PlayUIProvider tree
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * import { usePlayView } from "@xmachines/play-react";
45
+ *
46
+ * function MyRenderer() {
47
+ * const view = usePlayView();
48
+ * return <Renderer spec={view.spec} registry={view.registry} />;
49
+ * }
50
+ * ```
51
+ *
52
+ * @public
53
+ */
54
+ export declare function usePlayView(): ViewContextValue;
55
+ /**
56
+ * ActorProvider — escape hatch primitive for composing actor lifecycle with custom providers.
57
+ *
58
+ * Subscribes to actor.currentView signal, manages the per-view StateStore lifecycle,
59
+ * wraps children in StateProvider and PlayErrorBoundary, and injects onRenderError
60
+ * into the component registry.
61
+ *
62
+ * Standard usage: prefer <PlayUIProvider> unless you need to compose providers manually.
63
+ *
64
+ * @example
65
+ * ```tsx
66
+ * // Custom composition (escape hatch):
67
+ * <ActorProvider actor={actor} registryResult={registryResult}>
68
+ * <JSONUIProvider registry={registryResult.registry}>
69
+ * <PlayRenderer />
70
+ * </JSONUIProvider>
71
+ * </ActorProvider>
72
+ *
73
+ * // Standard usage: prefer PlayUIProvider
74
+ * <PlayUIProvider actor={actor} registryResult={registryResult}>
75
+ * <PlayRenderer />
76
+ * </PlayUIProvider>
77
+ * ```
78
+ *
79
+ * @public
80
+ */
81
+ export declare const ActorProvider: React.FC<ActorProviderProps>;
82
+ //# sourceMappingURL=ActorProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActorProvider.d.ts","sourceRoot":"","sources":["../src/ActorProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAsD,MAAM,OAAO,CAAC;AAE3E,OAAO,KAAK,EAAE,oBAAoB,EAAY,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAO5F,OAAO,KAAK,EAAY,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAIpG;;;;GAIG;AACH,MAAM,WAAW,kBAAmB,SAAQ,sBAAsB,CAAC,oBAAoB,CAAC;IACvF,sFAAsF;IACtF,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,4FAA4F;IAC5F,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC;IACxD,0DAA0D;IAC1D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;CAAG;AAQpF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,IAAI,gBAAgB,CAE9C;AAmDD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA0FtD,CAAC"}
@@ -0,0 +1,167 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * ActorProvider — escape hatch primitive for actor lifecycle management.
4
+ *
5
+ * Owns: actor bridging, signal subscription (useSignalEffect), per-view StateStore
6
+ * lifecycle (controlled/uncontrolled), handler resolution via inner component pattern
7
+ * (uses useStateStore()), StateProvider wrap, PlayErrorBoundary wrap, onRenderError injection.
8
+ *
9
+ * Standard usage: prefer <PlayUIProvider> unless you need to compose providers manually.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+ import React, { useState, useRef, createContext, useContext } from "react";
14
+ import { StateProvider, useStateStore } from "@json-render/react";
15
+ import { createAtom } from "@xstate/store";
16
+ import { xstateStoreStateStore } from "@json-render/xstate";
17
+ import { useSignalEffect } from "./useSignalEffect.js";
18
+ import { PlayErrorBoundary } from "./PlayErrorBoundary.js";
19
+ import { assertNonNullable } from "@xmachines/play";
20
+ import { ActorContext } from "./useActor.js";
21
+ /**
22
+ * Internal React context for ViewContextValue.
23
+ * Accessed via usePlayView() hook.
24
+ */
25
+ const ViewContext = createContext(null);
26
+ /**
27
+ * Hook to access the current view spec, handlers, and registry.
28
+ *
29
+ * Must be called inside <ActorProvider> or <PlayUIProvider>.
30
+ *
31
+ * @throws {Error} If called outside an ActorProvider/PlayUIProvider tree
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * import { usePlayView } from "@xmachines/play-react";
36
+ *
37
+ * function MyRenderer() {
38
+ * const view = usePlayView();
39
+ * return <Renderer spec={view.spec} registry={view.registry} />;
40
+ * }
41
+ * ```
42
+ *
43
+ * @public
44
+ */
45
+ export function usePlayView() {
46
+ return assertNonNullable(useContext(ViewContext), "ViewContext");
47
+ }
48
+ /**
49
+ * Create a StateStore backed by a fresh @xstate/store atom seeded from the given state.
50
+ * Called internally per view transition when no external store prop is provided.
51
+ */
52
+ function createViewStore(initialState) {
53
+ return xstateStoreStateStore({ atom: createAtom(initialState) });
54
+ }
55
+ /**
56
+ * Inner component that runs inside StateProvider so it can access StateStore context
57
+ * via useStateStore(). Resolves action handlers from registryResult.handlers() using
58
+ * the live StateProvider set/getSnapshot functions, then exposes them via ViewContext.
59
+ */
60
+ function ActorProviderInner({ registryResult, spec, store, children, }) {
61
+ const stateCtx = useStateStore();
62
+ // Build a SetState adapter: the handlers factory expects an updater-function pattern
63
+ // ((prev) => next), while stateCtx provides path-based set/update. This adapter
64
+ // bridges the two so action functions can use setState if needed.
65
+ const setStateAdapter = (updater) => {
66
+ const prev = stateCtx.getSnapshot();
67
+ stateCtx.update(updater(prev));
68
+ };
69
+ const handlers = registryResult.handlers(() => setStateAdapter, () => stateCtx.getSnapshot());
70
+ const viewValue = {
71
+ spec,
72
+ handlers,
73
+ registry: registryResult.registry,
74
+ store,
75
+ };
76
+ return _jsx(ViewContext.Provider, { value: viewValue, children: children });
77
+ }
78
+ /**
79
+ * ActorProvider — escape hatch primitive for composing actor lifecycle with custom providers.
80
+ *
81
+ * Subscribes to actor.currentView signal, manages the per-view StateStore lifecycle,
82
+ * wraps children in StateProvider and PlayErrorBoundary, and injects onRenderError
83
+ * into the component registry.
84
+ *
85
+ * Standard usage: prefer <PlayUIProvider> unless you need to compose providers manually.
86
+ *
87
+ * @example
88
+ * ```tsx
89
+ * // Custom composition (escape hatch):
90
+ * <ActorProvider actor={actor} registryResult={registryResult}>
91
+ * <JSONUIProvider registry={registryResult.registry}>
92
+ * <PlayRenderer />
93
+ * </JSONUIProvider>
94
+ * </ActorProvider>
95
+ *
96
+ * // Standard usage: prefer PlayUIProvider
97
+ * <PlayUIProvider actor={actor} registryResult={registryResult}>
98
+ * <PlayRenderer />
99
+ * </PlayUIProvider>
100
+ * ```
101
+ *
102
+ * @public
103
+ */
104
+ export const ActorProvider = ({ actor, registryResult, store: externalStore, fallback = null, onError, onRenderError, children, }) => {
105
+ // React state for triggering re-renders (NOT business logic state)
106
+ // Signal is source of truth, useState is just React's render trigger
107
+ const [view, setView] = useState(() => actor.currentView.get());
108
+ // Internal store ref — tracks the current per-view atom store.
109
+ // Keyed to view identity: recreated whenever the view changes (new spec.state seed).
110
+ // Ignored when externalStore is provided.
111
+ const internalStoreRef = useRef(null);
112
+ const lastViewRef = useRef(null);
113
+ // Subscribe to signal changes
114
+ useSignalEffect(() => {
115
+ const currentView = actor.currentView.get();
116
+ setView(currentView);
117
+ });
118
+ // Inject onRenderError prop into registry (non-enumerable, overrides defineRegistry-level handler)
119
+ // Centralised here per D-19 — one location for all framework renderers
120
+ const activeRegistryResult = onRenderError
121
+ ? {
122
+ ...registryResult,
123
+ registry: (() => {
124
+ const r = { ...registryResult.registry };
125
+ Object.defineProperty(r, "onRenderError", {
126
+ value: onRenderError,
127
+ enumerable: false,
128
+ configurable: true,
129
+ });
130
+ return r;
131
+ })(),
132
+ }
133
+ : registryResult;
134
+ // No view in current state — render fallback
135
+ if (!view) {
136
+ return _jsx(_Fragment, { children: fallback });
137
+ }
138
+ // Resolve the store to use for StateProvider:
139
+ // - External (controlled): use as-is, caller manages lifecycle
140
+ // - Internal: create a fresh atom when the view changes (new route/state)
141
+ let store;
142
+ if (externalStore) {
143
+ store = externalStore;
144
+ }
145
+ else {
146
+ // Recreate the internal store when the view identity changes
147
+ // (view is a new object on every transition per deriveCurrentView)
148
+ if (internalStoreRef.current === null || lastViewRef.current !== view) {
149
+ // Proto-safe guard (T-37-03-01): prevents Date/Array/class-instance from being
150
+ // passed to createAtom. Replaces the weak `?? {}` guard from old code_context.
151
+ const rawState = view.state;
152
+ const initialState = rawState !== null &&
153
+ rawState !== undefined &&
154
+ typeof rawState === "object" &&
155
+ !Array.isArray(rawState) &&
156
+ (Object.getPrototypeOf(rawState) === Object.prototype ||
157
+ Object.getPrototypeOf(rawState) === null)
158
+ ? rawState
159
+ : {};
160
+ internalStoreRef.current = createViewStore(initialState);
161
+ lastViewRef.current = view;
162
+ }
163
+ store = internalStoreRef.current;
164
+ }
165
+ return (_jsx(ActorContext.Provider, { value: actor, children: _jsx(PlayErrorBoundary, { fallback: fallback, ...(onError && { onError }), children: _jsx(StateProvider, { store: store, children: _jsx(ActorProviderInner, { registryResult: activeRegistryResult, spec: view, store: store, children: children }) }) }) }));
166
+ };
167
+ //# sourceMappingURL=ActorProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActorProvider.js","sourceRoot":"","sources":["../src/ActorProvider.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * PlayErrorBoundary - React error boundary for catching catalog component render errors
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import React from "react";
7
+ /**
8
+ * Props for PlayErrorBoundary
9
+ *
10
+ * @public
11
+ */
12
+ export interface PlayErrorBoundaryProps {
13
+ /** Fallback UI to render when a child component throws. Defaults to null. */
14
+ fallback?: React.ReactNode;
15
+ /** Child components to render */
16
+ children: React.ReactNode;
17
+ /** Optional error handler callback — forwards errors to observability tools (Sentry, etc.) */
18
+ onError?: (error: Error, info: React.ErrorInfo) => void;
19
+ }
20
+ /**
21
+ * Internal state shape for PlayErrorBoundary
22
+ *
23
+ * @public
24
+ */
25
+ export interface PlayErrorBoundaryState {
26
+ hasError: boolean;
27
+ error: Error | null;
28
+ }
29
+ /**
30
+ * React class component error boundary for catching catalog component render errors.
31
+ *
32
+ * Wraps catalog component renders so failures are caught and forwarded to standard
33
+ * React error boundary protocol. Consumers can attach the `onError` prop to forward
34
+ * errors to production observability tools (Sentry, Datadog, etc.).
35
+ *
36
+ * **React 19 safety (Phase 29):** `componentDidCatch` calls `onError` for observability
37
+ * but does NOT re-throw. `getDerivedStateFromError` already sets the fallback state —
38
+ * re-throwing from `componentDidCatch` can unmount the entire React 19 root.
39
+ *
40
+ * Per CONS-14: Class component pattern works with all React versions (18 and 19).
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * <PlayErrorBoundary fallback={<div>Something went wrong</div>} onError={Sentry.captureException}>
45
+ * <CatalogComponent {...props} />
46
+ * </PlayErrorBoundary>
47
+ * ```
48
+ */
49
+ export declare class PlayErrorBoundary extends React.Component<PlayErrorBoundaryProps, PlayErrorBoundaryState> {
50
+ constructor(props: PlayErrorBoundaryProps);
51
+ static getDerivedStateFromError(error: Error): PlayErrorBoundaryState;
52
+ componentDidCatch(error: Error, info: React.ErrorInfo): void;
53
+ render(): React.ReactNode;
54
+ }
55
+ //# sourceMappingURL=PlayErrorBoundary.d.ts.map
@@ -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,57 +1,36 @@
1
1
  /**
2
- * PlayRenderer - Main React renderer component for XMachines Play architecture
2
+ * PlayRenderer — zero-prop leaf component for rendering the current actor view.
3
3
  *
4
- * @packageDocumentation
5
- */
6
- import React from "react";
7
- import type { PlayRendererProps } from "./types.js";
8
- /**
9
- * Main renderer component that subscribes to actor signals and renders UI
10
- *
11
- * Architecture (per RESEARCH.md Pattern 1):
12
- * - 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
- *
17
- * Invariant: Actor Authority - Actor decides all state transitions via guards.
18
- * Invariant: Passive Infrastructure - Component observes signals and sends events.
19
- * Invariant: Signal-Only Reactivity - Business logic state lives in actor signals.
20
- *
21
- * @example
22
- * ```typescript
23
- * import { PlayRenderer } from "@xmachines/play-react";
24
- * import { definePlayer } from "@xmachines/play-xstate";
25
- *
26
- * const actor = definePlayer({ machine, catalog })();
27
- * actor.start();
4
+ * Must be rendered inside <ActorProvider> or <PlayUIProvider>.
5
+ * Reads view spec, handlers, and registry from usePlayView() context,
6
+ * then delegates to @json-render/react Renderer.
28
7
  *
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} />
8
+ * Standard usage:
9
+ * ```tsx
10
+ * <PlayUIProvider actor={actor} registryResult={registryResult}>
11
+ * <PlayRenderer />
12
+ * </PlayUIProvider>
38
13
  * ```
39
14
  *
40
- * @param props - Component props
41
- * @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.
15
+ * For custom provider composition, use <ActorProvider> (escape hatch):
16
+ * ```tsx
17
+ * <ActorProvider actor={actor} registryResult={registryResult}>
18
+ * <JSONUIProvider registry={registryResult.registry}>
19
+ * <PlayRenderer />
20
+ * </JSONUIProvider>
21
+ * </ActorProvider>
22
+ * ```
46
23
  *
47
- * **Event forwarding:** Injects `send` function as prop to components. Components
48
- * call `send(event)` to forward intents to actor. Actor guards decide validity.
24
+ * @packageDocumentation
25
+ */
26
+ import React from "react";
27
+ /**
28
+ * Zero-prop leaf component that renders the current actor view.
49
29
  *
50
- * **Error handling:** If component not found in catalog, logs error and shows
51
- * fallback. This indicates missing component registration, not runtime error.
30
+ * Reads the current PlaySpec, handlers, and registry from the ActorProvider
31
+ * context via usePlayView(), then renders via @json-render/react Renderer.
52
32
  *
53
- * **CRITICAL:** Never call actor.send() during render - only in event handlers.
54
- * Calling send during render causes infinite render loops.
33
+ * @public
55
34
  */
56
- export declare const PlayRenderer: React.FC<PlayRendererProps>;
35
+ export declare const PlayRenderer: React.FC<Record<string, never>>;
57
36
  //# 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;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAGxD,CAAC"}
@@ -1,87 +1,42 @@
1
- import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  /**
3
- * PlayRenderer - Main React renderer component for XMachines Play architecture
3
+ * PlayRenderer — zero-prop leaf component for rendering the current actor view.
4
4
  *
5
- * @packageDocumentation
6
- */
7
- import React, { useState } from "react";
8
- import { useSignalEffect } from "./useSignalEffect.js";
9
- /**
10
- * Main renderer component that subscribes to actor signals and renders UI
11
- *
12
- * Architecture (per RESEARCH.md Pattern 1):
13
- * - 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
17
- *
18
- * Invariant: Actor Authority - Actor decides all state transitions via guards.
19
- * Invariant: Passive Infrastructure - Component observes signals and sends events.
20
- * Invariant: Signal-Only Reactivity - Business logic state lives in actor signals.
21
- *
22
- * @example
23
- * ```typescript
24
- * import { PlayRenderer } from "@xmachines/play-react";
25
- * import { definePlayer } from "@xmachines/play-xstate";
26
- *
27
- * const actor = definePlayer({ machine, catalog })();
28
- * actor.start();
5
+ * Must be rendered inside <ActorProvider> or <PlayUIProvider>.
6
+ * Reads view spec, handlers, and registry from usePlayView() context,
7
+ * then delegates to @json-render/react Renderer.
29
8
  *
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
- * };
37
- *
38
- * <PlayRenderer actor={actor} components={components} />
9
+ * Standard usage:
10
+ * ```tsx
11
+ * <PlayUIProvider actor={actor} registryResult={registryResult}>
12
+ * <PlayRenderer />
13
+ * </PlayUIProvider>
39
14
  * ```
40
15
  *
41
- * @param props - Component props
42
- * @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.
16
+ * For custom provider composition, use <ActorProvider> (escape hatch):
17
+ * ```tsx
18
+ * <ActorProvider actor={actor} registryResult={registryResult}>
19
+ * <JSONUIProvider registry={registryResult.registry}>
20
+ * <PlayRenderer />
21
+ * </JSONUIProvider>
22
+ * </ActorProvider>
23
+ * ```
47
24
  *
48
- * **Event forwarding:** Injects `send` function as prop to components. Components
49
- * call `send(event)` to forward intents to actor. Actor guards decide validity.
25
+ * @packageDocumentation
26
+ */
27
+ import React from "react";
28
+ import { Renderer } from "@json-render/react";
29
+ import { usePlayView } from "./ActorProvider.js";
30
+ /**
31
+ * Zero-prop leaf component that renders the current actor view.
50
32
  *
51
- * **Error handling:** If component not found in catalog, logs error and shows
52
- * fallback. This indicates missing component registration, not runtime error.
33
+ * Reads the current PlaySpec, handlers, and registry from the ActorProvider
34
+ * context via usePlayView(), then renders via @json-render/react Renderer.
53
35
  *
54
- * **CRITICAL:** Never call actor.send() during render - only in event handlers.
55
- * Calling send during render causes infinite render loops.
36
+ * @public
56
37
  */
57
- export const PlayRenderer = ({ actor, components, fallback = null, }) => {
58
- // React state for triggering re-renders (NOT business logic state)
59
- // Signal is source of truth, useState is just React's render trigger
60
- const [view, setView] = useState(() => actor.currentView.get());
61
- // Subscribe to signal changes
62
- useSignalEffect(() => {
63
- const currentView = actor.currentView.get();
64
- setView(currentView);
65
- });
66
- // No view in current state
67
- if (!view) {
68
- return _jsx(_Fragment, { children: fallback });
69
- }
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 });
75
- }
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 });
82
- }
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) });
38
+ export const PlayRenderer = () => {
39
+ const view = usePlayView();
40
+ return _jsx(Renderer, { spec: view.spec, registry: view.registry });
86
41
  };
87
42
  //# 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":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAoC,GAAG,EAAE;IACjE,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,OAAO,KAAC,QAAQ,IAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,GAAI,CAAC;AAC/D,CAAC,CAAC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * PlayUIProvider — batteries-included composite provider for actor-driven UI rendering.
3
+ *
4
+ * Wraps ActorProvider + JSONUIProvider (from @json-render/react) to provide a single
5
+ * entry point for all actor lifecycle and UI rendering concerns.
6
+ *
7
+ * Standard usage:
8
+ * ```tsx
9
+ * <PlayUIProvider actor={actor} registryResult={registryResult}>
10
+ * <PlayRenderer />
11
+ * </PlayUIProvider>
12
+ * ```
13
+ *
14
+ * For custom provider composition (escape hatch), use <ActorProvider> directly.
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+ import React from "react";
19
+ import { type JSONUIProviderProps } from "@json-render/react";
20
+ import { type ActorProviderProps } from "./ActorProvider.js";
21
+ type JSONUIForwardedProps = Pick<JSONUIProviderProps, "validationFunctions" | "navigate" | "functions">;
22
+ /**
23
+ * Props for PlayUIProvider — all ActorProvider props plus JSONUIProvider's own props.
24
+ *
25
+ * @public
26
+ */
27
+ export interface PlayUIProviderProps extends ActorProviderProps, Partial<JSONUIForwardedProps> {
28
+ }
29
+ /**
30
+ * PlayUIProvider — batteries-included entry point for actor-driven UI rendering.
31
+ *
32
+ * Combines actor lifecycle management (ActorProvider) with full UI provider setup
33
+ * (JSONUIProvider including ActionProvider, ValidationProvider, VisibilityProvider,
34
+ * StateProvider, and ConfirmDialogManager).
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * import { PlayUIProvider, PlayRenderer } from "@xmachines/play-react";
39
+ *
40
+ * const registryResult = defineRegistry(catalog, {
41
+ * components: { Login, Dashboard },
42
+ * actions: {
43
+ * login: async ({ username }) => actor.send({ type: 'auth.login', username }),
44
+ * logout: async () => actor.send({ type: 'auth.logout' }),
45
+ * },
46
+ * });
47
+ *
48
+ * <PlayUIProvider actor={actor} registryResult={registryResult}>
49
+ * <PlayRenderer />
50
+ * </PlayUIProvider>
51
+ *
52
+ * // With JSONUIProvider options:
53
+ * <PlayUIProvider
54
+ * actor={actor}
55
+ * registryResult={registryResult}
56
+ * navigate={(path) => router.push(path)}
57
+ * validationFunctions={{ isEmail: (v) => /^.+@.+$/.test(String(v)) }}
58
+ * >
59
+ * <PlayRenderer />
60
+ * </PlayUIProvider>
61
+ * ```
62
+ *
63
+ * @public
64
+ */
65
+ export declare const PlayUIProvider: React.FC<PlayUIProviderProps>;
66
+ export {};
67
+ //# sourceMappingURL=PlayUIProvider.d.ts.map