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

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":"PlayUIProvider.d.ts","sourceRoot":"","sources":["../src/PlayUIProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAA8B,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGzF,KAAK,oBAAoB,GAAG,IAAI,CAC/B,mBAAmB,EACnB,qBAAqB,GAAG,UAAU,GAAG,WAAW,CAChD,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB,EAAE,OAAO,CAAC,oBAAoB,CAAC;CAAG;AAgCjG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAiBxD,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * PlayUIProvider — batteries-included composite provider for actor-driven UI rendering.
4
+ *
5
+ * Wraps ActorProvider + JSONUIProvider (from @json-render/react) to provide a single
6
+ * entry point for all actor lifecycle and UI rendering concerns.
7
+ *
8
+ * Standard usage:
9
+ * ```tsx
10
+ * <PlayUIProvider actor={actor} registryResult={registryResult}>
11
+ * <PlayRenderer />
12
+ * </PlayUIProvider>
13
+ * ```
14
+ *
15
+ * For custom provider composition (escape hatch), use <ActorProvider> directly.
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+ import React from "react";
20
+ import { JSONUIProvider } from "@json-render/react";
21
+ import { ActorProvider, usePlayView } from "./ActorProvider.js";
22
+ /**
23
+ * Inner bridge component — reads ViewContext (usePlayView) to get the resolved handlers,
24
+ * registry, and store, then passes them to JSONUIProvider. The store is passed explicitly
25
+ * so JSONUIProvider's internal StateProvider uses the same store ActorProvider set up —
26
+ * without this, JSONUIProvider would create a fresh empty store, shadowing the seeded one.
27
+ *
28
+ * Must be inside ActorProvider's tree so usePlayView() has access to ViewContextValue.
29
+ */
30
+ function JSONUIBridge({ validationFunctions, navigate, functions, children, }) {
31
+ const view = usePlayView();
32
+ return (_jsx(JSONUIProvider, { registry: view.registry, handlers: view.handlers, store: view.store, ...(validationFunctions && { validationFunctions }), ...(navigate && { navigate }), ...(functions && { functions }), children: children }));
33
+ }
34
+ /**
35
+ * PlayUIProvider — batteries-included entry point for actor-driven UI rendering.
36
+ *
37
+ * Combines actor lifecycle management (ActorProvider) with full UI provider setup
38
+ * (JSONUIProvider including ActionProvider, ValidationProvider, VisibilityProvider,
39
+ * StateProvider, and ConfirmDialogManager).
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * import { PlayUIProvider, PlayRenderer } from "@xmachines/play-react";
44
+ *
45
+ * const registryResult = defineRegistry(catalog, {
46
+ * components: { Login, Dashboard },
47
+ * actions: {
48
+ * login: async ({ username }) => actor.send({ type: 'auth.login', username }),
49
+ * logout: async () => actor.send({ type: 'auth.logout' }),
50
+ * },
51
+ * });
52
+ *
53
+ * <PlayUIProvider actor={actor} registryResult={registryResult}>
54
+ * <PlayRenderer />
55
+ * </PlayUIProvider>
56
+ *
57
+ * // With JSONUIProvider options:
58
+ * <PlayUIProvider
59
+ * actor={actor}
60
+ * registryResult={registryResult}
61
+ * navigate={(path) => router.push(path)}
62
+ * validationFunctions={{ isEmail: (v) => /^.+@.+$/.test(String(v)) }}
63
+ * >
64
+ * <PlayRenderer />
65
+ * </PlayUIProvider>
66
+ * ```
67
+ *
68
+ * @public
69
+ */
70
+ export const PlayUIProvider = ({ validationFunctions, navigate, functions, ...actorProps }) => {
71
+ return (_jsx(ActorProvider, { ...actorProps, children: _jsx(JSONUIBridge, { ...(validationFunctions && { validationFunctions }), ...(navigate && { navigate }), ...(functions && { functions }), children: actorProps.children }) }));
72
+ };
73
+ //# sourceMappingURL=PlayUIProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlayUIProvider.js","sourceRoot":"","sources":["../src/PlayUIProvider.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,cAAc,EAA4B,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,WAAW,EAA2B,MAAM,oBAAoB,CAAC;AAezF;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,EACrB,mBAAmB,EACnB,QAAQ,EACR,SAAS,EACT,QAAQ,GACuD;IAC/D,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAE3B,OAAO,CACN,KAAC,cAAc,IACd,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,KAAK,EAAE,IAAI,CAAC,KAAK,KACb,CAAC,mBAAmB,IAAI,EAAE,mBAAmB,EAAE,CAAC,KAChD,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,CAAC,KAC1B,CAAC,SAAS,IAAI,EAAE,SAAS,EAAE,CAAC,YAE/B,QAAQ,GACO,CACjB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,CAAC,MAAM,cAAc,GAAkC,CAAC,EAC7D,mBAAmB,EACnB,QAAQ,EACR,SAAS,EACT,GAAG,UAAU,EACb,EAAE,EAAE;IACJ,OAAO,CACN,KAAC,aAAa,OAAK,UAAU,YAC5B,KAAC,YAAY,OACR,CAAC,mBAAmB,IAAI,EAAE,mBAAmB,EAAE,CAAC,KAChD,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,CAAC,KAC1B,CAAC,SAAS,IAAI,EAAE,SAAS,EAAE,CAAC,YAE/B,UAAU,CAAC,QAAQ,GACN,GACA,CAChB,CAAC;AACH,CAAC,CAAC"}
@@ -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
@@ -1,18 +1,46 @@
1
1
  /**
2
2
  * @xmachines/play-react - React renderer for XMachines Play architecture
3
3
  *
4
- * Provides a thin React rendering layer that passively observes actor signals
5
- * and renders UI components from catalog definitions. This package enables
4
+ * Provides a provider-based React rendering layer that passively observes actor signals
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
+ * **Standard usage:**
13
+ * ```tsx
14
+ * <PlayUIProvider actor={actor} registryResult={registryResult}>
15
+ * <PlayRenderer />
16
+ * </PlayUIProvider>
17
+ * ```
18
+ *
19
+ * **Escape hatch (custom composition):**
20
+ * ```tsx
21
+ * <ActorProvider actor={actor} registryResult={registryResult}>
22
+ * <JSONUIProvider registry={registryResult.registry}>
23
+ * <PlayRenderer />
24
+ * </JSONUIProvider>
25
+ * </ActorProvider>
26
+ * ```
27
+ *
12
28
  * @packageDocumentation
13
29
  * @module @xmachines/play-react
14
30
  */
15
31
  export { PlayRenderer } from "./PlayRenderer.js";
32
+ export { ActorProvider } from "./ActorProvider.js";
33
+ export type { ActorProviderProps, ViewContextValue } from "./ActorProvider.js";
34
+ export { usePlayView } from "./ActorProvider.js";
35
+ export { PlayUIProvider } from "./PlayUIProvider.js";
36
+ export type { PlayUIProviderProps } from "./PlayUIProvider.js";
16
37
  export { useSignalEffect } from "./useSignalEffect.js";
17
- export type { PlayRendererProps } from "./types.js";
38
+ export { PlayErrorBoundary } from "./PlayErrorBoundary.js";
39
+ export { useActor } from "./useActor.js";
40
+ export type { PlayActor } from "./useActor.js";
41
+ export { defineRegistry, useBoundProp, JSONUIProvider, StateProvider, ActionProvider, VisibilityProvider, ValidationProvider, Renderer, } from "@json-render/react";
42
+ export type { ComponentFn, ComponentContext, JSONUIProviderProps, StateProviderProps, ActionProviderProps, VisibilityProviderProps, ValidationProviderProps, RendererProps, } from "@json-render/react";
43
+ export type { ActorProviderProps as PlayRendererProps } from "./ActorProvider.js";
44
+ export type { RenderErrorHandler } from "./types.js";
45
+ export type { PlayErrorBoundaryProps, PlayErrorBoundaryState } from "./PlayErrorBoundary.js";
18
46
  //# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EACN,cAAc,EACd,YAAY,EACZ,cAAc,EACd,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,GACR,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACX,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,aAAa,GACb,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EAAE,kBAAkB,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAClF,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,YAAY,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -1,18 +1,41 @@
1
1
  /**
2
2
  * @xmachines/play-react - React renderer for XMachines Play architecture
3
3
  *
4
- * Provides a thin React rendering layer that passively observes actor signals
5
- * and renders UI components from catalog definitions. This package enables
4
+ * Provides a provider-based React rendering layer that passively observes actor signals
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
+ * **Standard usage:**
13
+ * ```tsx
14
+ * <PlayUIProvider actor={actor} registryResult={registryResult}>
15
+ * <PlayRenderer />
16
+ * </PlayUIProvider>
17
+ * ```
18
+ *
19
+ * **Escape hatch (custom composition):**
20
+ * ```tsx
21
+ * <ActorProvider actor={actor} registryResult={registryResult}>
22
+ * <JSONUIProvider registry={registryResult.registry}>
23
+ * <PlayRenderer />
24
+ * </JSONUIProvider>
25
+ * </ActorProvider>
26
+ * ```
27
+ *
12
28
  * @packageDocumentation
13
29
  * @module @xmachines/play-react
14
30
  */
15
31
  // Main exports
16
32
  export { PlayRenderer } from "./PlayRenderer.js";
33
+ export { ActorProvider } from "./ActorProvider.js";
34
+ export { usePlayView } from "./ActorProvider.js";
35
+ export { PlayUIProvider } from "./PlayUIProvider.js";
17
36
  export { useSignalEffect } from "./useSignalEffect.js";
37
+ export { PlayErrorBoundary } from "./PlayErrorBoundary.js";
38
+ export { useActor } from "./useActor.js";
39
+ // Re-exports from @json-render/react (per D-14)
40
+ export { defineRegistry, useBoundProp, JSONUIProvider, StateProvider, ActionProvider, VisibilityProvider, ValidationProvider, Renderer, } from "@json-render/react";
18
41
  //# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,eAAe;AACf,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,gDAAgD;AAChD,OAAO,EACN,cAAc,EACd,YAAY,EACZ,cAAc,EACd,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,GACR,MAAM,oBAAoB,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,24 +1,12 @@
1
1
  /**
2
2
  * TypeScript type definitions for play-react
3
3
  *
4
- * @packageDocumentation
5
- */
6
- import type { AbstractActor, Viewable } from "@xmachines/play-actor";
7
- import type React from "react";
8
- import type { AnyActorLogic } from "xstate";
9
- /**
10
- * Props for PlayRenderer component
4
+ * PlayRendererProps has been removed — use ActorProviderProps or PlayUIProviderProps instead.
5
+ * See ActorProvider.tsx and PlayUIProvider.tsx.
11
6
  *
12
- * @property actor - Actor instance with currentView signal (requires Viewable capability)
13
- * @property components - Map of component names to React components
14
- * @property fallback - Optional component shown when currentView is null
7
+ * @packageDocumentation
15
8
  */
16
- export interface PlayRendererProps {
17
- /** 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 */
22
- fallback?: React.ReactNode;
23
- }
9
+ export type { ActorProviderProps } from "./ActorProvider.js";
10
+ export type { PlayUIProviderProps } from "./PlayUIProvider.js";
11
+ export type { RenderErrorHandler } from "@json-render/react";
24
12
  //# 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;;;;;;;GAOG;AAGH,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/types.js CHANGED
@@ -1,6 +1,9 @@
1
1
  /**
2
2
  * TypeScript type definitions for play-react
3
3
  *
4
+ * PlayRendererProps has been removed — use ActorProviderProps or PlayUIProviderProps instead.
5
+ * See ActorProvider.tsx and PlayUIProvider.tsx.
6
+ *
4
7
  * @packageDocumentation
5
8
  */
6
9
  export {};
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -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 {NonNullableError} If called outside an ActorProvider/PlayUIProvider 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;AAIH,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,CAEpC"}
@@ -0,0 +1,27 @@
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 {NonNullableError} If called outside an ActorProvider/PlayUIProvider 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
+ import { assertNonNullable } from "@xmachines/play";
23
+ export const ActorContext = createContext(null);
24
+ export function useActor() {
25
+ return assertNonNullable(useContext(ActorContext), "ActorContext");
26
+ }
27
+ //# 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;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAMpD,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAmB,IAAI,CAAC,CAAC;AAElE,MAAM,UAAU,QAAQ;IACvB,OAAO,iBAAiB,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,cAAc,CAAC,CAAC;AACpE,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"}
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @packageDocumentation
5
5
  */
6
- import { useEffect, useReducer } from "react";
6
+ import { useEffect, useReducer, useRef } from "react";
7
7
  import { Signal } from "@xmachines/play-signals";
8
8
  /**
9
9
  * Marker symbol returned by effect Computed to satisfy type requirements
@@ -58,39 +58,54 @@ const EFFECT_RUN_MARKER = Symbol("effect-run");
58
58
  * **Implementation note:** We wrap the callback in Signal.Computed because
59
59
  * Signal.subtle.Watcher cannot automatically track arbitrary function calls.
60
60
  * The Computed handles dependency tracking, and the Watcher monitors it.
61
+ *
62
+ * **Memory safety (Phase 29):**
63
+ * - `disposed` flag prevents post-cleanup callback execution: if cleanup is
64
+ * called before a pending microtask fires, the microtask returns early.
65
+ * - `needsEnqueue` guard dedups rapid synchronous signal changes: only one
66
+ * microtask is ever queued per batch of synchronous mutations.
61
67
  */
62
68
  export const useSignalEffect = (callback) => {
69
+ // Store callback in a ref so the effect closure always calls the latest version
70
+ // WITHOUT triggering watcher teardown/re-setup on each render (standard React ref pattern)
71
+ const callbackRef = useRef(callback);
72
+ // Assignment outside useEffect keeps ref current without triggering re-effect
73
+ callbackRef.current = callback;
63
74
  // Force re-render when signal changes (React needs state change to re-render)
64
75
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
65
76
  useEffect(() => {
66
77
  let cleanup;
67
78
  let needsEnqueue = true;
79
+ let disposed = false;
68
80
  // Wrap callback in a Computed to automatically track dependencies
69
81
  // The Computed will re-evaluate when any accessed signal changes
70
82
  const effect = new Signal.Computed(() => {
71
83
  // Run cleanup from previous effect
72
- cleanup?.();
73
- // Run user callback (may access signals, return cleanup)
74
- cleanup = callback();
84
+ if (typeof cleanup === "function")
85
+ cleanup();
86
+ // Run user callback via ref (always latest version, no dep churn)
87
+ cleanup = callbackRef.current();
75
88
  // Return marker value (Computed requires a return value)
76
89
  return EFFECT_RUN_MARKER;
77
90
  });
78
91
  // Create watcher to detect when Computed needs re-evaluation
79
92
  const watcher = new Signal.subtle.Watcher(() => {
80
93
  // Batching flag prevents multiple microtasks for rapid changes
81
- if (needsEnqueue) {
82
- needsEnqueue = false;
83
- // Schedule microtask to batch updates (prevent React thrashing)
84
- queueMicrotask(() => {
85
- needsEnqueue = true;
86
- // Re-evaluate the Computed (runs callback with new signal values)
87
- effect.get();
88
- // Force React re-render
89
- forceUpdate();
90
- // Re-watch for next change
91
- watcher.watch();
92
- });
93
- }
94
+ if (disposed || !needsEnqueue)
95
+ return;
96
+ needsEnqueue = false;
97
+ // Schedule microtask to batch updates (prevent React thrashing)
98
+ queueMicrotask(() => {
99
+ if (disposed)
100
+ return;
101
+ needsEnqueue = true;
102
+ // Re-evaluate the Computed (runs callback with new signal values)
103
+ effect.get();
104
+ // Force React re-render
105
+ forceUpdate();
106
+ // Re-watch for next change
107
+ watcher.watch();
108
+ });
94
109
  });
95
110
  // Watch the Computed signal
96
111
  watcher.watch(effect);
@@ -98,10 +113,12 @@ export const useSignalEffect = (callback) => {
98
113
  effect.get();
99
114
  // Cleanup on unmount
100
115
  return () => {
101
- cleanup?.();
116
+ disposed = true;
117
+ if (typeof cleanup === "function")
118
+ cleanup();
102
119
  // Unwatch to stop receiving signal updates
103
120
  watcher.unwatch(effect);
104
121
  };
105
- }, [callback]);
122
+ }, []); // ← [] dep array: watcher created ONCE per mount, not once per render
106
123
  };
107
124
  //# sourceMappingURL=useSignalEffect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSignalEffect.js","sourceRoot":"","sources":["../src/useSignalEffect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD;;;GAGG;AACH,MAAM,iBAAiB,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAmC,EAAQ,EAAE;IAC5E,8EAA8E;IAC9E,MAAM,CAAC,EAAE,WAAW,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAEpD,SAAS,CAAC,GAAG,EAAE;QACd,IAAI,OAA4B,CAAC;QACjC,IAAI,YAAY,GAAG,IAAI,CAAC;QAExB,kEAAkE;QAClE,iEAAiE;QACjE,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE;YACvC,mCAAmC;YACnC,OAAO,EAAE,EAAE,CAAC;YAEZ,yDAAyD;YACzD,OAAO,GAAG,QAAQ,EAAE,CAAC;YAErB,yDAAyD;YACzD,OAAO,iBAAiB,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,6DAA6D;QAC7D,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;YAC9C,+DAA+D;YAC/D,IAAI,YAAY,EAAE,CAAC;gBAClB,YAAY,GAAG,KAAK,CAAC;gBACrB,gEAAgE;gBAChE,cAAc,CAAC,GAAG,EAAE;oBACnB,YAAY,GAAG,IAAI,CAAC;oBAEpB,kEAAkE;oBAClE,MAAM,CAAC,GAAG,EAAE,CAAC;oBAEb,wBAAwB;oBACxB,WAAW,EAAE,CAAC;oBAEd,2BAA2B;oBAC3B,OAAO,CAAC,KAAK,EAAE,CAAC;gBACjB,CAAC,CAAC,CAAC;YACJ,CAAC;QACF,CAAC,CAAC,CAAC;QAEH,4BAA4B;QAC5B,OAAO,CAAC,KAAK,CAAC,MAAa,CAAC,CAAC;QAE7B,yCAAyC;QACzC,MAAM,CAAC,GAAG,EAAE,CAAC;QAEb,qBAAqB;QACrB,OAAO,GAAG,EAAE;YACX,OAAO,EAAE,EAAE,CAAC;YACZ,2CAA2C;YAC3C,OAAO,CAAC,OAAO,CAAC,MAAa,CAAC,CAAC;QAChC,CAAC,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;AAChB,CAAC,CAAC"}
1
+ {"version":3,"file":"useSignalEffect.js","sourceRoot":"","sources":["../src/useSignalEffect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD;;;GAGG;AACH,MAAM,iBAAiB,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAmC,EAAQ,EAAE;IAC5E,gFAAgF;IAChF,2FAA2F;IAC3F,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,8EAA8E;IAC9E,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IAE/B,8EAA8E;IAC9E,MAAM,CAAC,EAAE,WAAW,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAEpD,SAAS,CAAC,GAAG,EAAE;QACd,IAAI,OAA4B,CAAC;QACjC,IAAI,YAAY,GAAG,IAAI,CAAC;QACxB,IAAI,QAAQ,GAAG,KAAK,CAAC;QAErB,kEAAkE;QAClE,iEAAiE;QACjE,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE;YACvC,mCAAmC;YACnC,IAAI,OAAO,OAAO,KAAK,UAAU;gBAAE,OAAO,EAAE,CAAC;YAE7C,kEAAkE;YAClE,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC;YAEhC,yDAAyD;YACzD,OAAO,iBAAiB,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,6DAA6D;QAC7D,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;YAC9C,+DAA+D;YAC/D,IAAI,QAAQ,IAAI,CAAC,YAAY;gBAAE,OAAO;YACtC,YAAY,GAAG,KAAK,CAAC;YACrB,gEAAgE;YAChE,cAAc,CAAC,GAAG,EAAE;gBACnB,IAAI,QAAQ;oBAAE,OAAO;gBACrB,YAAY,GAAG,IAAI,CAAC;gBAEpB,kEAAkE;gBAClE,MAAM,CAAC,GAAG,EAAE,CAAC;gBAEb,wBAAwB;gBACxB,WAAW,EAAE,CAAC;gBAEd,2BAA2B;gBAC3B,OAAO,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,4BAA4B;QAC5B,OAAO,CAAC,KAAK,CAAC,MAAkC,CAAC,CAAC;QAElD,yCAAyC;QACzC,MAAM,CAAC,GAAG,EAAE,CAAC;QAEb,qBAAqB;QACrB,OAAO,GAAG,EAAE;YACX,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,OAAO,OAAO,KAAK,UAAU;gBAAE,OAAO,EAAE,CAAC;YAC7C,2CAA2C;YAC3C,OAAO,CAAC,OAAO,CAAC,MAAkC,CAAC,CAAC;QACrD,CAAC,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,sEAAsE;AAC/E,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmachines/play-react",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.41",
4
4
  "description": "React renderer for XMachines Play architecture with signal-driven rendering",
5
5
  "keywords": [
6
6
  "actor",
@@ -26,6 +26,7 @@
26
26
  "type": "module",
27
27
  "exports": {
28
28
  ".": {
29
+ "source": "./src/index.ts",
29
30
  "types": "./dist/index.d.ts",
30
31
  "import": "./dist/index.js"
31
32
  }
@@ -35,31 +36,46 @@
35
36
  },
36
37
  "scripts": {
37
38
  "build": "tsc --build",
38
- "clean": "rm -rf dist *.tsbuildinfo",
39
- "typecheck": "tsc --noEmit",
40
- "test": "vitest run",
39
+ "clean": "rm -rf dist *.tsbuildinfo coverage .vitest-attachments test/browser/__screenshots__ node_modules/.svelte2tsx-*",
40
+ "lint": "oxlint .",
41
+ "format": "oxfmt .",
42
+ "test": "vitest",
41
43
  "test:vitest": "vitest run",
42
- "test:browser": "vitest run --browser.enabled --browser.name=chromium test/browser",
44
+ "test:browser": "vitest --browser.enabled --browser.name=chromium test/browser",
43
45
  "prepublishOnly": "npm run build"
44
46
  },
45
47
  "dependencies": {
46
- "@xmachines/play-actor": "1.0.0-beta.4",
47
- "@xmachines/play-catalog": "1.0.0-beta.4",
48
- "@xmachines/play-signals": "1.0.0-beta.4"
48
+ "@xmachines/play": "1.0.0-beta.41",
49
+ "@xmachines/play-actor": "1.0.0-beta.41",
50
+ "@xmachines/play-signals": "1.0.0-beta.41"
49
51
  },
50
52
  "devDependencies": {
53
+ "@json-render/core": "^0.18.0",
54
+ "@json-render/react": "^0.18.0",
55
+ "@json-render/xstate": "^0.18.0",
56
+ "@testing-library/jest-dom": "^6.9.1",
51
57
  "@testing-library/react": "^16.3.2",
52
- "@types/node": "^25.5.0",
58
+ "@types/node": "^25.6.0",
53
59
  "@types/react": "^19.2.14",
54
60
  "@types/react-dom": "^19.2.3",
55
- "@xmachines/shared": "1.0.0-beta.4",
56
- "jsdom": "^29.0.0",
57
- "react": "^19.2.4",
58
- "react-dom": "^19.2.4",
59
- "typescript": "^5.9.3"
61
+ "@xmachines/shared": "1.0.0-beta.41",
62
+ "@xstate/store": "^3.17.0",
63
+ "jsdom": "^29.0.2",
64
+ "oxfmt": "^0.45.0",
65
+ "oxlint": "^1.60.0",
66
+ "react": "^19.2.5",
67
+ "react-dom": "^19.2.5",
68
+ "typescript": "^5.9.3 || ^6.0.3",
69
+ "vitest": "^4.1.4",
70
+ "xstate": "^5.30.0"
60
71
  },
61
72
  "peerDependencies": {
62
- "react": "^18.2.0 || ^19.0.0",
63
- "react-dom": "^18.2.0 || ^19.0.0"
73
+ "@json-render/core": "^0.18.0",
74
+ "@json-render/react": "^0.18.0",
75
+ "@json-render/xstate": "^0.18.0",
76
+ "@xstate/store": "^3.17.0",
77
+ "react": "^18.0.0 || ^19.0.0",
78
+ "react-dom": "^18.0.0 || ^19.0.0",
79
+ "xstate": "^5.30.0"
64
80
  }
65
81
  }