@xmachines/play-vue 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.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +295 -83
  3. package/dist/ActorProvider.js +8 -0
  4. package/dist/ActorProvider.js.map +1 -0
  5. package/dist/ActorProvider.vue.d.ts +51 -0
  6. package/dist/ActorProvider.vue.d.ts.map +1 -0
  7. package/dist/ActorProvider.vue_vue_type_script_lang.js +104 -0
  8. package/dist/ActorProvider.vue_vue_type_script_lang.js.map +1 -0
  9. package/dist/PlayRenderer.js +2 -4
  10. package/dist/PlayRenderer.js.map +1 -1
  11. package/dist/PlayRenderer.vue.d.ts +5 -0
  12. package/dist/PlayRenderer.vue.d.ts.map +1 -0
  13. package/dist/PlayRenderer.vue_vue_type_script_lang.js +10 -27
  14. package/dist/PlayRenderer.vue_vue_type_script_lang.js.map +1 -1
  15. package/dist/PlayUIProvider.js +7 -0
  16. package/dist/PlayUIProvider.js.map +1 -0
  17. package/dist/PlayUIProvider.vue.d.ts +76 -0
  18. package/dist/PlayUIProvider.vue.d.ts.map +1 -0
  19. package/dist/PlayUIProvider.vue_vue_type_script_lang.js +86 -0
  20. package/dist/PlayUIProvider.vue_vue_type_script_lang.js.map +1 -0
  21. package/dist/actor-provider-context.d.ts +40 -0
  22. package/dist/actor-provider-context.d.ts.map +1 -0
  23. package/dist/actor-provider-context.js +11 -0
  24. package/dist/actor-provider-context.js.map +1 -0
  25. package/dist/define-registry.d.ts +79 -0
  26. package/dist/define-registry.d.ts.map +1 -0
  27. package/dist/define-registry.js +25 -0
  28. package/dist/define-registry.js.map +1 -0
  29. package/dist/index.d.ts +21 -2
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +8 -2
  32. package/dist/types.d.ts +17 -12
  33. package/dist/types.d.ts.map +1 -1
  34. package/dist/useActor.d.ts +35 -0
  35. package/dist/useActor.d.ts.map +1 -0
  36. package/dist/useActor.js +14 -0
  37. package/dist/useActor.js.map +1 -0
  38. package/package.json +28 -14
  39. package/dist/_virtual/_plugin-vue_export-helper.js +0 -8
  40. package/dist/index.css +0 -2
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mikael Karon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,137 +1,349 @@
1
1
  # @xmachines/play-vue
2
2
 
3
- Vue renderer component for XMachines Play architecture.
3
+ **Vue 3 renderer for XMachines Play Architecture**
4
4
 
5
- This package keeps Vue as passive infrastructure: it observes actor signals and forwards events, but does not enforce business policy.
5
+ Bridges TC39 Signal-driven actors to Vue's reactivity. Business logic stays in the actor; Vue is purely a rendering target.
6
+
7
+ ## Overview
8
+
9
+ `@xmachines/play-vue` provides `PlayRenderer`, a Vue component that:
10
+
11
+ - Subscribes to `actor.currentView` (TC39 Signal) and re-renders on every state transition
12
+ - Renders the current view's JSON spec via `@json-render/vue`
13
+ - Routes action names from spec elements to `actor.send()` via the `actions` prop
14
+ - Manages per-view UI state in an `@xstate/store` atom (automatic or caller-supplied)
15
+
16
+ Per [Play RFC](../docs/rfc/play.md):
17
+
18
+ - **Actor Authority (INV-01):** Guards in the machine decide all state transitions
19
+ - **Passive Infrastructure (INV-04):** Vue observes signals and dispatches events — never decides
20
+ - **Signal-Only Reactivity (INV-05):** `actor.currentView` signal is the sole render trigger
6
21
 
7
22
  ## Installation
8
23
 
9
24
  ```bash
10
- npm install @xmachines/play-vue vue
25
+ npm install @xmachines/play-vue
26
+ npm install @json-render/vue @json-render/core # peer deps
27
+ npm install @json-render/xstate @xstate/store # store integration
11
28
  ```
12
29
 
30
+ In this monorepo, the root install applies a `patch-package` patch to `@json-render/vue`
31
+ so `defineRegistry(..., { onRenderError })` can intercept inner element-boundary errors
32
+ without muting console output.
33
+
13
34
  ## Current Exports
14
35
 
15
- - `PlayRenderer` (Vue component)
16
- - `PlayRendererProps` (type)
36
+ - `PlayRenderer` — main renderer component (Vue SFC)
37
+ - `useActor` — composable for accessing the actor inside a `PlayRenderer` tree
38
+ - `defineRegistry` — SFC-aware wrapper; auto-wraps `.vue` SFCs via `h(SFC, ctx)`
39
+ - `useBoundProp` — re-exported from `@json-render/vue`
40
+ - `ComponentFn` (type) — re-exported from `@json-render/vue`
41
+ - `ComponentContext` (type) — re-exported from `@json-render/vue`
42
+ - `ActorProvider` — escape hatch primitive (owns actor bridging, signal bridge, store lifecycle)
43
+ - `PlayUIProvider` — batteries-included composite (wraps `ActorProvider` + `JSONUIProvider`)
44
+ - `getPlayViewContext` — composable for accessing the current view spec inside a provider tree
45
+ - `RenderErrorHandler` (type) — inner per-element error callback signature
46
+ - `ActorProviderProps` (type)
47
+ - `ViewContextValue` (type)
48
+ - `PlayActor` (type)
49
+
50
+ ## Quick Start
51
+
52
+ ```ts
53
+ // catalog.ts — shared contract
54
+ import { defineCatalog } from "@json-render/core";
55
+ import { z } from "zod";
56
+
57
+ export const catalog = defineCatalog({
58
+ elements: {
59
+ Login: { props: z.object({ title: z.string() }), description: "Login form" },
60
+ Dashboard: { props: z.object({ username: z.string() }), description: "Dashboard" },
61
+ },
62
+ });
17
63
 
18
- ## Usage
64
+ export type Catalog = typeof catalog;
65
+ ```
19
66
 
20
67
  ```vue
68
+ <!-- Login.vue — Vue SFC; useBoundProp works in <script setup> -->
21
69
  <script setup lang="ts">
22
- import { PlayRenderer } from "@xmachines/play-vue";
23
- import { definePlayer } from "@xmachines/play-xstate";
24
- import { defineCatalog } from "@xmachines/play-catalog";
70
+ import { useBoundProp } from "@xmachines/play-vue";
71
+ import type { ComponentContext } from "@xmachines/play-vue";
72
+ import type { Catalog } from "./catalog.js";
73
+
74
+ const { props, emit, bindings } = defineProps<ComponentContext<Catalog, "Login">>();
75
+ const [username, setUsername] = useBoundProp<string>(bindings?.username ?? "/username");
76
+ </script>
77
+
78
+ <template>
79
+ <div class="view">
80
+ <h2>{{ props.title }}</h2>
81
+ <form @submit.prevent="emit('submit')">
82
+ <input id="username" v-model="username" @input="setUsername(username)" />
83
+ <button type="submit">Log In</button>
84
+ </form>
85
+ </div>
86
+ </template>
87
+ ```
25
88
 
26
- // Define catalog
27
- const catalog = defineCatalog({
28
- home: { component: "Home", props: { title: "string" } },
29
- login: { component: "Login", props: { error: "string?" } },
30
- dashboard: { component: "Dashboard", props: { userId: "string" } },
89
+ ```ts
90
+ // registry.ts pass SFCs directly; defineRegistry auto-wraps them
91
+ import { defineRegistry } from "@xmachines/play-vue";
92
+ import { catalog } from "./catalog.js";
93
+ import LoginSFC from "./Login.vue";
94
+ import DashboardSFC from "./Dashboard.vue";
95
+
96
+ export const registryResult = defineRegistry(catalog, {
97
+ components: { Login: LoginSFC, Dashboard: DashboardSFC },
98
+ actions: {
99
+ login: async (params) => {
100
+ if (!params) return;
101
+ actor.send({ type: "auth.login", username: params.username });
102
+ },
103
+ logout: async (params) => {
104
+ actor.send({ type: "auth.logout" });
105
+ },
106
+ },
31
107
  });
108
+ ```
32
109
 
33
- // Create actor
34
- const actor = definePlayer({ machine: authMachine, catalog })();
35
- actor.start();
110
+ ```ts
111
+ // machine.ts
112
+ import { setup, assign } from "xstate";
113
+ import { formatPlayRouteTransitions } from "@xmachines/play-xstate";
114
+
115
+ export const machine = setup({
116
+ types: {
117
+ context: {} as {
118
+ isAuthenticated: boolean;
119
+ username: string | null;
120
+ params: Record<string, string>;
121
+ query: Record<string, string>;
122
+ },
123
+ events: {} as
124
+ | { type: "auth.login"; username: string }
125
+ | { type: "auth.logout" }
126
+ | { type: "play.route"; to: string; params?: Record<string, string> },
127
+ },
128
+ }).createMachine(
129
+ formatPlayRouteTransitions({
130
+ id: "app",
131
+ initial: "login",
132
+ context: { isAuthenticated: false, username: null, params: {}, query: {} },
133
+ states: {
134
+ login: {
135
+ id: "login",
136
+ meta: {
137
+ route: "/login",
138
+ view: {
139
+ root: "root",
140
+ elements: {
141
+ root: { type: "Login", props: { title: "Sign In" }, children: [] },
142
+ },
143
+ },
144
+ },
145
+ },
146
+ dashboard: {
147
+ id: "dashboard",
148
+ meta: {
149
+ route: "/dashboard",
150
+ view: {
151
+ root: "root",
152
+ elements: {
153
+ root: { type: "Dashboard", props: { username: "" }, children: [] },
154
+ },
155
+ },
156
+ },
157
+ },
158
+ },
159
+ on: {
160
+ "auth.login": {
161
+ target: ".dashboard",
162
+ guard: ({ context }) => !context.isAuthenticated,
163
+ actions: assign({ isAuthenticated: true, username: ({ event }) => event.username }),
164
+ },
165
+ "auth.logout": {
166
+ target: ".login",
167
+ guard: ({ context }) => context.isAuthenticated,
168
+ actions: assign({ isAuthenticated: false, username: null }),
169
+ },
170
+ },
171
+ }),
172
+ );
173
+ ```
174
+
175
+ ```vue
176
+ <!-- App.vue -->
177
+ <script setup lang="ts">
178
+ import { definePlayer } from "@xmachines/play-xstate";
179
+ import { PlayUIProvider, PlayRenderer } from "@xmachines/play-vue";
180
+ import { machine } from "./machine.js";
181
+ import { registryResult } from "./registry.js";
36
182
 
37
- // Define component map
38
- const components = {
39
- Home: HomeComponent,
40
- Login: LoginComponent,
41
- Dashboard: DashboardComponent,
42
- };
183
+ const createPlayer = definePlayer({ machine });
184
+ const actor = createPlayer();
185
+ actor.start();
43
186
  </script>
44
187
 
45
188
  <template>
46
- <PlayRenderer :actor="actor" :components="components">
47
- <template #fallback>
48
- <div>Loading...</div>
49
- </template>
50
- </PlayRenderer>
189
+ <PlayUIProvider :actor="actor" :registryResult="registryResult">
190
+ <PlayRenderer />
191
+ </PlayUIProvider>
51
192
  </template>
52
193
  ```
53
194
 
54
- ## API
195
+ ## API Reference
55
196
 
56
- ### PlayRenderer
197
+ ### `PlayUIProvider`
57
198
 
58
- Main renderer component that observes `actor.currentView` signal and dynamically renders catalog components.
199
+ Batteries-included composite provider. Wraps `ActorProvider` + `JSONUIProvider`. Pass `actor` and `registryResult` here, then place `<PlayRenderer />` inside as a zero-prop child.
59
200
 
60
- **Props:**
201
+ ```vue
202
+ <PlayUIProvider
203
+ :actor="actor"
204
+ :registryResult="registryResult"
205
+ :store="myStore"
206
+ :onRenderError="(error, elementType) => console.warn(`<${elementType}> crashed:`, error)"
207
+ >
208
+ <template #fallback><p>Something went wrong.</p></template>
209
+ <PlayRenderer />
210
+ </PlayUIProvider>
211
+ ```
61
212
 
62
- - `actor` (required) - Actor instance with `currentView` signal
63
- - `components` (required) - Map of component names to Vue components
64
- - `fallback` (optional) - Slot shown when `currentView` is null
213
+ **`actor`** — A `PlayerActor` (or any `AbstractActor & Viewable`). Provides the `currentView` signal.
65
214
 
66
- **Component Props:**
215
+ **`registryResult`** — The full `DefineRegistryResult` returned by `defineRegistry(catalog, { components, actions })` from `@xmachines/play-vue`. Pass `.vue` SFCs directly — they are auto-wrapped via `h(SFC, ctx)` so `useBoundProp` and other composables work inside `<script setup>`.
67
216
 
68
- Each rendered component receives:
217
+ **`store`** (optional) Controls per-view UI state (`$state` bindings, form values):
69
218
 
70
- - All props from `view.props` (spread via `v-bind`)
71
- - `send` function for sending events to actor
219
+ - **Omitted (uncontrolled, default):** A fresh `@xstate/store` atom is created per view transition, seeded from `view.spec.state`.
220
+ - **Provided (controlled):** The caller owns the store; `spec.state` is ignored.
72
221
 
73
- **Example Component:**
222
+ ```ts
223
+ import { createAtom } from "@xstate/store";
224
+ import { xstateStoreStateStore } from "@json-render/xstate";
225
+ import type { StateStore } from "@json-render/core";
226
+
227
+ const store: StateStore = xstateStoreStateStore({ atom: createAtom({ username: "" }) });
228
+ ```
74
229
 
75
230
  ```vue
76
- <script setup lang="ts">
77
- import type { AbstractActor } from "@xmachines/play-actor";
231
+ <PlayUIProvider :actor="actor" :registryResult="registryResult" :store="store">
232
+ <PlayRenderer />
233
+ </PlayUIProvider>
234
+ ```
78
235
 
79
- defineProps<{
80
- userId: string;
81
- send: AbstractActor<any>["send"];
82
- }>();
236
+ **`onRenderError`** — Called when an individual catalog component throws during render. Caught by `@json-render/vue`'s inner per-element error boundary — the failed component is silently removed while the rest of the spec continues rendering. When both `onRenderError` on `PlayUIProvider` and on `defineRegistry` are set, the prop wins.
83
237
 
84
- function handleClick() {
85
- send({ type: "user.click", payload: { action: "details" } });
86
- }
87
- </script>
238
+ ---
88
239
 
89
- <template>
90
- <div>
91
- <h1>User: {{ userId }}</h1>
92
- <button @click="handleClick">View Details</button>
93
- </div>
94
- </template>
240
+ ### `ActorProvider`
241
+
242
+ Escape hatch primitive. Owns actor bridging, signal bridge, and store lifecycle. Use this when you need direct control over the provider layer.
243
+
244
+ ```vue
245
+ <ActorProvider :actor="actor" :registryResult="registryResult" :onRenderError="handleError">
246
+ <!-- your own JSONUIProvider + PlayRenderer tree -->
247
+ </ActorProvider>
248
+ ```
249
+
250
+ ---
251
+
252
+ ### `PlayRenderer`
253
+
254
+ Zero-prop leaf component. Must be rendered inside a `PlayUIProvider` (or `ActorProvider`) tree. Subscribes to `actor.currentView` via context and renders the current spec.
255
+
256
+ ```vue
257
+ <PlayUIProvider :actor="actor" :registryResult="registryResult">
258
+ <PlayRenderer />
259
+ </PlayUIProvider>
95
260
  ```
96
261
 
97
- ## Features
262
+ `PlayRenderer` accepts no props — all configuration (`actor`, `registryResult`, `store`, `fallback`, `onRenderError`) is provided by the enclosing `PlayUIProvider` or `ActorProvider`.
98
263
 
99
- - **Signal Bridge:** Uses TC39 Signal.subtle.Watcher to bridge actor signals to Vue reactivity
100
- - **Dynamic Rendering:** Renders components based on `actor.currentView.component` string
101
- - **Type Safe:** Full TypeScript support with generic type inference
102
- - **Error Handling:** Gracefully handles missing components and null catalogs
103
- - **One-Shot Re-Watch:** Implements proper signal watcher pattern with microtask batching
264
+ ## Error handling
104
265
 
105
- ## Canonical Watcher Lifecycle
266
+ The provider tree has two layers of error boundaries:
106
267
 
107
- Use the same watcher flow as other adapters/packages:
268
+ ### Outer boundary `fallback` slot
108
269
 
109
- 1. `notify`
110
- 2. `queueMicrotask`
111
- 3. `getPending()`
112
- 4. read actor signals and update framework-local state
113
- 5. re-arm with `watch(...)` or `watch()`
270
+ Vue's `onErrorCaptured` wraps the entire renderer. Triggered when the spec or store setup throws, or when the inner boundary is not present. Use Vue's built-in `onErrorCaptured` in a parent component for observability.
114
271
 
115
- Watcher notify is one-shot. Re-arm is required for continuous observation.
272
+ ```vue
273
+ <PlayUIProvider :actor="actor" :registryResult="registryResult">
274
+ <template #fallback>
275
+ <p>Something went wrong.</p>
276
+ </template>
277
+ <PlayRenderer />
278
+ </PlayUIProvider>
279
+ ```
116
280
 
117
- ## Cleanup Contract
281
+ ### Inner boundary — `onRenderError`
118
282
 
119
- Cleanup must be explicit:
283
+ Each catalog element is individually wrapped in an error boundary by `@json-render/vue`. When a component throws, it is silently removed while the rest of the spec continues rendering. The outer boundary is **not** triggered.
120
284
 
121
- - Call `unwatch(...)` during Vue lifecycle teardown (`onUnmounted`).
122
- - Treat renderer/provider disposal as deterministic teardown, not GC-only cleanup.
123
- - Keep actor-driven routing and view decisions in the actor layer.
285
+ Pass `onRenderError` to `PlayUIProvider` (or `ActorProvider`) overrides any registry-level handler — or bake it into `defineRegistry`:
124
286
 
125
- ## Architecture
287
+ ```vue
288
+ <!-- via PlayUIProvider prop -->
289
+ <PlayUIProvider
290
+ :actor="actor"
291
+ :registryResult="registryResult"
292
+ :onRenderError="(error, elementType) => console.warn(`<${elementType}> crashed:`, error)"
293
+ >
294
+ <PlayRenderer />
295
+ </PlayUIProvider>
296
+ ```
126
297
 
127
- PlayRenderer follows the XMachines Play architecture principles:
298
+ ```ts
299
+ // via defineRegistry — bakes the handler into the registry
300
+ const registryResult = defineRegistry(catalog, {
301
+ components: { Login: LoginSFC, Dashboard: DashboardSFC },
302
+ actions: { login: async (params) => { ... }, logout: async () => { ... } },
303
+ onRenderError(error, elementType) {
304
+ reportExpectedRenderError(error, elementType);
305
+ },
306
+ });
307
+ ```
308
+
309
+ `onRenderError` is typed as `RenderErrorHandler` and exported from `@xmachines/play-vue`.
310
+
311
+ ---
312
+
313
+ ### `useActor`
314
+
315
+ Vue composable for accessing the actor from inside any component rendered by `PlayRenderer`.
316
+
317
+ ```ts
318
+ import { useActor } from "@xmachines/play-vue";
319
+
320
+ // Inside any component rendered inside PlayRenderer:
321
+ const actor = useActor();
322
+ actor.send({ type: "auth.logout" });
323
+ ```
324
+
325
+ Throws `NonNullableError: "useActor() must be called inside <ActorProvider> (or <PlayUIProvider>)"` if called outside the tree.
326
+
327
+ ---
328
+
329
+ ## Route Parameters in Props
330
+
331
+ When using `formatPlayRouteTransitions`, URL path parameters flow automatically into component props. Declare an `undefined` slot in the spec to opt in:
332
+
333
+ ```ts
334
+ // spec: { section: undefined, user: "alice" }
335
+ // After play.route to /settings/profile → context.params = { section: "profile" }
336
+ // Component receives: { section: "profile", user: "alice" }
337
+ ```
128
338
 
129
- 1. **Actor Authority:** Actor decides all state transitions
130
- 2. **Passive Infrastructure:** Renderer observes signals, sends events
131
- 3. **Signal-Only Reactivity:** Business logic state lives in actor signals
339
+ Priority: **route param fills `undefined` slots; explicit non-`undefined` spec props always win.**
132
340
 
133
- Signals are reactivity substrate only. They are not a business mutation channel.
341
+ ---
134
342
 
135
- ## License
343
+ ## Architecture Notes
136
344
 
137
- MIT
345
+ - Vue reactivity is only used to trigger re-renders — not for business logic
346
+ - `actor.currentView` (TC39 Signal) is bridged to Vue's reactive system inside `PlayRenderer`
347
+ - Per-view UI state lives in an `@xstate/store` atom, not in Vue reactive state
348
+ - `@json-render/vue` drives rendering; `PlayRenderer` is the signal bridge — import `defineRegistry`, `ComponentFn`, `ComponentContext`, and `useBoundProp` from `@xmachines/play-vue`
349
+ - Vue views should be `.vue` SFCs using `ComponentContext<MyCatalog, "X">` — `defineRegistry` from `@xmachines/play-vue` auto-wraps them via `h(SFC, ctx)`, giving each SFC its own `setup()` context where `useBoundProp` and Vue composables work correctly
@@ -0,0 +1,8 @@
1
+ import "./actor-provider-context.js";
2
+ import e from "./ActorProvider.vue_vue_type_script_lang.js";
3
+ //#region src/ActorProvider.vue
4
+ var t = e;
5
+ //#endregion
6
+ export { t as default };
7
+
8
+ //# sourceMappingURL=ActorProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActorProvider.js","names":[],"sources":["../src/ActorProvider.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * ActorProvider — escape-hatch primitive for the XMachines Vue render architecture.\n *\n * Owns the full actor lifecycle:\n * - Signal subscription (watchSignal) bridging TC39 Signals to Vue reactivity\n * - Per-view StateStore lifecycle (controlled/uncontrolled)\n * - Handler resolution via ActorProviderInner (must be inside StateProvider)\n * - ViewContextValue provision via ViewKey injection key\n * - ActionProvider + VisibilityProvider wrapping for downstream Renderer\n * - onRenderError injection into registry\n *\n * Library authors who need fine-grained control use this directly.\n * End users should use <PlayUIProvider> instead.\n *\n * @invariant Actor Authority - Actor decides all state transitions via guards\n * @invariant Passive Infrastructure - Component observes signals, sends events\n * @invariant Signal-Only Reactivity - Business logic state lives in actor signals\n */\n\nimport { defineComponent, ref, toRaw, markRaw, onUnmounted, h, provide, shallowRef } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { watchSignal } from \"@xmachines/play-signals\";\nimport type { AbstractActor, Viewable, PlaySpec } from \"@xmachines/play-actor\";\nimport type { AnyActorLogic } from \"xstate\";\nimport type { DefineRegistryResult, SetState, RenderErrorHandler } from \"@json-render/vue\";\n\nimport { StateProvider, useStateStore, ActionProvider, VisibilityProvider } from \"@json-render/vue\";\nimport type { StateStore } from \"@json-render/core\";\nimport { createAtom } from \"@xstate/store\";\nimport { xstateStoreStateStore } from \"@json-render/xstate\";\nimport { provideActor, type PlayActor } from \"./useActor.js\";\nimport { ViewKey, type ViewContextValue } from \"./actor-provider-context.js\";\nimport type { ActorProviderProps } from \"./types.js\";\n\n// Re-export props type and context accessors for consumers who import from this file\nexport type { ActorProviderProps } from \"./types.js\";\nexport { getPlayViewContext } from \"./actor-provider-context.js\";\nexport type { ViewContextValue } from \"./actor-provider-context.js\";\n\n// ---------------------------------------------------------------------------\n// ActorProviderInner — renders inside StateProvider to call useStateStore()\n// Provides ViewContextValue + ActionProvider + VisibilityProvider for downstream Renderer.\n// ---------------------------------------------------------------------------\n\nconst ActorProviderInner = defineComponent({\n\tname: \"ActorProviderInner\",\n\tprops: {\n\t\tregistryResult: {\n\t\t\ttype: Object as PropType<DefineRegistryResult>,\n\t\t\trequired: true,\n\t\t},\n\t\tspec: {\n\t\t\ttype: Object as PropType<PlaySpec | null>,\n\t\t\tdefault: null,\n\t\t},\n\t\tstore: {\n\t\t\ttype: Object as PropType<StateStore>,\n\t\t\trequired: true,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\t// Use shallowRef for the view context value to avoid deep reactivity overhead.\n\t\t// The Proxy below allows inject() to always read the latest value.\n\t\tconst viewRef = shallowRef<ViewContextValue | null>(null);\n\n\t\t// Provide the ViewContextValue via Vue's inject/provide system.\n\t\t// Called synchronously in setup() so Vue registers it on the component instance.\n\t\t// Uses Proxy so descendants always receive the latest viewRef.value.\n\t\tprovide(\n\t\t\tViewKey,\n\t\t\tnew Proxy({} as ViewContextValue, {\n\t\t\t\tget(_target, prop: string) {\n\t\t\t\t\treturn viewRef.value?.[prop as keyof ViewContextValue];\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\n\t\t// Call useStateStore() at setup() time (synchronous, before return).\n\t\t// Vue composables that use inject() must be called during setup(), not in a render function.\n\t\tconst { update, getSnapshot } = useStateStore();\n\n\t\t// Build a SetState adapter: handlers factory expects updater-function pattern\n\t\tconst setStateAdapter: SetState = (updater) => {\n\t\t\tconst prev = getSnapshot();\n\t\t\tupdate(updater(prev));\n\t\t};\n\n\t\treturn () => {\n\t\t\tif (!props.spec) {\n\t\t\t\tviewRef.value = null;\n\t\t\t\treturn slots.default?.() ?? null;\n\t\t\t}\n\n\t\t\tconst handlers = props.registryResult.handlers(\n\t\t\t\t() => setStateAdapter,\n\t\t\t\t() => getSnapshot(),\n\t\t\t);\n\n\t\t\tviewRef.value = {\n\t\t\t\tspec: props.spec,\n\t\t\t\thandlers,\n\t\t\t\tregistry: props.registryResult.registry,\n\t\t\t\tstore: props.store,\n\t\t\t};\n\n\t\t\t// Wrap with ActionProvider + VisibilityProvider so PlayRenderer's Renderer works\n\t\t\t// even when ActorProvider is used directly (without PlayUIProvider / JSONUIProvider)\n\t\t\treturn h(ActionProvider, { handlers }, () =>\n\t\t\t\th(VisibilityProvider, {}, () => slots.default?.() ?? null),\n\t\t\t);\n\t\t};\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// ActorProvider — main export\n// ---------------------------------------------------------------------------\n\nexport default defineComponent({\n\tname: \"ActorProvider\",\n\tprops: {\n\t\tactor: {\n\t\t\ttype: Object as PropType<ActorProviderProps[\"actor\"]>,\n\t\t\trequired: true,\n\t\t},\n\t\tregistryResult: {\n\t\t\ttype: Object as PropType<DefineRegistryResult>,\n\t\t\trequired: true,\n\t\t},\n\t\tstore: {\n\t\t\ttype: Object as PropType<StateStore>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tonRenderError: {\n\t\t\ttype: Function as PropType<RenderErrorHandler>,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\t// Unwrap actor from Vue's reactive proxy to access raw Signal objects\n\t\tconst actor = toRaw(props.actor as AbstractActor<AnyActorLogic> & Viewable);\n\n\t\t// Unwrap the registryResult and mark components as raw to avoid Vue reactivity overhead\n\t\tconst rawRegistryResult: DefineRegistryResult = {\n\t\t\t...toRaw(props.registryResult),\n\t\t\tregistry: markRaw(\n\t\t\t\tObject.fromEntries(\n\t\t\t\t\tObject.entries(toRaw(props.registryResult).registry).map(([k, v]) => [\n\t\t\t\t\t\tk,\n\t\t\t\t\t\tmarkRaw(v as object),\n\t\t\t\t\t]),\n\t\t\t\t) as DefineRegistryResult[\"registry\"],\n\t\t\t),\n\t\t};\n\n\t\t// Inject onRenderError prop into registry (non-enumerable, overrides defineRegistry-level handler)\n\t\tif (props.onRenderError) {\n\t\t\tObject.defineProperty(rawRegistryResult.registry, \"onRenderError\", {\n\t\t\t\tvalue: props.onRenderError,\n\t\t\t\tenumerable: false,\n\t\t\t\tconfigurable: true,\n\t\t\t});\n\t\t}\n\n\t\t// Provide the raw actor to all descendants via Vue's provide/inject mechanism\n\t\tprovideActor(actor as PlayActor);\n\n\t\t// Seed initial value then subscribe — both synchronous, no scheduler gap.\n\t\t// This mirrors the atomic seed+watch pattern used in Solid (createEffect)\n\t\t// and Svelte ($effect) for cross-framework consistency.\n\t\tconst view = ref<PlaySpec | null>(actor.currentView.get());\n\n\t\t// Internal per-view store — recreated on each view transition when no external store\n\t\tlet internalStore: StateStore | null = null;\n\t\tlet lastView: PlaySpec | null = null;\n\t\t// storeKey is only incremented in the internalStore branch (not when props.store is set)\n\t\tlet storeKey = 0;\n\n\t\t// Signal watcher for bridging TC39 Signals to Vue reactivity\n\t\tconst unwatch = watchSignal(actor.currentView, (nextView) => {\n\t\t\tview.value = nextView;\n\t\t});\n\n\t\tonUnmounted(() => {\n\t\t\tunwatch();\n\t\t});\n\n\t\treturn () => {\n\t\t\t// No view — show fallback slot or nothing\n\t\t\tif (!view.value) {\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\n\t\t\tconst spec = view.value;\n\n\t\t\t// Resolve the store: external (controlled) or internal per-view atom\n\t\t\tlet store: StateStore;\n\t\t\tif (props.store) {\n\t\t\t\tstore = props.store;\n\t\t\t} else {\n\t\t\t\tif (internalStore === null || lastView !== view.value) {\n\t\t\t\t\t// T-37-04-02: Proper proto-safe guard for spec.state\n\t\t\t\t\tconst rawState = spec.state;\n\t\t\t\t\tconst initialState: Record<string, unknown> =\n\t\t\t\t\t\trawState !== null &&\n\t\t\t\t\t\ttypeof rawState === \"object\" &&\n\t\t\t\t\t\t!Array.isArray(rawState) &&\n\t\t\t\t\t\t(Object.getPrototypeOf(rawState) === Object.prototype ||\n\t\t\t\t\t\t\tObject.getPrototypeOf(rawState) === null)\n\t\t\t\t\t\t\t? (rawState as Record<string, unknown>)\n\t\t\t\t\t\t\t: {};\n\t\t\t\t\tinternalStore = xstateStoreStateStore({ atom: createAtom(initialState) });\n\t\t\t\t\tlastView = view.value;\n\t\t\t\t\tstoreKey++;\n\t\t\t\t}\n\t\t\t\tstore = internalStore;\n\t\t\t}\n\n\t\t\t// ActorProviderInner renders inside StateProvider so useStateStore() works\n\t\t\treturn h(StateProvider, { store, key: storeKey }, () =>\n\t\t\t\th(ActorProviderInner, { registryResult: rawRegistryResult, spec, store }, slots),\n\t\t\t);\n\t\t};\n\t},\n});\n</script>\n"],"mappings":""}
@@ -0,0 +1,51 @@
1
+ import { PropType } from 'vue';
2
+ import { DefineRegistryResult, RenderErrorHandler } from '@json-render/vue';
3
+ import { StateStore } from '@json-render/core';
4
+ import { ActorProviderProps } from './types.js';
5
+ export type { ActorProviderProps } from './types.js';
6
+ export { getPlayViewContext } from './actor-provider-context.js';
7
+ export type { ViewContextValue } from './actor-provider-context.js';
8
+ declare const _default: import('vue', { with: { "resolution-mode": "import" } }).DefineComponent<import('vue', { with: { "resolution-mode": "import" } }).ExtractPropTypes<{
9
+ actor: {
10
+ type: PropType<ActorProviderProps["actor"]>;
11
+ required: true;
12
+ };
13
+ registryResult: {
14
+ type: PropType<DefineRegistryResult>;
15
+ required: true;
16
+ };
17
+ store: {
18
+ type: PropType<StateStore>;
19
+ default: undefined;
20
+ };
21
+ onRenderError: {
22
+ type: PropType<RenderErrorHandler>;
23
+ default: undefined;
24
+ };
25
+ }>, () => import('vue', { with: { "resolution-mode": "import" } }).VNode<import('vue', { with: { "resolution-mode": "import" } }).RendererNode, import('vue', { with: { "resolution-mode": "import" } }).RendererElement, {
26
+ [key: string]: any;
27
+ }> | import('vue', { with: { "resolution-mode": "import" } }).VNode<import('vue', { with: { "resolution-mode": "import" } }).RendererNode, import('vue', { with: { "resolution-mode": "import" } }).RendererElement, {
28
+ [key: string]: any;
29
+ }>[] | null, {}, {}, {}, import('vue', { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import('vue', { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {}, string, import('vue', { with: { "resolution-mode": "import" } }).PublicProps, Readonly<import('vue', { with: { "resolution-mode": "import" } }).ExtractPropTypes<{
30
+ actor: {
31
+ type: PropType<ActorProviderProps["actor"]>;
32
+ required: true;
33
+ };
34
+ registryResult: {
35
+ type: PropType<DefineRegistryResult>;
36
+ required: true;
37
+ };
38
+ store: {
39
+ type: PropType<StateStore>;
40
+ default: undefined;
41
+ };
42
+ onRenderError: {
43
+ type: PropType<RenderErrorHandler>;
44
+ default: undefined;
45
+ };
46
+ }>> & Readonly<{}>, {
47
+ store: StateStore;
48
+ onRenderError: RenderErrorHandler;
49
+ }, {}, {}, {}, string, import('vue', { with: { "resolution-mode": "import" } }).ComponentProvideOptions, true, {}, any>;
50
+ export default _default;
51
+ //# sourceMappingURL=ActorProvider.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActorProvider.vue.d.ts","sourceRoot":"","sources":["../src/ActorProvider.vue"],"names":[],"mappings":"AAyPA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAIpC,OAAO,KAAK,EAAE,oBAAoB,EAAY,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAKpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrD,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;;;cAqFjD,QAAQ,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;;;;cAIrC,QAAQ,CAAC,oBAAoB,CAAC;;;;cAI9B,QAAQ,CAAC,UAAU,CAAC;;;;cAIlB,QAAQ,CAAC,kBAAkB,CAAC;;;;;;;;;cAZ9B,QAAQ,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;;;;cAIrC,QAAQ,CAAC,oBAAoB,CAAC;;;;cAI9B,QAAQ,CAAC,UAAU,CAAC;;;;cAIlB,QAAQ,CAAC,kBAAkB,CAAC;;;;;;;AAhBjD,wBA0GG"}
@@ -0,0 +1,104 @@
1
+ import { ViewKey as e } from "./actor-provider-context.js";
2
+ import { provideActor as t } from "./useActor.js";
3
+ import { defineComponent as n, h as r, markRaw as i, onUnmounted as a, provide as o, ref as s, shallowRef as c, toRaw as l } from "vue";
4
+ import { ActionProvider as u, StateProvider as d, VisibilityProvider as f, useStateStore as p } from "@json-render/vue";
5
+ import { watchSignal as m } from "@xmachines/play-signals";
6
+ import { createAtom as h } from "@xstate/store";
7
+ import { xstateStoreStateStore as g } from "@json-render/xstate";
8
+ //#region src/ActorProvider.vue?vue&type=script&lang.ts
9
+ var _ = n({
10
+ name: "ActorProviderInner",
11
+ props: {
12
+ registryResult: {
13
+ type: Object,
14
+ required: !0
15
+ },
16
+ spec: {
17
+ type: Object,
18
+ default: null
19
+ },
20
+ store: {
21
+ type: Object,
22
+ required: !0
23
+ }
24
+ },
25
+ setup(t, { slots: n }) {
26
+ let i = c(null);
27
+ o(e, new Proxy({}, { get(e, t) {
28
+ return i.value?.[t];
29
+ } }));
30
+ let { update: a, getSnapshot: s } = p(), l = (e) => {
31
+ a(e(s()));
32
+ };
33
+ return () => {
34
+ if (!t.spec) return i.value = null, n.default?.() ?? null;
35
+ let e = t.registryResult.handlers(() => l, () => s());
36
+ return i.value = {
37
+ spec: t.spec,
38
+ handlers: e,
39
+ registry: t.registryResult.registry,
40
+ store: t.store
41
+ }, r(u, { handlers: e }, () => r(f, {}, () => n.default?.() ?? null));
42
+ };
43
+ }
44
+ }), v = n({
45
+ name: "ActorProvider",
46
+ props: {
47
+ actor: {
48
+ type: Object,
49
+ required: !0
50
+ },
51
+ registryResult: {
52
+ type: Object,
53
+ required: !0
54
+ },
55
+ store: {
56
+ type: Object,
57
+ default: void 0
58
+ },
59
+ onRenderError: {
60
+ type: Function,
61
+ default: void 0
62
+ }
63
+ },
64
+ setup(e, { slots: n }) {
65
+ let o = l(e.actor), c = {
66
+ ...l(e.registryResult),
67
+ registry: i(Object.fromEntries(Object.entries(l(e.registryResult).registry).map(([e, t]) => [e, i(t)])))
68
+ };
69
+ e.onRenderError && Object.defineProperty(c.registry, "onRenderError", {
70
+ value: e.onRenderError,
71
+ enumerable: !1,
72
+ configurable: !0
73
+ }), t(o);
74
+ let u = s(o.currentView.get()), f = null, p = null, v = 0, y = m(o.currentView, (e) => {
75
+ u.value = e;
76
+ });
77
+ return a(() => {
78
+ y();
79
+ }), () => {
80
+ if (!u.value) return n.fallback ? n.fallback() : null;
81
+ let t = u.value, i;
82
+ if (e.store) i = e.store;
83
+ else {
84
+ if (f === null || p !== u.value) {
85
+ let e = t.state;
86
+ f = g({ atom: h(typeof e == "object" && e && !Array.isArray(e) && (Object.getPrototypeOf(e) === Object.prototype || Object.getPrototypeOf(e) === null) ? e : {}) }), p = u.value, v++;
87
+ }
88
+ i = f;
89
+ }
90
+ return r(d, {
91
+ store: i,
92
+ key: v
93
+ }, () => r(_, {
94
+ registryResult: c,
95
+ spec: t,
96
+ store: i
97
+ }, n));
98
+ };
99
+ }
100
+ });
101
+ //#endregion
102
+ export { v as default };
103
+
104
+ //# sourceMappingURL=ActorProvider.vue_vue_type_script_lang.js.map