@xmachines/play-vue 3.0.0 → 4.0.0
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.
- package/README.md +14 -31
- package/dist/ActorProvider.js.map +1 -1
- package/dist/ActorProvider.vue_vue_type_script_lang.js +1 -1
- package/dist/ActorProvider.vue_vue_type_script_lang.js.map +1 -1
- package/dist/PlayUIProvider.js.map +1 -1
- package/dist/PlayUIProvider.vue_vue_type_script_lang.js.map +1 -1
- package/dist/actor-provider-context.d.ts +1 -7
- package/dist/actor-provider-context.d.ts.map +1 -1
- package/dist/actor-provider-context.js +1 -7
- package/dist/actor-provider-context.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/types.d.ts +1 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/useActor.d.ts +11 -4
- package/dist/useActor.d.ts.map +1 -1
- package/dist/useActor.js.map +1 -1
- package/package.json +24 -21
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Vue 3 renderer for the XMachines Play Architecture. It observes the actor signals and renders the UI through `@xmachines/json-render-vue`.
|
|
4
4
|
|
|
5
|
-
[](https://opensource.org/licenses/MIT) [](https://opensource.org/licenses/MIT) [](https://www.npmjs.com/package/@xmachines/play-vue)
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -27,7 +27,7 @@ pnpm add @xmachines/play-vue
|
|
|
27
27
|
**Peer dependencies.** Install them with the package:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
pnpm add vue@^3.5.0
|
|
30
|
+
pnpm add vue@^3.5.0 @xstate/store@^3.17.0 @xmachines/json-render-vue@^0.20.0-xm.4 @xmachines/json-render-core@^0.20.0-xm.4 @xmachines/json-render-xstate@^0.20.0-xm.4
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
---
|
|
@@ -44,13 +44,15 @@ pnpm add vue@^3.5.0 xstate@^5.31.0 @xstate/store@^3.17.0 @xmachines/json-render-
|
|
|
44
44
|
|
|
45
45
|
<script setup lang="ts">
|
|
46
46
|
import { defineRegistry, PlayUIProvider, PlayRenderer } from "@xmachines/play-vue";
|
|
47
|
-
import { definePlayer } from "@xmachines/play-xstate";
|
|
47
|
+
import { definePlayer, compose, PlayerActor } from "@xmachines/play-xstate";
|
|
48
|
+
import { withView } from "@xmachines/play-xstate/view";
|
|
48
49
|
import { myMachine } from "./machine.js";
|
|
49
50
|
import { myCatalog } from "./catalog.js";
|
|
50
51
|
import HomeSFC from "./views/Home.vue";
|
|
51
52
|
import LoginSFC from "./views/Login.vue";
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
// A renderer needs the view capability
|
|
55
|
+
const createPlayer = definePlayer({ machine: myMachine, actor: compose(PlayerActor, withView) });
|
|
54
56
|
const actor = createPlayer();
|
|
55
57
|
actor.start();
|
|
56
58
|
|
|
@@ -79,7 +81,7 @@ The composite provider. It wraps `<ActorProvider>` and `JSONUIProvider` in one c
|
|
|
79
81
|
|
|
80
82
|
| Prop | Type | Required | Description |
|
|
81
83
|
| --------------------- | --------------------------------------------- | -------- | ---------------------------------------------------------------------- |
|
|
82
|
-
| `actor` | `
|
|
84
|
+
| `actor` | `PlayActor & Viewable` | ✅ | The XMachines actor instance |
|
|
83
85
|
| `registryResult` | `DefineRegistryResult` | ✅ | Result of `defineRegistry()` |
|
|
84
86
|
| `store` | `StateStore` | — | External controlled state store (optional) |
|
|
85
87
|
| `onRenderError` | `RenderErrorHandler` | — | Error handler for render failures |
|
|
@@ -118,12 +120,12 @@ The leaf component without props. It reads the current `spec` and `registry` fro
|
|
|
118
120
|
|
|
119
121
|
The low-level provider for a custom provider composition. It owns the complete actor lifecycle: the signal subscription, the state store of each view, the handler resolution, and the Vue context. Use `<PlayUIProvider>` when you do not need this control.
|
|
120
122
|
|
|
121
|
-
| Prop | Type
|
|
122
|
-
| ---------------- |
|
|
123
|
-
| `actor` | `
|
|
124
|
-
| `registryResult` | `DefineRegistryResult`
|
|
125
|
-
| `store` | `StateStore`
|
|
126
|
-
| `onRenderError` | `RenderErrorHandler`
|
|
123
|
+
| Prop | Type | Required | Description |
|
|
124
|
+
| ---------------- | ---------------------- | -------- | ------------------------------- |
|
|
125
|
+
| `actor` | `PlayActor & Viewable` | ✅ | The XMachines actor instance |
|
|
126
|
+
| `registryResult` | `DefineRegistryResult` | ✅ | Result of `defineRegistry()` |
|
|
127
|
+
| `store` | `StateStore` | — | External controlled state store |
|
|
128
|
+
| `onRenderError` | `RenderErrorHandler` | — | Override render error handler |
|
|
127
129
|
|
|
128
130
|
---
|
|
129
131
|
|
|
@@ -189,7 +191,7 @@ const view = usePlayView();
|
|
|
189
191
|
|
|
190
192
|
`usePlayView()` returns a **Proxy**: every property read goes to the live context of the provider.
|
|
191
193
|
|
|
192
|
-
> **Note:** `usePlayView` was
|
|
194
|
+
> **Note:** `usePlayView` was named `getPlayViewContext` before. That alias is gone.
|
|
193
195
|
|
|
194
196
|
---
|
|
195
197
|
|
|
@@ -205,25 +207,6 @@ This package re-exports the following, so that a consumer imports everything fro
|
|
|
205
207
|
|
|
206
208
|
---
|
|
207
209
|
|
|
208
|
-
## Testing
|
|
209
|
-
|
|
210
|
-
Run tests for this package in isolation:
|
|
211
|
-
|
|
212
|
-
```bash
|
|
213
|
-
# From the monorepo root
|
|
214
|
-
pnpm --filter @xmachines/play-vue test
|
|
215
|
-
|
|
216
|
-
# Watch mode
|
|
217
|
-
pnpm --filter @xmachines/play-vue run test:watch
|
|
218
|
-
|
|
219
|
-
# With coverage (80% threshold enforced on lines, functions, branches, statements)
|
|
220
|
-
pnpm exec vitest run --coverage --config packages/play-vue/vitest.config.ts
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
The tests use [Vitest](https://vitest.dev/) in a `jsdom` environment. They mount the components with `@vue/test-utils`.
|
|
224
|
-
|
|
225
|
-
---
|
|
226
|
-
|
|
227
210
|
## License
|
|
228
211
|
|
|
229
212
|
MIT — see [LICENSE](LICENSE).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActorProvider.js","names":[],"sources":["../src/ActorProvider.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * ActorProvider — the low-level provider of the XMachines Vue render architecture.\n *\n * It owns the complete actor lifecycle:\n * - the signal subscription (watchSignal), which connects the TC39 Signals to the Vue reactivity\n * - the StateStore lifecycle of each view, controlled and uncontrolled\n * - the resolution of the handlers, through ActorProviderInner, which must be inside StateProvider\n * - the ViewContextValue, which it provides through the ViewKey injection key\n * - the ActionProvider wrapper and the VisibilityProvider wrapper, for the Renderer below\n * - the injection of onRenderError into the registry\n *\n * The author of a library who needs this control uses the component directly.\n * An end user takes <PlayUIProvider> instead.\n *\n * @invariant Actor Authority - the Actor decides each state transition, with its guards\n * @invariant Passive Infrastructure - the component observes the signals, and it sends the events\n * @invariant Signal-Only Reactivity - the state of the business logic is in the actor signals\n */\n\nimport {\n\tdefineComponent,\n\tref,\n\tcomputed,\n\ttoRaw,\n\tmarkRaw,\n\tonUnmounted,\n\tonErrorCaptured,\n\th,\n\tprovide,\n\tshallowRef,\n\twatch,\n\tgetCurrentInstance,\n} from \"vue\";\nimport type { ComponentPublicInstance, PropType } from \"vue\";\nimport { watchSignal } from \"@xmachines/play-signals\";\nimport { canRaiseAgain, failsTheRender, isComponentUpdate } from \"./error-source.js\";\nimport {\n\tattachRenderErrorHandler,\n\tcreateFailureLatch,\n\tcreateReportGuard,\n\tcreateViewStoreLifecycle,\n} from \"@xmachines/play-actor\";\nimport type { AbstractActor, Viewable, PlaySpec } from \"@xmachines/play-actor\";\nimport type { AnyActorLogic } from \"xstate\";\nimport type {\n\tDefineRegistryResult,\n\tSetState,\n\tRenderErrorHandler,\n} from \"@xmachines/json-render-vue\";\n\nimport {\n\tStateProvider,\n\tuseStateStore,\n\tActionProvider,\n\tVisibilityProvider,\n} from \"@xmachines/json-render-vue\";\nimport type { StateStore } from \"@xmachines/json-render-core\";\nimport { createAtom } from \"@xstate/store\";\nimport { xstateStoreStateStore } from \"@xmachines/json-render-xstate\";\nimport { provideActor, type AnyPlayActor } from \"./useActor.js\";\nimport { ViewKey, type ViewContextValue } from \"./actor-provider-context.js\";\nimport type { ActorProviderProps } from \"./types.js\";\n\n// Re-export the props type and the context accessors, for a consumer that imports from this file\nexport type { ActorProviderProps } from \"./types.js\";\nexport { usePlayView, getPlayViewContext } from \"./actor-provider-context.js\"; // getPlayViewContext @deprecated\nexport type { ViewContextValue } from \"./actor-provider-context.js\";\n\n// ---------------------------------------------------------------------------\n// ActorProviderInner — it renders inside StateProvider, so that it can call useStateStore()\n// It gives the ViewContextValue, the ActionProvider, and the VisibilityProvider to the Renderer below.\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// The code uses a shallowRef for the value of the view context, because a deep\n\t\t// reactivity costs too much. The Proxy below lets inject() always read the newest\n\t\t// value.\n\t\tconst viewRef = shallowRef<ViewContextValue | null>(null);\n\n\t\t// Give the ViewContextValue through the inject and provide system of Vue.\n\t\t// The code calls provide synchronously in setup(), so that Vue registers the value on\n\t\t// the instance of the component. It uses a Proxy, so that each descendant always\n\t\t// receives the newest 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() during setup(), which is synchronous and before the return.\n\t\t// A Vue composable that uses inject() must run during setup(), and not in a render\n\t\t// function.\n\t\tconst stateStore = useStateStore();\n\n\t\t// Build a SetState adapter, because the factory of the handlers expects the pattern\n\t\t// of an updater function. The code defines it one time, in setup(). It reads\n\t\t// stateStore.getSnapshot and stateStore.update at the moment of the call. Therefore\n\t\t// it always works on the live state of the store, and it needs no new definition when\n\t\t// the identity of stateStore changes between two renders.\n\t\tconst setStateAdapter: SetState = (updater) => {\n\t\t\tconst prev = stateStore.getSnapshot();\n\t\t\tstateStore.update(updater(prev));\n\t\t};\n\n\t\t// Keep the handlers, with the identity of the registryResult prop as the key. The\n\t\t// getter functions are stable closures, and they hold the stateStore of setup().\n\t\t// Therefore they cause no new computation. The code computes the handlers again only\n\t\t// after a change of the definition of the registry, and not on each render of a view\n\t\t// update.\n\t\tconst handlers = computed(() =>\n\t\t\tprops.registryResult.handlers(\n\t\t\t\t() => setStateAdapter,\n\t\t\t\t() => stateStore.getSnapshot(),\n\t\t\t),\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\tviewRef.value = {\n\t\t\t\tspec: props.spec,\n\t\t\t\thandlers: handlers.value,\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 the children with an ActionProvider and a VisibilityProvider. The Renderer of\n\t\t\t// PlayRenderer therefore works also with ActorProvider alone, without PlayUIProvider\n\t\t\t// and without JSONUIProvider.\n\t\t\treturn h(ActionProvider, { handlers: handlers.value }, () =>\n\t\t\t\th(VisibilityProvider, {}, () => slots.default?.() ?? null),\n\t\t\t);\n\t\t};\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// ActorProvider — the 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\tonError: {\n\t\t\ttype: Function as PropType<(error: unknown, reset: () => void) => void>,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\t// The error boundary. It contains an error of the RENDER of a descendant — the\n\t\t// render function, the setup function, and a lifecycle hook — so that a subtree\n\t\t// that throws cannot take the whole application down. `onErrorCaptured` notifies\n\t\t// `onError`, it sets the errored flag, and the render function below then returns\n\t\t// the `fallback` slot instead of the children.\n\t\t//\n\t\t// `onErrorCaptured` of Vue also fires for an error of an event handler, and of a\n\t\t// watcher callback, which no other renderer of this workspace sees: the boundary\n\t\t// of React, the ErrorBoundary of Solid, and `<svelte:boundary>` each catch a\n\t\t// failure of the render only. A click handler that throws must therefore NOT\n\t\t// replace the view with the fallback. This boundary leaves such an error\n\t\t// COMPLETELY alone: it calls `onError` never, and it returns undefined, so the\n\t\t// error continues to `app.config.errorHandler` exactly as it does without this\n\t\t// provider. A report here would give play-vue a wider `onError` contract than the\n\t\t// other four renderers, and it would reach a caller that wires both this prop and\n\t\t// the global handler two times for one click.\n\t\t//\n\t\t// RESET RULE: the next view emission clears the flag (see the callbacks of\n\t\t// watchSignal and the handler of the actor swap). A view transition therefore\n\t\t// retries the render. Without the rule the user sends a recovery event, the actor\n\t\t// moves to a healthy view, and the screen keeps the fallback.\n\t\t//\n\t\t// The per-element boundary of @json-render catches a throw of a catalog component\n\t\t// upstream, and reports it through `onRenderError`. This boundary is the outer net\n\t\t// for everything else.\n\t\t//\n\t\t// The return of false stops a render error HERE. Without `onError` that error\n\t\t// therefore reaches neither `app.config.errorHandler` nor the console, and a\n\t\t// consumer that gives no handler loses it in silence. Write it to `console.error`\n\t\t// in that case, in the same way as the inner renderer does for a component that\n\t\t// throws without an `onRenderError` handler.\n\t\tconst errored = ref(false);\n\t\t/**\n\t\t * The report-once rule, from @xmachines/play-actor.\n\t\t *\n\t\t * It holds the actor, the view and the store of the failure that it reported. A\n\t\t * latch that compared the identity of the ERROR held nothing: an accessor that\n\t\t * throws builds a new one at each read, which is the ordinary shape of a failing\n\t\t * projection. play-svelte and play-solid use the same latch.\n\t\t */\n\t\tconst failureLatch = createFailureLatch();\n\n\t\t// The report guard, from @xmachines/play-actor: it blocks a reset that the host\n\t\t// calls from INSIDE the report, and every callback once the provider is gone. The\n\t\t// five renderers share the one implementation.\n\t\t//\n\t\t// The re-entry rule matters more here than a stack suggests: a write of a ref\n\t\t// renders on the NEXT tick, so a host with a retry-once policy loops with no stack\n\t\t// at all, and a flag around the retry itself would catch nothing.\n\t\t//\n\t\t// It CONTAINS a handler of the host that throws. The flag below schedules a\n\t\t// RENDER, and a throw that leaves the hook stops that render and skips the\n\t\t// `return false`, so the subtree that threw stays on the screen AND the error\n\t\t// reaches `app.config.errorHandler`.\n\t\tconst guard = createReportGuard({\n\t\t\tnoHandler:\n\t\t\t\t\"[@xmachines/play-vue] ActorProvider contained an error of a descendant. \" +\n\t\t\t\t\"Give an onError prop to receive it.\",\n\t\t\thandlerThrew:\n\t\t\t\t\"[@xmachines/play-vue] the onError handler of the host threw. \" +\n\t\t\t\t\"ActorProvider contained the failure of the view all the same.\",\n\t\t});\n\t\t// The retry renders again through a nonce that the render function reads. Clearing\n\t\t// the flags alone renders nothing: `errored.value = false` writes the value that\n\t\t// the ref holds already on the path of a failed RESOLUTION, and Vue schedules\n\t\t// nothing for a write that changes no value.\n\t\tconst retryNonce = ref(0);\n\n\t\t/**\n\t\t * Clears the contained error and renders again.\n\t\t *\n\t\t * The render function reads `view.value`, so the retry resolves the view that the\n\t\t * actor holds NOW. A retry bound to the view that failed would take a host that\n\t\t * keeps the callback back onto the screen that threw.\n\t\t */\n\t\tconst retry = (): void => {\n\t\t\tif (guard.blocked()) return;\n\t\t\terrored.value = false;\n\t\t\tfailureLatch.clear();\n\t\t\tretryNonce.value++;\n\t\t};\n\n\t\t/** Sends a contained failure to `onError`, or to the console when none is given. */\n\t\tconst report = (err: unknown): void => {\n\t\t\tconst handler = props.onError;\n\t\t\tguard.report(err, handler ? () => handler(err, retry) : undefined);\n\t\t};\n\n\t\t// The configuration of the application, read one time. `canRaiseAgain` asks the two\n\t\t// fields that `handleError` of Vue reads on every error. It lives in error-source.ts,\n\t\t// because its production half answers a spelling that a mounted test reaches never.\n\t\tconst appConfig = getCurrentInstance()?.appContext.config;\n\n\t\t// The error that this boundary let through LAST, and that the NEXT raise can still\n\t\t// claim as a repeat.\n\t\t//\n\t\t// The second raise is SYNCHRONOUS and IMMEDIATE. `logError` of the development\n\t\t// bundle re-throws, and the throw unwinds straight back into `callWithErrorHandling`\n\t\t// of `flushJobs`, which reports the same value again. No other raise reaches this\n\t\t// hook in between, so ONE slot holds the complete window.\n\t\t//\n\t\t// EVERY raise that fails the render takes the slot, and not a component update\n\t\t// alone. A record that no raise takes can otherwise outlive its window.\n\t\t//\n\t\t// The record names the INSTANCE beside the error value. A repeat carries the\n\t\t// instance whose JOB ran when the error unwound, and that is the owner of the\n\t\t// watcher OR one of its ANCESTORS: a pre-flush watcher of a child runs inside the\n\t\t// update of the parent that passes it a prop, and `flushJobs` reports the throw\n\t\t// under the parent — or under the outermost ancestor whose update ran. The take\n\t\t// therefore walks the `$parent` chain of the recorded instance. A SIBLING stands on\n\t\t// that chain never, and a sibling that fails with the same cached error is one\n\t\t// loader with two consumers: that failure is a first incident, and the boundary\n\t\t// contains it.\n\t\t//\n\t\t// The microtask is the BACKSTOP, for a raise that neither a repeat nor another\n\t\t// raise follows. A record for the life of the provider would disarm the boundary\n\t\t// for that error for ever, and a descendant that keeps a failure and raises it\n\t\t// again from a LATER render is an ordinary shape.\n\t\t//\n\t\t// Each `remember` schedules a clear of ITS OWN record, and that clear takes no\n\t\t// other. A clear that removed whatever the slot holds could run after a take\n\t\t// emptied the slot and a later `remember` filled it again, and it would then\n\t\t// disarm the boundary for a repeat that is still to come.\n\t\tlet letThrough: { error: unknown; instance: ComponentPublicInstance | null } | null = null;\n\t\tconst remember = (error: unknown, instance: ComponentPublicInstance | null): void => {\n\t\t\tconst record = { error, instance };\n\t\t\tletThrough = record;\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tif (letThrough === record) letThrough = null;\n\t\t\t});\n\t\t};\n\t\t/**\n\t\t * Takes the record. It answers TRUE when the record names this error, and when the\n\t\t * instance that raises it is the recorded one or one of its ancestors.\n\t\t *\n\t\t * The comparison reads `$` on both sides, which is the INTERNAL instance. Vue hands\n\t\t * `onErrorCaptured` the `proxy` of a component, and `$parent` answers with the\n\t\t * `exposeProxy` of a parent that called `expose()` — which the compiler makes every\n\t\t * `<script setup>` component do. The two objects differ, so a walk that compared\n\t\t * them found an exposed ancestor never, and the boundary contained a watcher error\n\t\t * that it must let through. `$` resolves through both proxies and names one object.\n\t\t */\n\t\tconst takeRepeat = (error: unknown, instance: ComponentPublicInstance | null): boolean => {\n\t\t\tconst record = letThrough;\n\t\t\tletThrough = null;\n\t\t\tif (record === null || record.error !== error) return false;\n\t\t\tfor (let node = record.instance; node !== null; node = node.$parent) {\n\t\t\t\tif (node.$ === instance?.$) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\n\t\tonErrorCaptured((err, instance, info) => {\n\t\t\t// An error that did NOT fail the render leaves this boundary untouched: the\n\t\t\t// screen keeps its view, `onError` stays silent, and the return of undefined\n\t\t\t// sends the error on to `app.config.errorHandler`. The boundary of React, the\n\t\t\t// ErrorBoundary of Solid, and `<svelte:boundary>` each see such an error never,\n\t\t\t// so a call of `onError` here would give play-vue a wider contract than the\n\t\t\t// other four, AND it would report the same failure twice to a caller that wires\n\t\t\t// both `onError` and the global handler of the application.\n\t\t\tif (!failsTheRender(info)) {\n\t\t\t\t// Arm the record only where Vue WILL raise this error a second time. A record\n\t\t\t\t// that no repeat claims stands until the microtask, and a genuine failure of\n\t\t\t\t// a render in the same flush — the same descendant, the same cached error —\n\t\t\t\t// would then take it: no fallback, no `onError`, and no screen.\n\t\t\t\t//\n\t\t\t\t// `handleError` of Vue calls `app.config.errorHandler` where one exists, and\n\t\t\t\t// it reaches `logError` never; the production bundle throws only where the\n\t\t\t\t// application asks for `throwUnhandledErrorInProduction`. The one case that\n\t\t\t\t// this line cannot see is an ANCESTOR `errorCaptured` that answers false,\n\t\t\t\t// which stops Vue before `logError` too. That case stays open, and the doc of\n\t\t\t\t// `onError` names it.\n\t\t\t\tif (canRaiseAgain(info, appConfig)) remember(err, instance);\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Vue can raise the SAME error a SECOND time under another source. The\n\t\t\t// development bundle of `logError` re-throws an error that no\n\t\t\t// `app.config.errorHandler` took. A queued job then loses that throw to\n\t\t\t// `callWithErrorHandling(job, job.i, 15)` of `flushJobs` — a `watch` callback\n\t\t\t// with the default `pre` flush, for one — and `flushJobs` reports it again as\n\t\t\t// \"component update\". The classification above answers \"render failure\" for that\n\t\t\t// second spelling, so a watcher that threw would take the view off the screen\n\t\t\t// after all. One error is one incident: an error that this boundary let through\n\t\t\t// in this turn stays through.\n\t\t\t//\n\t\t\t// The gate asks for the SOURCE of the repeat as well. `flushJobs` reports a\n\t\t\t// job under \"component update\", and no other source can carry a second raise\n\t\t\t// to this hook, so a record of the turn can disarm the boundary for that one\n\t\t\t// spelling alone. Without the question a setup function or a render function\n\t\t\t// that throws the SAME error value in the same turn — a loader that caches its\n\t\t\t// rejection, for one — would reach no fallback, no `onError`, and no screen.\n\t\t\t//\n\t\t\t// It returns FALSE, and not undefined. The FIRST raise went on to\n\t\t\t// `app.config.errorHandler`, which is the contract of a non-render source, and\n\t\t\t// the development bundle re-threw it there — that throw IS this second report.\n\t\t\t// A second `undefined` sends it on one more time, so `logError` warns twice for\n\t\t\t// one incident and throws again, and that throw leaves `flushJobs`. The\n\t\t\t// `finally` of `flushJobs` runs `queue.length = 0`. `flushJobs` therefore drops\n\t\t\t// every job behind the one that failed, and a sibling of the descendant that\n\t\t\t// threw renders never. One incident reaches the caller ONE time, and the flush\n\t\t\t// of Vue finishes.\n\t\t\t// `takeRepeat` runs for EVERY raise that fails the render, and not inside the\n\t\t\t// condition below: a raise of another source ENDS the window of the repeat, so\n\t\t\t// the record can disarm the boundary for the one raise that follows it and for\n\t\t\t// no other.\n\t\t\tconst repeated = takeRepeat(err, instance);\n\t\t\tif (isComponentUpdate(info) && repeated) {\n\t\t\t\t// The value reaches no console without this line. `logError` of the\n\t\t\t\t// development bundle writes the SOURCE, and not the value. It then throws\n\t\t\t\t// the value, and that throw IS this second report, which the `return`\n\t\t\t\t// below stops. `report()` wrote such an error before this gate existed, so\n\t\t\t\t// a host with no `onError` prop could still debug a watcher or a loader\n\t\t\t\t// that failed. The production bundle writes the value on the FIRST raise,\n\t\t\t\t// and it throws instead where the application asks for\n\t\t\t\t// `throwUnhandledErrorInProduction` — that application gets the write here.\n\t\t\t\t// One incident reaches the console ONE time in both bundles.\n\t\t\t\t//\n\t\t\t\t// The line carries the name of this package, as every other console write\n\t\t\t\t// of this provider does. Without it a developer reads a bare error and\n\t\t\t\t// cannot tell that the provider wrote it, and not Vue.\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[@xmachines/play-vue] ActorProvider stopped a second report of this \" +\n\t\t\t\t\t\t\"error. Vue raised it again under the source of a component update, \" +\n\t\t\t\t\t\t\"and it failed no render.\",\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// The SCREEN comes first, and the report second, as `contain()` of play-dom\n\t\t\t// states it. A reporter can throw — one that re-throws to escalate, or a\n\t\t\t// transport that fails — and here the order alone is not enough: the flag\n\t\t\t// schedules a RENDER, and a throw that leaves this hook stops that render and\n\t\t\t// skips the `return false`. The GUARD holds that half, for the four renderers\n\t\t\t// that need it: it carries a `handlerThrew` message, so it contains the throw\n\t\t\t// of the handler and writes it to `console.error`.\n\t\t\t//\n\t\t\t// The five renderers hold the one rule: a handler of the host never defeats the\n\t\t\t// containment, and a host that must escalate raises the failure from a task of\n\t\t\t// its own.\n\t\t\terrored.value = true;\n\t\t\treport(err);\n\t\t\treturn false;\n\t\t});\n\n\t\t// Take the actor out of the reactive proxy of Vue, to reach the raw Signal objects.\n\t\t// A shallowRef holds the actor. Therefore the injected value and the signal watcher\n\t\t// can follow `props.actor` when the prop changes after the mount.\n\t\tconst actorRef = shallowRef(toRaw(props.actor as AbstractActor<AnyActorLogic> & Viewable));\n\n\t\t// Take the registryResult out of the proxy, and mark each component as raw, because the reactivity of Vue costs too much here\n\t\tconst rawRegistry = Object.fromEntries(\n\t\t\tObject.entries(toRaw(props.registryResult).registry).map(([k, v]) => [\n\t\t\t\tk,\n\t\t\t\tmarkRaw(v as object),\n\t\t\t]),\n\t\t) as DefineRegistryResult[\"registry\"];\n\n\t\t// Put the onRenderError prop into the registry. The property is not enumerable, and\n\t\t// it replaces the handler of the defineRegistry level. attachRenderErrorHandler makes\n\t\t// a copy, and it changes the registry of the caller never.\n\t\t// The code must call markRaw on the copy again, because a copy does not carry the\n\t\t// non-enumerable flag of markRaw.\n\t\tconst rawRegistryResult: DefineRegistryResult = {\n\t\t\t...toRaw(props.registryResult),\n\t\t\tregistry: markRaw(\n\t\t\t\tprops.onRenderError\n\t\t\t\t\t? attachRenderErrorHandler(rawRegistry, props.onRenderError)\n\t\t\t\t\t: rawRegistry,\n\t\t\t),\n\t\t};\n\n\t\t// Give the actor to each descendant, through the provide and inject mechanism of Vue.\n\t\t//\n\t\t// The code gives a stable Proxy, and not the raw actor. The pattern is the same as\n\t\t// the ViewKey proxy of ActorProviderInner. The injected value therefore follows the\n\t\t// CURRENT `props.actor`: each injector keeps the reference of the setup(), and every\n\t\t// trap resolves against the newest actor (actorRef.value) after a change of the prop.\n\t\t// The proxy forwards each relevant trap. Therefore it behaves like the actor below it\n\t\t// for a read, for a write, for a membership test (`in`), for an enumeration\n\t\t// (Object.keys and a spread), and for a prototype test (`instanceof`). Each method\n\t\t// binds to the current raw actor. Therefore `this`, and also each private field,\n\t\t// works exactly as in a direct call.\n\t\t//\n\t\t// IMPORTANT: this proxy is NOT identity-equal to `props.actor`, because it is a\n\t\t// separate object, and therefore a separate key of a WeakMap. A consumer that uses\n\t\t// the identity as its key must receive the `props.actor` value itself, and never this\n\t\t// injected proxy. The most important such consumer is the guard of one bridge for\n\t\t// each actor in RouterBridgeBase: it uses the identity of the actor as the key of a\n\t\t// WeakMap at the module level.\n\t\t//\n\t\t// The identity of a method: the code keeps each bound method, with the method below\n\t\t// it as the key. A repeated read therefore returns the SAME function\n\t\t// (actor.send === actor.send), and the hot path allocates nothing. A read binds the\n\t\t// method again only after a change of the method below it. The watch handler below\n\t\t// CLEARS the cache on a change of the actor. Therefore each prototype method binds to\n\t\t// the new actor.\n\t\tconst boundMethodCache = new Map<PropertyKey, { source: unknown; bound: unknown }>();\n\t\tprovideActor(\n\t\t\tnew Proxy({} as AnyPlayActor, {\n\t\t\t\tget(_target, prop) {\n\t\t\t\t\tconst current = actorRef.value;\n\t\t\t\t\tconst value = Reflect.get(current, prop, current) as unknown;\n\t\t\t\t\tif (typeof value !== \"function\") return value;\n\t\t\t\t\tconst cached = boundMethodCache.get(prop);\n\t\t\t\t\tif (cached && cached.source === value) return cached.bound;\n\t\t\t\t\tconst bound = (value as (...args: unknown[]) => unknown).bind(current);\n\t\t\t\t\tboundMethodCache.set(prop, { source: value, bound });\n\t\t\t\t\treturn bound;\n\t\t\t\t},\n\t\t\t\thas(_target, prop) {\n\t\t\t\t\treturn prop in actorRef.value;\n\t\t\t\t},\n\t\t\t\tset(_target, prop, value) {\n\t\t\t\t\treturn Reflect.set(actorRef.value, prop, value, actorRef.value);\n\t\t\t\t},\n\t\t\t\tdeleteProperty(_target, prop) {\n\t\t\t\t\treturn Reflect.deleteProperty(actorRef.value, prop);\n\t\t\t\t},\n\t\t\t\townKeys() {\n\t\t\t\t\treturn Reflect.ownKeys(actorRef.value);\n\t\t\t\t},\n\t\t\t\tgetOwnPropertyDescriptor(_target, prop) {\n\t\t\t\t\tconst desc = Reflect.getOwnPropertyDescriptor(actorRef.value, prop);\n\t\t\t\t\tif (!desc) return undefined;\n\t\t\t\t\t// An invariant of a Proxy: the target of this proxy is an empty {}. Therefore each\n\t\t\t\t\t// key of ownKeys must resolve to a configurable descriptor here. Without that, the\n\t\t\t\t\t// [[GetOwnProperty]] invariant throws, because a proxy must not report a property that\n\t\t\t\t\t// its target has not as non-configurable. The own props of the actor are mutable at\n\t\t\t\t\t// run time in each case. Therefore `configurable: true` is correct enough for the\n\t\t\t\t\t// enumeration of Object.keys and of a spread.\n\t\t\t\t\tdesc.configurable = true;\n\t\t\t\t\treturn desc;\n\t\t\t\t},\n\t\t\t\tgetPrototypeOf() {\n\t\t\t\t\treturn Reflect.getPrototypeOf(actorRef.value);\n\t\t\t\t},\n\t\t\t}) as AnyPlayActor,\n\t\t);\n\n\t\t// Seed the first value, then subscribe. Both steps are synchronous, and no scheduler\n\t\t// runs between them.\n\t\t// Solid does the same with createEffect, and Svelte does the same with $effect.\n\t\t// The pattern is therefore the same in each framework.\n\t\tconst view = ref<PlaySpec | null>(actorRef.value.currentView.get());\n\n\t\t// The store lifecycle: it seeds the store again on a change of the viewKey, it\n\t\t// refreshes /context in place in every other case, it resets the store on a change of\n\t\t// the actor, and it guards the identity cache. The shared coordinator comes from\n\t\t// @xmachines/play-actor. Only the wiring of the reactivity below belongs to Vue.\n\t\tconst storeLifecycle = createViewStoreLifecycle((seed) =>\n\t\t\txstateStoreStateStore({ atom: createAtom(seed) }),\n\t\t);\n\t\t// storeKey is the key of a SUBTREE REMOUNT. Therefore it must change only after a\n\t\t// new seed of the store, and never after a refresh of /context in place. Without this\n\t\t// rule, each update of the projection removes the subtree on the screen.\n\t\t// (The controlled mode seeds the store never again. Therefore the key changes never\n\t\t// there.)\n\t\tlet storeKey = 0;\n\n\t\t// The signal watcher connects the TC39 Signals to the reactivity of Vue.\n\t\t// The code assigns it again on a change of the actor. Therefore the cleanup always\n\t\t// releases the live watcher.\n\t\t// Every view emission clears the errored flag (see the reset rule above) before the\n\t\t// new view renders. A hit of the error boundary therefore retries here.\n\t\tlet unwatch = watchSignal(actorRef.value.currentView, (nextView) => {\n\t\t\terrored.value = false;\n\t\t\tfailureLatch.clear();\n\t\t\tview.value = nextView;\n\t\t});\n\n\t\t// The reset watches the STORE too. A render fails on it, so a caller that repairs a\n\t\t// controlled store — without a new emission — must see the children again.\n\t\t// play-react and play-solid hold the same rule, and the actor swap below covers the\n\t\t// third input.\n\t\twatch(\n\t\t\t() => props.store,\n\t\t\t() => {\n\t\t\t\terrored.value = false;\n\t\t\t\tfailureLatch.clear();\n\t\t\t},\n\t\t);\n\n\t\t// React to a change of `props.actor`: cancel the subscription to the currentView of\n\t\t// the OLD actor, seed the view from the NEW actor synchronously (`flush: \"sync\"`, so\n\t\t// that no scheduler runs between the steps, as in the seed and watch pattern of the\n\t\t// mount above), and subscribe to the new signal. The view of the new seed has a new\n\t\t// identity. Therefore the branch of the internal store below builds the store again,\n\t\t// and it changes storeKey, exactly like a normal view transition.\n\t\twatch(\n\t\t\t() => toRaw(props.actor as AbstractActor<AnyActorLogic> & Viewable),\n\t\t\t(nextActor) => {\n\t\t\t\tif (nextActor === actorRef.value) return;\n\t\t\t\tunwatch();\n\t\t\t\tactorRef.value = nextActor;\n\t\t\t\t// (The store lifecycle resets itself on a change of the actor, because a store that\n\t\t\t\t// stays alive must not outlive its actor.)\n\t\t\t\t// Clear the cache of the bound methods. Each prototype method therefore binds to the\n\t\t\t\t// new actor on the next read. See the get trap of the injected proxy.\n\t\t\t\tboundMethodCache.clear();\n\t\t\t\t// The new seed is a view emission — clear the errored flag with it.\n\t\t\t\terrored.value = false;\n\t\t\t\tfailureLatch.clear();\n\t\t\t\tview.value = nextActor.currentView.get();\n\t\t\t\tunwatch = watchSignal(nextActor.currentView, (nextView) => {\n\t\t\t\t\terrored.value = false;\n\t\t\t\t\tfailureLatch.clear();\n\t\t\t\t\tview.value = nextView;\n\t\t\t\t});\n\t\t\t},\n\t\t\t{ flush: \"sync\" },\n\t\t);\n\n\t\tonUnmounted(() => {\n\t\t\tguard.dispose();\n\t\t\tunwatch();\n\t\t});\n\n\t\treturn () => {\n\t\t\t// The nonce of the retry. The read makes this render function depend on it, so\n\t\t\t// the reset of the host renders again — see `retry` above.\n\t\t\tvoid retryNonce.value;\n\n\t\t\t// A contained error of a descendant: show the fallback slot, or show nothing,\n\t\t\t// until the next view emission clears the flag and retries the render.\n\t\t\tif (errored.value) {\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\n\t\t\t// No view: show the fallback slot, or show 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, which is external and controlled, or internal for each\n\t\t\t// viewKey, through the shared lifecycle. The children receive the store with the\n\t\t\t// guard, because /context is read-only to the spec. The machinery refreshes the\n\t\t\t// store through the reference without a guard, inside the coordinator.\n\t\t\t//\n\t\t\t// The try CONTAINS a failure of this resolution. `spec.state` can throw — a\n\t\t\t// projection with an accessor, or a seed of a store — and `onErrorCaptured`\n\t\t\t// above sees a throw of a DESCENDANT only, never one of this render function.\n\t\t\t// Without the try such a failure leaves the provider, reaches\n\t\t\t// app.config.errorHandler, and takes the application down, while `onError` and\n\t\t\t// the fallback slot both stay unused.\n\t\t\t//\n\t\t\t// The branch sets the errored flag NOT: a mutation of the state during a render\n\t\t\t// starts a second render. It returns the fallback for THIS render instead, and\n\t\t\t// the next view emission renders the children again — which is the reset rule,\n\t\t\t// with no flag to clear.\n\t\t\tlet resolution;\n\t\t\ttry {\n\t\t\t\tresolution = storeLifecycle.resolve(actorRef.value, spec, props.store);\n\t\t\t} catch (error) {\n\t\t\t\t// LATCH the failure, and report it ONE time. The render function runs again\n\t\t\t\t// for any tracked change, and a report on each attempt would call onError a\n\t\t\t\t// second and a third time for one failure — play-react, play-solid and\n\t\t\t\t// play-svelte each report once.\n\t\t\t\tif (failureLatch.shouldReport(actorRef.value, spec, props.store)) report(error);\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\t\t\tfailureLatch.clear();\n\t\t\tif (resolution.reseeded) storeKey++;\n\t\t\tconst store: StateStore = resolution.guardedStore;\n\n\t\t\t// ActorProviderInner renders inside StateProvider. Therefore 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":""}
|
|
1
|
+
{"version":3,"file":"ActorProvider.js","names":[],"sources":["../src/ActorProvider.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * ActorProvider — the low-level provider of the XMachines Vue render architecture.\n *\n * It owns the complete actor lifecycle:\n * - the signal subscription (watchSignal), which connects the TC39 Signals to the Vue reactivity\n * - the StateStore lifecycle of each view, controlled and uncontrolled\n * - the resolution of the handlers, through ActorProviderInner, which must be inside StateProvider\n * - the ViewContextValue, which it provides through the ViewKey injection key\n * - the ActionProvider wrapper and the VisibilityProvider wrapper, for the Renderer below\n * - the injection of onRenderError into the registry\n *\n * The author of a library who needs this control uses the component directly.\n * An end user takes <PlayUIProvider> instead.\n *\n * @invariant Actor Authority - the Actor decides each state transition, with its guards\n * @invariant Passive Infrastructure - the component observes the signals, and it sends the events\n * @invariant Signal-Only Reactivity - the state of the business logic is in the actor signals\n */\n\nimport {\n\tdefineComponent,\n\tref,\n\tcomputed,\n\ttoRaw,\n\tmarkRaw,\n\tonUnmounted,\n\tonErrorCaptured,\n\th,\n\tprovide,\n\tshallowRef,\n\twatch,\n\tgetCurrentInstance,\n} from \"vue\";\nimport type { ComponentPublicInstance, PropType } from \"vue\";\nimport { watchSignal } from \"@xmachines/play-signals\";\nimport { canRaiseAgain, failsTheRender, isComponentUpdate } from \"./error-source.js\";\nimport {\n\tattachRenderErrorHandler,\n\tcreateFailureLatch,\n\tcreateReportGuard,\n\tcreateViewStoreLifecycle,\n} from \"@xmachines/play-view\";\nimport type { Viewable, PlaySpec, ViewActor } from \"@xmachines/play-view\";\nimport type {\n\tDefineRegistryResult,\n\tSetState,\n\tRenderErrorHandler,\n} from \"@xmachines/json-render-vue\";\n\nimport {\n\tStateProvider,\n\tuseStateStore,\n\tActionProvider,\n\tVisibilityProvider,\n} from \"@xmachines/json-render-vue\";\nimport type { StateStore } from \"@xmachines/json-render-core\";\nimport { createAtom } from \"@xstate/store\";\nimport { xstateStoreStateStore } from \"@xmachines/json-render-xstate\";\nimport { provideActor, type AnyPlayActor } from \"./useActor.js\";\nimport { ViewKey, type ViewContextValue } from \"./actor-provider-context.js\";\nimport type { ActorProviderProps } from \"./types.js\";\n\n// Re-export the props type and the context accessors, for a consumer that imports from this file\nexport type { ActorProviderProps } from \"./types.js\";\nexport { usePlayView } from \"./actor-provider-context.js\";\nexport type { ViewContextValue } from \"./actor-provider-context.js\";\n\n// ---------------------------------------------------------------------------\n// ActorProviderInner — it renders inside StateProvider, so that it can call useStateStore()\n// It gives the ViewContextValue, the ActionProvider, and the VisibilityProvider to the Renderer below.\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// The code uses a shallowRef for the value of the view context, because a deep\n\t\t// reactivity costs too much. The Proxy below lets inject() always read the newest\n\t\t// value.\n\t\tconst viewRef = shallowRef<ViewContextValue | null>(null);\n\n\t\t// Give the ViewContextValue through the inject and provide system of Vue.\n\t\t// The code calls provide synchronously in setup(), so that Vue registers the value on\n\t\t// the instance of the component. It uses a Proxy, so that each descendant always\n\t\t// receives the newest 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() during setup(), which is synchronous and before the return.\n\t\t// A Vue composable that uses inject() must run during setup(), and not in a render\n\t\t// function.\n\t\tconst stateStore = useStateStore();\n\n\t\t// Build a SetState adapter, because the factory of the handlers expects the pattern\n\t\t// of an updater function. The code defines it one time, in setup(). It reads\n\t\t// stateStore.getSnapshot and stateStore.update at the moment of the call. Therefore\n\t\t// it always works on the live state of the store, and it needs no new definition when\n\t\t// the identity of stateStore changes between two renders.\n\t\tconst setStateAdapter: SetState = (updater) => {\n\t\t\tconst prev = stateStore.getSnapshot();\n\t\t\tstateStore.update(updater(prev));\n\t\t};\n\n\t\t// Keep the handlers, with the identity of the registryResult prop as the key. The\n\t\t// getter functions are stable closures, and they hold the stateStore of setup().\n\t\t// Therefore they cause no new computation. The code computes the handlers again only\n\t\t// after a change of the definition of the registry, and not on each render of a view\n\t\t// update.\n\t\tconst handlers = computed(() =>\n\t\t\tprops.registryResult.handlers(\n\t\t\t\t() => setStateAdapter,\n\t\t\t\t() => stateStore.getSnapshot(),\n\t\t\t),\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\tviewRef.value = {\n\t\t\t\tspec: props.spec,\n\t\t\t\thandlers: handlers.value,\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 the children with an ActionProvider and a VisibilityProvider. The Renderer of\n\t\t\t// PlayRenderer therefore works also with ActorProvider alone, without PlayUIProvider\n\t\t\t// and without JSONUIProvider.\n\t\t\treturn h(ActionProvider, { handlers: handlers.value }, () =>\n\t\t\t\th(VisibilityProvider, {}, () => slots.default?.() ?? null),\n\t\t\t);\n\t\t};\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// ActorProvider — the 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\tonError: {\n\t\t\ttype: Function as PropType<(error: unknown, reset: () => void) => void>,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\t// The error boundary. It contains an error of the RENDER of a descendant — the\n\t\t// render function, the setup function, and a lifecycle hook — so that a subtree\n\t\t// that throws cannot take the whole application down. `onErrorCaptured` notifies\n\t\t// `onError`, it sets the errored flag, and the render function below then returns\n\t\t// the `fallback` slot instead of the children.\n\t\t//\n\t\t// `onErrorCaptured` of Vue also fires for an error of an event handler, and of a\n\t\t// watcher callback, which no other renderer of this workspace sees: the boundary\n\t\t// of React, the ErrorBoundary of Solid, and `<svelte:boundary>` each catch a\n\t\t// failure of the render only. A click handler that throws must therefore NOT\n\t\t// replace the view with the fallback. This boundary leaves such an error\n\t\t// COMPLETELY alone: it calls `onError` never, and it returns undefined, so the\n\t\t// error continues to `app.config.errorHandler` exactly as it does without this\n\t\t// provider. A report here would give play-vue a wider `onError` contract than the\n\t\t// other four renderers, and it would reach a caller that wires both this prop and\n\t\t// the global handler two times for one click.\n\t\t//\n\t\t// RESET RULE: the next view emission clears the flag (see the callbacks of\n\t\t// watchSignal and the handler of the actor swap). A view transition therefore\n\t\t// retries the render. Without the rule the user sends a recovery event, the actor\n\t\t// moves to a healthy view, and the screen keeps the fallback.\n\t\t//\n\t\t// The per-element boundary of @json-render catches a throw of a catalog component\n\t\t// upstream, and reports it through `onRenderError`. This boundary is the outer net\n\t\t// for everything else.\n\t\t//\n\t\t// The return of false stops a render error HERE. Without `onError` that error\n\t\t// therefore reaches neither `app.config.errorHandler` nor the console, and a\n\t\t// consumer that gives no handler loses it in silence. Write it to `console.error`\n\t\t// in that case, in the same way as the inner renderer does for a component that\n\t\t// throws without an `onRenderError` handler.\n\t\tconst errored = ref(false);\n\t\t/**\n\t\t * The report-once rule, from @xmachines/play-actor.\n\t\t *\n\t\t * It holds the actor, the view and the store of the failure that it reported. A\n\t\t * latch that compared the identity of the ERROR held nothing: an accessor that\n\t\t * throws builds a new one at each read, which is the ordinary shape of a failing\n\t\t * projection. play-svelte and play-solid use the same latch.\n\t\t */\n\t\tconst failureLatch = createFailureLatch();\n\n\t\t// The report guard, from @xmachines/play-actor: it blocks a reset that the host\n\t\t// calls from INSIDE the report, and every callback once the provider is gone. The\n\t\t// five renderers share the one implementation.\n\t\t//\n\t\t// The re-entry rule matters more here than a stack suggests: a write of a ref\n\t\t// renders on the NEXT tick, so a host with a retry-once policy loops with no stack\n\t\t// at all, and a flag around the retry itself would catch nothing.\n\t\t//\n\t\t// It CONTAINS a handler of the host that throws. The flag below schedules a\n\t\t// RENDER, and a throw that leaves the hook stops that render and skips the\n\t\t// `return false`, so the subtree that threw stays on the screen AND the error\n\t\t// reaches `app.config.errorHandler`.\n\t\tconst guard = createReportGuard({\n\t\t\tnoHandler:\n\t\t\t\t\"[@xmachines/play-vue] ActorProvider contained an error of a descendant. \" +\n\t\t\t\t\"Give an onError prop to receive it.\",\n\t\t\thandlerThrew:\n\t\t\t\t\"[@xmachines/play-vue] the onError handler of the host threw. \" +\n\t\t\t\t\"ActorProvider contained the failure of the view all the same.\",\n\t\t});\n\t\t// The retry renders again through a nonce that the render function reads. Clearing\n\t\t// the flags alone renders nothing: `errored.value = false` writes the value that\n\t\t// the ref holds already on the path of a failed RESOLUTION, and Vue schedules\n\t\t// nothing for a write that changes no value.\n\t\tconst retryNonce = ref(0);\n\n\t\t/**\n\t\t * Clears the contained error and renders again.\n\t\t *\n\t\t * The render function reads `view.value`, so the retry resolves the view that the\n\t\t * actor holds NOW. A retry bound to the view that failed would take a host that\n\t\t * keeps the callback back onto the screen that threw.\n\t\t */\n\t\tconst retry = (): void => {\n\t\t\tif (guard.blocked()) return;\n\t\t\terrored.value = false;\n\t\t\tfailureLatch.clear();\n\t\t\tretryNonce.value++;\n\t\t};\n\n\t\t/** Sends a contained failure to `onError`, or to the console when none is given. */\n\t\tconst report = (err: unknown): void => {\n\t\t\tconst handler = props.onError;\n\t\t\tguard.report(err, handler ? () => handler(err, retry) : undefined);\n\t\t};\n\n\t\t// The configuration of the application, read one time. `canRaiseAgain` asks the two\n\t\t// fields that `handleError` of Vue reads on every error. It lives in error-source.ts,\n\t\t// because its production half answers a spelling that a mounted test reaches never.\n\t\tconst appConfig = getCurrentInstance()?.appContext.config;\n\n\t\t// The error that this boundary let through LAST, and that the NEXT raise can still\n\t\t// claim as a repeat.\n\t\t//\n\t\t// The second raise is SYNCHRONOUS and IMMEDIATE. `logError` of the development\n\t\t// bundle re-throws, and the throw unwinds straight back into `callWithErrorHandling`\n\t\t// of `flushJobs`, which reports the same value again. No other raise reaches this\n\t\t// hook in between, so ONE slot holds the complete window.\n\t\t//\n\t\t// EVERY raise that fails the render takes the slot, and not a component update\n\t\t// alone. A record that no raise takes can otherwise outlive its window.\n\t\t//\n\t\t// The record names the INSTANCE beside the error value. A repeat carries the\n\t\t// instance whose JOB ran when the error unwound, and that is the owner of the\n\t\t// watcher OR one of its ANCESTORS: a pre-flush watcher of a child runs inside the\n\t\t// update of the parent that passes it a prop, and `flushJobs` reports the throw\n\t\t// under the parent — or under the outermost ancestor whose update ran. The take\n\t\t// therefore walks the `$parent` chain of the recorded instance. A SIBLING stands on\n\t\t// that chain never, and a sibling that fails with the same cached error is one\n\t\t// loader with two consumers: that failure is a first incident, and the boundary\n\t\t// contains it.\n\t\t//\n\t\t// The microtask is the BACKSTOP, for a raise that neither a repeat nor another\n\t\t// raise follows. A record for the life of the provider would disarm the boundary\n\t\t// for that error for ever, and a descendant that keeps a failure and raises it\n\t\t// again from a LATER render is an ordinary shape.\n\t\t//\n\t\t// Each `remember` schedules a clear of ITS OWN record, and that clear takes no\n\t\t// other. A clear that removed whatever the slot holds could run after a take\n\t\t// emptied the slot and a later `remember` filled it again, and it would then\n\t\t// disarm the boundary for a repeat that is still to come.\n\t\tlet letThrough: { error: unknown; instance: ComponentPublicInstance | null } | null = null;\n\t\tconst remember = (error: unknown, instance: ComponentPublicInstance | null): void => {\n\t\t\tconst record = { error, instance };\n\t\t\tletThrough = record;\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tif (letThrough === record) letThrough = null;\n\t\t\t});\n\t\t};\n\t\t/**\n\t\t * Takes the record. It answers TRUE when the record names this error, and when the\n\t\t * instance that raises it is the recorded one or one of its ancestors.\n\t\t *\n\t\t * The comparison reads `$` on both sides, which is the INTERNAL instance. Vue hands\n\t\t * `onErrorCaptured` the `proxy` of a component, and `$parent` answers with the\n\t\t * `exposeProxy` of a parent that called `expose()` — which the compiler makes every\n\t\t * `<script setup>` component do. The two objects differ, so a walk that compared\n\t\t * them found an exposed ancestor never, and the boundary contained a watcher error\n\t\t * that it must let through. `$` resolves through both proxies and names one object.\n\t\t */\n\t\tconst takeRepeat = (error: unknown, instance: ComponentPublicInstance | null): boolean => {\n\t\t\tconst record = letThrough;\n\t\t\tletThrough = null;\n\t\t\tif (record === null || record.error !== error) return false;\n\t\t\tfor (let node = record.instance; node !== null; node = node.$parent) {\n\t\t\t\tif (node.$ === instance?.$) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\n\t\tonErrorCaptured((err, instance, info) => {\n\t\t\t// An error that did NOT fail the render leaves this boundary untouched: the\n\t\t\t// screen keeps its view, `onError` stays silent, and the return of undefined\n\t\t\t// sends the error on to `app.config.errorHandler`. The boundary of React, the\n\t\t\t// ErrorBoundary of Solid, and `<svelte:boundary>` each see such an error never,\n\t\t\t// so a call of `onError` here would give play-vue a wider contract than the\n\t\t\t// other four, AND it would report the same failure twice to a caller that wires\n\t\t\t// both `onError` and the global handler of the application.\n\t\t\tif (!failsTheRender(info)) {\n\t\t\t\t// Arm the record only where Vue WILL raise this error a second time. A record\n\t\t\t\t// that no repeat claims stands until the microtask, and a genuine failure of\n\t\t\t\t// a render in the same flush — the same descendant, the same cached error —\n\t\t\t\t// would then take it: no fallback, no `onError`, and no screen.\n\t\t\t\t//\n\t\t\t\t// `handleError` of Vue calls `app.config.errorHandler` where one exists, and\n\t\t\t\t// it reaches `logError` never; the production bundle throws only where the\n\t\t\t\t// application asks for `throwUnhandledErrorInProduction`. The one case that\n\t\t\t\t// this line cannot see is an ANCESTOR `errorCaptured` that answers false,\n\t\t\t\t// which stops Vue before `logError` too. That case stays open, and the doc of\n\t\t\t\t// `onError` names it.\n\t\t\t\tif (canRaiseAgain(info, appConfig)) remember(err, instance);\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Vue can raise the SAME error a SECOND time under another source. The\n\t\t\t// development bundle of `logError` re-throws an error that no\n\t\t\t// `app.config.errorHandler` took. A queued job then loses that throw to\n\t\t\t// `callWithErrorHandling(job, job.i, 15)` of `flushJobs` — a `watch` callback\n\t\t\t// with the default `pre` flush, for one — and `flushJobs` reports it again as\n\t\t\t// \"component update\". The classification above answers \"render failure\" for that\n\t\t\t// second spelling, so a watcher that threw would take the view off the screen\n\t\t\t// after all. One error is one incident: an error that this boundary let through\n\t\t\t// in this turn stays through.\n\t\t\t//\n\t\t\t// The gate asks for the SOURCE of the repeat as well. `flushJobs` reports a\n\t\t\t// job under \"component update\", and no other source can carry a second raise\n\t\t\t// to this hook, so a record of the turn can disarm the boundary for that one\n\t\t\t// spelling alone. Without the question a setup function or a render function\n\t\t\t// that throws the SAME error value in the same turn — a loader that caches its\n\t\t\t// rejection, for one — would reach no fallback, no `onError`, and no screen.\n\t\t\t//\n\t\t\t// It returns FALSE, and not undefined. The FIRST raise went on to\n\t\t\t// `app.config.errorHandler`, which is the contract of a non-render source, and\n\t\t\t// the development bundle re-threw it there — that throw IS this second report.\n\t\t\t// A second `undefined` sends it on one more time, so `logError` warns twice for\n\t\t\t// one incident and throws again, and that throw leaves `flushJobs`. The\n\t\t\t// `finally` of `flushJobs` runs `queue.length = 0`. `flushJobs` therefore drops\n\t\t\t// every job behind the one that failed, and a sibling of the descendant that\n\t\t\t// threw renders never. One incident reaches the caller ONE time, and the flush\n\t\t\t// of Vue finishes.\n\t\t\t// `takeRepeat` runs for EVERY raise that fails the render, and not inside the\n\t\t\t// condition below: a raise of another source ENDS the window of the repeat, so\n\t\t\t// the record can disarm the boundary for the one raise that follows it and for\n\t\t\t// no other.\n\t\t\tconst repeated = takeRepeat(err, instance);\n\t\t\tif (isComponentUpdate(info) && repeated) {\n\t\t\t\t// The value reaches no console without this line. `logError` of the\n\t\t\t\t// development bundle writes the SOURCE, and not the value. It then throws\n\t\t\t\t// the value, and that throw IS this second report, which the `return`\n\t\t\t\t// below stops. `report()` wrote such an error before this gate existed, so\n\t\t\t\t// a host with no `onError` prop could still debug a watcher or a loader\n\t\t\t\t// that failed. The production bundle writes the value on the FIRST raise,\n\t\t\t\t// and it throws instead where the application asks for\n\t\t\t\t// `throwUnhandledErrorInProduction` — that application gets the write here.\n\t\t\t\t// One incident reaches the console ONE time in both bundles.\n\t\t\t\t//\n\t\t\t\t// The line carries the name of this package, as every other console write\n\t\t\t\t// of this provider does. Without it a developer reads a bare error and\n\t\t\t\t// cannot tell that the provider wrote it, and not Vue.\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[@xmachines/play-vue] ActorProvider stopped a second report of this \" +\n\t\t\t\t\t\t\"error. Vue raised it again under the source of a component update, \" +\n\t\t\t\t\t\t\"and it failed no render.\",\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// The SCREEN comes first, and the report second, as `contain()` of play-dom\n\t\t\t// states it. A reporter can throw — one that re-throws to escalate, or a\n\t\t\t// transport that fails — and here the order alone is not enough: the flag\n\t\t\t// schedules a RENDER, and a throw that leaves this hook stops that render and\n\t\t\t// skips the `return false`. The GUARD holds that half, for the four renderers\n\t\t\t// that need it: it carries a `handlerThrew` message, so it contains the throw\n\t\t\t// of the handler and writes it to `console.error`.\n\t\t\t//\n\t\t\t// The five renderers hold the one rule: a handler of the host never defeats the\n\t\t\t// containment, and a host that must escalate raises the failure from a task of\n\t\t\t// its own.\n\t\t\terrored.value = true;\n\t\t\treport(err);\n\t\t\treturn false;\n\t\t});\n\n\t\t// Take the actor out of the reactive proxy of Vue, to reach the raw Signal objects.\n\t\t// A shallowRef holds the actor. Therefore the injected value and the signal watcher\n\t\t// can follow `props.actor` when the prop changes after the mount.\n\t\tconst actorRef = shallowRef(toRaw(props.actor as ViewActor));\n\n\t\t// Take the registryResult out of the proxy, and mark each component as raw, because the reactivity of Vue costs too much here\n\t\tconst rawRegistry = Object.fromEntries(\n\t\t\tObject.entries(toRaw(props.registryResult).registry).map(([k, v]) => [\n\t\t\t\tk,\n\t\t\t\tmarkRaw(v as object),\n\t\t\t]),\n\t\t) as DefineRegistryResult[\"registry\"];\n\n\t\t// Put the onRenderError prop into the registry. The property is not enumerable, and\n\t\t// it replaces the handler of the defineRegistry level. attachRenderErrorHandler makes\n\t\t// a copy, and it changes the registry of the caller never.\n\t\t// The code must call markRaw on the copy again, because a copy does not carry the\n\t\t// non-enumerable flag of markRaw.\n\t\tconst rawRegistryResult: DefineRegistryResult = {\n\t\t\t...toRaw(props.registryResult),\n\t\t\tregistry: markRaw(\n\t\t\t\tprops.onRenderError\n\t\t\t\t\t? attachRenderErrorHandler(rawRegistry, props.onRenderError)\n\t\t\t\t\t: rawRegistry,\n\t\t\t),\n\t\t};\n\n\t\t// Give the actor to each descendant, through the provide and inject mechanism of Vue.\n\t\t//\n\t\t// The code gives a stable Proxy, and not the raw actor. The pattern is the same as\n\t\t// the ViewKey proxy of ActorProviderInner. The injected value therefore follows the\n\t\t// CURRENT `props.actor`: each injector keeps the reference of the setup(), and every\n\t\t// trap resolves against the newest actor (actorRef.value) after a change of the prop.\n\t\t// The proxy forwards each relevant trap. Therefore it behaves like the actor below it\n\t\t// for a read, for a write, for a membership test (`in`), for an enumeration\n\t\t// (Object.keys and a spread), and for a prototype test (`instanceof`). Each method\n\t\t// binds to the current raw actor. Therefore `this`, and also each private field,\n\t\t// works exactly as in a direct call.\n\t\t//\n\t\t// IMPORTANT: this proxy is NOT identity-equal to `props.actor`, because it is a\n\t\t// separate object, and therefore a separate key of a WeakMap. A consumer that uses\n\t\t// the identity as its key must receive the `props.actor` value itself, and never this\n\t\t// injected proxy. The most important such consumer is the guard of one bridge for\n\t\t// each actor in RouterBridgeBase: it uses the identity of the actor as the key of a\n\t\t// WeakMap at the module level.\n\t\t//\n\t\t// The identity of a method: the code keeps each bound method, with the method below\n\t\t// it as the key. A repeated read therefore returns the SAME function\n\t\t// (actor.send === actor.send), and the hot path allocates nothing. A read binds the\n\t\t// method again only after a change of the method below it. The watch handler below\n\t\t// CLEARS the cache on a change of the actor. Therefore each prototype method binds to\n\t\t// the new actor.\n\t\tconst boundMethodCache = new Map<PropertyKey, { source: unknown; bound: unknown }>();\n\t\tprovideActor(\n\t\t\tnew Proxy({} as AnyPlayActor, {\n\t\t\t\tget(_target, prop) {\n\t\t\t\t\tconst current = actorRef.value;\n\t\t\t\t\tconst value = Reflect.get(current, prop, current) as unknown;\n\t\t\t\t\tif (typeof value !== \"function\") return value;\n\t\t\t\t\tconst cached = boundMethodCache.get(prop);\n\t\t\t\t\tif (cached && cached.source === value) return cached.bound;\n\t\t\t\t\tconst bound = (value as (...args: unknown[]) => unknown).bind(current);\n\t\t\t\t\tboundMethodCache.set(prop, { source: value, bound });\n\t\t\t\t\treturn bound;\n\t\t\t\t},\n\t\t\t\thas(_target, prop) {\n\t\t\t\t\treturn prop in actorRef.value;\n\t\t\t\t},\n\t\t\t\tset(_target, prop, value) {\n\t\t\t\t\treturn Reflect.set(actorRef.value, prop, value, actorRef.value);\n\t\t\t\t},\n\t\t\t\tdeleteProperty(_target, prop) {\n\t\t\t\t\treturn Reflect.deleteProperty(actorRef.value, prop);\n\t\t\t\t},\n\t\t\t\townKeys() {\n\t\t\t\t\treturn Reflect.ownKeys(actorRef.value);\n\t\t\t\t},\n\t\t\t\tgetOwnPropertyDescriptor(_target, prop) {\n\t\t\t\t\tconst desc = Reflect.getOwnPropertyDescriptor(actorRef.value, prop);\n\t\t\t\t\tif (!desc) return undefined;\n\t\t\t\t\t// An invariant of a Proxy: the target of this proxy is an empty {}. Therefore each\n\t\t\t\t\t// key of ownKeys must resolve to a configurable descriptor here. Without that, the\n\t\t\t\t\t// [[GetOwnProperty]] invariant throws, because a proxy must not report a property that\n\t\t\t\t\t// its target has not as non-configurable. The own props of the actor are mutable at\n\t\t\t\t\t// run time in each case. Therefore `configurable: true` is correct enough for the\n\t\t\t\t\t// enumeration of Object.keys and of a spread.\n\t\t\t\t\tdesc.configurable = true;\n\t\t\t\t\treturn desc;\n\t\t\t\t},\n\t\t\t\tgetPrototypeOf() {\n\t\t\t\t\treturn Reflect.getPrototypeOf(actorRef.value);\n\t\t\t\t},\n\t\t\t}) as AnyPlayActor,\n\t\t);\n\n\t\t// Seed the first value, then subscribe. Both steps are synchronous, and no scheduler\n\t\t// runs between them.\n\t\t// Solid does the same with createEffect, and Svelte does the same with $effect.\n\t\t// The pattern is therefore the same in each framework.\n\t\tconst view = ref<PlaySpec | null>(actorRef.value.currentView.get());\n\n\t\t// The store lifecycle: it seeds the store again on a change of the viewKey, it\n\t\t// refreshes /context in place in every other case, it resets the store on a change of\n\t\t// the actor, and it guards the identity cache. The shared coordinator comes from\n\t\t// @xmachines/play-actor. Only the wiring of the reactivity below belongs to Vue.\n\t\tconst storeLifecycle = createViewStoreLifecycle((seed) =>\n\t\t\txstateStoreStateStore({ atom: createAtom(seed) }),\n\t\t);\n\t\t// storeKey is the key of a SUBTREE REMOUNT. Therefore it must change only after a\n\t\t// new seed of the store, and never after a refresh of /context in place. Without this\n\t\t// rule, each update of the projection removes the subtree on the screen.\n\t\t// (The controlled mode seeds the store never again. Therefore the key changes never\n\t\t// there.)\n\t\tlet storeKey = 0;\n\n\t\t// The signal watcher connects the TC39 Signals to the reactivity of Vue.\n\t\t// The code assigns it again on a change of the actor. Therefore the cleanup always\n\t\t// releases the live watcher.\n\t\t// Every view emission clears the errored flag (see the reset rule above) before the\n\t\t// new view renders. A hit of the error boundary therefore retries here.\n\t\tlet unwatch = watchSignal(actorRef.value.currentView, (nextView) => {\n\t\t\terrored.value = false;\n\t\t\tfailureLatch.clear();\n\t\t\tview.value = nextView;\n\t\t});\n\n\t\t// The reset watches the STORE too. A render fails on it, so a caller that repairs a\n\t\t// controlled store — without a new emission — must see the children again.\n\t\t// play-react and play-solid hold the same rule, and the actor swap below covers the\n\t\t// third input.\n\t\twatch(\n\t\t\t() => props.store,\n\t\t\t() => {\n\t\t\t\terrored.value = false;\n\t\t\t\tfailureLatch.clear();\n\t\t\t},\n\t\t);\n\n\t\t// React to a change of `props.actor`: cancel the subscription to the currentView of\n\t\t// the OLD actor, seed the view from the NEW actor synchronously (`flush: \"sync\"`, so\n\t\t// that no scheduler runs between the steps, as in the seed and watch pattern of the\n\t\t// mount above), and subscribe to the new signal. The view of the new seed has a new\n\t\t// identity. Therefore the branch of the internal store below builds the store again,\n\t\t// and it changes storeKey, exactly like a normal view transition.\n\t\twatch(\n\t\t\t() => toRaw(props.actor as ViewActor),\n\t\t\t(nextActor) => {\n\t\t\t\tif (nextActor === actorRef.value) return;\n\t\t\t\tunwatch();\n\t\t\t\tactorRef.value = nextActor;\n\t\t\t\t// (The store lifecycle resets itself on a change of the actor, because a store that\n\t\t\t\t// stays alive must not outlive its actor.)\n\t\t\t\t// Clear the cache of the bound methods. Each prototype method therefore binds to the\n\t\t\t\t// new actor on the next read. See the get trap of the injected proxy.\n\t\t\t\tboundMethodCache.clear();\n\t\t\t\t// The new seed is a view emission — clear the errored flag with it.\n\t\t\t\terrored.value = false;\n\t\t\t\tfailureLatch.clear();\n\t\t\t\tview.value = nextActor.currentView.get();\n\t\t\t\tunwatch = watchSignal(nextActor.currentView, (nextView) => {\n\t\t\t\t\terrored.value = false;\n\t\t\t\t\tfailureLatch.clear();\n\t\t\t\t\tview.value = nextView;\n\t\t\t\t});\n\t\t\t},\n\t\t\t{ flush: \"sync\" },\n\t\t);\n\n\t\tonUnmounted(() => {\n\t\t\tguard.dispose();\n\t\t\tunwatch();\n\t\t});\n\n\t\treturn () => {\n\t\t\t// The nonce of the retry. The read makes this render function depend on it, so\n\t\t\t// the reset of the host renders again — see `retry` above.\n\t\t\tvoid retryNonce.value;\n\n\t\t\t// A contained error of a descendant: show the fallback slot, or show nothing,\n\t\t\t// until the next view emission clears the flag and retries the render.\n\t\t\tif (errored.value) {\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\n\t\t\t// No view: show the fallback slot, or show 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, which is external and controlled, or internal for each\n\t\t\t// viewKey, through the shared lifecycle. The children receive the store with the\n\t\t\t// guard, because /context is read-only to the spec. The machinery refreshes the\n\t\t\t// store through the reference without a guard, inside the coordinator.\n\t\t\t//\n\t\t\t// The try CONTAINS a failure of this resolution. `spec.state` can throw — a\n\t\t\t// projection with an accessor, or a seed of a store — and `onErrorCaptured`\n\t\t\t// above sees a throw of a DESCENDANT only, never one of this render function.\n\t\t\t// Without the try such a failure leaves the provider, reaches\n\t\t\t// app.config.errorHandler, and takes the application down, while `onError` and\n\t\t\t// the fallback slot both stay unused.\n\t\t\t//\n\t\t\t// The branch sets the errored flag NOT: a mutation of the state during a render\n\t\t\t// starts a second render. It returns the fallback for THIS render instead, and\n\t\t\t// the next view emission renders the children again — which is the reset rule,\n\t\t\t// with no flag to clear.\n\t\t\tlet resolution;\n\t\t\ttry {\n\t\t\t\tresolution = storeLifecycle.resolve(actorRef.value, spec, props.store);\n\t\t\t} catch (error) {\n\t\t\t\t// LATCH the failure, and report it ONE time. The render function runs again\n\t\t\t\t// for any tracked change, and a report on each attempt would call onError a\n\t\t\t\t// second and a third time for one failure — play-react, play-solid and\n\t\t\t\t// play-svelte each report once.\n\t\t\t\tif (failureLatch.shouldReport(actorRef.value, spec, props.store)) report(error);\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\t\t\tfailureLatch.clear();\n\t\t\tif (resolution.reseeded) storeKey++;\n\t\t\tconst store: StateStore = resolution.guardedStore;\n\n\t\t\t// ActorProviderInner renders inside StateProvider. Therefore 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":""}
|
|
@@ -4,7 +4,7 @@ import { provideActor } from "./useActor.js";
|
|
|
4
4
|
import { computed, defineComponent, getCurrentInstance, h, markRaw, onErrorCaptured, onUnmounted, provide, ref, shallowRef, toRaw, watch } from "vue";
|
|
5
5
|
import { ActionProvider, StateProvider, VisibilityProvider, useStateStore } from "@xmachines/json-render-vue";
|
|
6
6
|
import { watchSignal } from "@xmachines/play-signals";
|
|
7
|
-
import { attachRenderErrorHandler, createFailureLatch, createReportGuard, createViewStoreLifecycle } from "@xmachines/play-
|
|
7
|
+
import { attachRenderErrorHandler, createFailureLatch, createReportGuard, createViewStoreLifecycle } from "@xmachines/play-view";
|
|
8
8
|
import { createAtom } from "@xstate/store";
|
|
9
9
|
import { xstateStoreStateStore } from "@xmachines/json-render-xstate";
|
|
10
10
|
//#region packages/play-vue/src/ActorProvider.vue?vue&type=script&lang.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActorProvider.vue_vue_type_script_lang.js","names":[],"sources":["../src/ActorProvider.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * ActorProvider — the low-level provider of the XMachines Vue render architecture.\n *\n * It owns the complete actor lifecycle:\n * - the signal subscription (watchSignal), which connects the TC39 Signals to the Vue reactivity\n * - the StateStore lifecycle of each view, controlled and uncontrolled\n * - the resolution of the handlers, through ActorProviderInner, which must be inside StateProvider\n * - the ViewContextValue, which it provides through the ViewKey injection key\n * - the ActionProvider wrapper and the VisibilityProvider wrapper, for the Renderer below\n * - the injection of onRenderError into the registry\n *\n * The author of a library who needs this control uses the component directly.\n * An end user takes <PlayUIProvider> instead.\n *\n * @invariant Actor Authority - the Actor decides each state transition, with its guards\n * @invariant Passive Infrastructure - the component observes the signals, and it sends the events\n * @invariant Signal-Only Reactivity - the state of the business logic is in the actor signals\n */\n\nimport {\n\tdefineComponent,\n\tref,\n\tcomputed,\n\ttoRaw,\n\tmarkRaw,\n\tonUnmounted,\n\tonErrorCaptured,\n\th,\n\tprovide,\n\tshallowRef,\n\twatch,\n\tgetCurrentInstance,\n} from \"vue\";\nimport type { ComponentPublicInstance, PropType } from \"vue\";\nimport { watchSignal } from \"@xmachines/play-signals\";\nimport { canRaiseAgain, failsTheRender, isComponentUpdate } from \"./error-source.js\";\nimport {\n\tattachRenderErrorHandler,\n\tcreateFailureLatch,\n\tcreateReportGuard,\n\tcreateViewStoreLifecycle,\n} from \"@xmachines/play-actor\";\nimport type { AbstractActor, Viewable, PlaySpec } from \"@xmachines/play-actor\";\nimport type { AnyActorLogic } from \"xstate\";\nimport type {\n\tDefineRegistryResult,\n\tSetState,\n\tRenderErrorHandler,\n} from \"@xmachines/json-render-vue\";\n\nimport {\n\tStateProvider,\n\tuseStateStore,\n\tActionProvider,\n\tVisibilityProvider,\n} from \"@xmachines/json-render-vue\";\nimport type { StateStore } from \"@xmachines/json-render-core\";\nimport { createAtom } from \"@xstate/store\";\nimport { xstateStoreStateStore } from \"@xmachines/json-render-xstate\";\nimport { provideActor, type AnyPlayActor } from \"./useActor.js\";\nimport { ViewKey, type ViewContextValue } from \"./actor-provider-context.js\";\nimport type { ActorProviderProps } from \"./types.js\";\n\n// Re-export the props type and the context accessors, for a consumer that imports from this file\nexport type { ActorProviderProps } from \"./types.js\";\nexport { usePlayView, getPlayViewContext } from \"./actor-provider-context.js\"; // getPlayViewContext @deprecated\nexport type { ViewContextValue } from \"./actor-provider-context.js\";\n\n// ---------------------------------------------------------------------------\n// ActorProviderInner — it renders inside StateProvider, so that it can call useStateStore()\n// It gives the ViewContextValue, the ActionProvider, and the VisibilityProvider to the Renderer below.\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// The code uses a shallowRef for the value of the view context, because a deep\n\t\t// reactivity costs too much. The Proxy below lets inject() always read the newest\n\t\t// value.\n\t\tconst viewRef = shallowRef<ViewContextValue | null>(null);\n\n\t\t// Give the ViewContextValue through the inject and provide system of Vue.\n\t\t// The code calls provide synchronously in setup(), so that Vue registers the value on\n\t\t// the instance of the component. It uses a Proxy, so that each descendant always\n\t\t// receives the newest 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() during setup(), which is synchronous and before the return.\n\t\t// A Vue composable that uses inject() must run during setup(), and not in a render\n\t\t// function.\n\t\tconst stateStore = useStateStore();\n\n\t\t// Build a SetState adapter, because the factory of the handlers expects the pattern\n\t\t// of an updater function. The code defines it one time, in setup(). It reads\n\t\t// stateStore.getSnapshot and stateStore.update at the moment of the call. Therefore\n\t\t// it always works on the live state of the store, and it needs no new definition when\n\t\t// the identity of stateStore changes between two renders.\n\t\tconst setStateAdapter: SetState = (updater) => {\n\t\t\tconst prev = stateStore.getSnapshot();\n\t\t\tstateStore.update(updater(prev));\n\t\t};\n\n\t\t// Keep the handlers, with the identity of the registryResult prop as the key. The\n\t\t// getter functions are stable closures, and they hold the stateStore of setup().\n\t\t// Therefore they cause no new computation. The code computes the handlers again only\n\t\t// after a change of the definition of the registry, and not on each render of a view\n\t\t// update.\n\t\tconst handlers = computed(() =>\n\t\t\tprops.registryResult.handlers(\n\t\t\t\t() => setStateAdapter,\n\t\t\t\t() => stateStore.getSnapshot(),\n\t\t\t),\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\tviewRef.value = {\n\t\t\t\tspec: props.spec,\n\t\t\t\thandlers: handlers.value,\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 the children with an ActionProvider and a VisibilityProvider. The Renderer of\n\t\t\t// PlayRenderer therefore works also with ActorProvider alone, without PlayUIProvider\n\t\t\t// and without JSONUIProvider.\n\t\t\treturn h(ActionProvider, { handlers: handlers.value }, () =>\n\t\t\t\th(VisibilityProvider, {}, () => slots.default?.() ?? null),\n\t\t\t);\n\t\t};\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// ActorProvider — the 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\tonError: {\n\t\t\ttype: Function as PropType<(error: unknown, reset: () => void) => void>,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\t// The error boundary. It contains an error of the RENDER of a descendant — the\n\t\t// render function, the setup function, and a lifecycle hook — so that a subtree\n\t\t// that throws cannot take the whole application down. `onErrorCaptured` notifies\n\t\t// `onError`, it sets the errored flag, and the render function below then returns\n\t\t// the `fallback` slot instead of the children.\n\t\t//\n\t\t// `onErrorCaptured` of Vue also fires for an error of an event handler, and of a\n\t\t// watcher callback, which no other renderer of this workspace sees: the boundary\n\t\t// of React, the ErrorBoundary of Solid, and `<svelte:boundary>` each catch a\n\t\t// failure of the render only. A click handler that throws must therefore NOT\n\t\t// replace the view with the fallback. This boundary leaves such an error\n\t\t// COMPLETELY alone: it calls `onError` never, and it returns undefined, so the\n\t\t// error continues to `app.config.errorHandler` exactly as it does without this\n\t\t// provider. A report here would give play-vue a wider `onError` contract than the\n\t\t// other four renderers, and it would reach a caller that wires both this prop and\n\t\t// the global handler two times for one click.\n\t\t//\n\t\t// RESET RULE: the next view emission clears the flag (see the callbacks of\n\t\t// watchSignal and the handler of the actor swap). A view transition therefore\n\t\t// retries the render. Without the rule the user sends a recovery event, the actor\n\t\t// moves to a healthy view, and the screen keeps the fallback.\n\t\t//\n\t\t// The per-element boundary of @json-render catches a throw of a catalog component\n\t\t// upstream, and reports it through `onRenderError`. This boundary is the outer net\n\t\t// for everything else.\n\t\t//\n\t\t// The return of false stops a render error HERE. Without `onError` that error\n\t\t// therefore reaches neither `app.config.errorHandler` nor the console, and a\n\t\t// consumer that gives no handler loses it in silence. Write it to `console.error`\n\t\t// in that case, in the same way as the inner renderer does for a component that\n\t\t// throws without an `onRenderError` handler.\n\t\tconst errored = ref(false);\n\t\t/**\n\t\t * The report-once rule, from @xmachines/play-actor.\n\t\t *\n\t\t * It holds the actor, the view and the store of the failure that it reported. A\n\t\t * latch that compared the identity of the ERROR held nothing: an accessor that\n\t\t * throws builds a new one at each read, which is the ordinary shape of a failing\n\t\t * projection. play-svelte and play-solid use the same latch.\n\t\t */\n\t\tconst failureLatch = createFailureLatch();\n\n\t\t// The report guard, from @xmachines/play-actor: it blocks a reset that the host\n\t\t// calls from INSIDE the report, and every callback once the provider is gone. The\n\t\t// five renderers share the one implementation.\n\t\t//\n\t\t// The re-entry rule matters more here than a stack suggests: a write of a ref\n\t\t// renders on the NEXT tick, so a host with a retry-once policy loops with no stack\n\t\t// at all, and a flag around the retry itself would catch nothing.\n\t\t//\n\t\t// It CONTAINS a handler of the host that throws. The flag below schedules a\n\t\t// RENDER, and a throw that leaves the hook stops that render and skips the\n\t\t// `return false`, so the subtree that threw stays on the screen AND the error\n\t\t// reaches `app.config.errorHandler`.\n\t\tconst guard = createReportGuard({\n\t\t\tnoHandler:\n\t\t\t\t\"[@xmachines/play-vue] ActorProvider contained an error of a descendant. \" +\n\t\t\t\t\"Give an onError prop to receive it.\",\n\t\t\thandlerThrew:\n\t\t\t\t\"[@xmachines/play-vue] the onError handler of the host threw. \" +\n\t\t\t\t\"ActorProvider contained the failure of the view all the same.\",\n\t\t});\n\t\t// The retry renders again through a nonce that the render function reads. Clearing\n\t\t// the flags alone renders nothing: `errored.value = false` writes the value that\n\t\t// the ref holds already on the path of a failed RESOLUTION, and Vue schedules\n\t\t// nothing for a write that changes no value.\n\t\tconst retryNonce = ref(0);\n\n\t\t/**\n\t\t * Clears the contained error and renders again.\n\t\t *\n\t\t * The render function reads `view.value`, so the retry resolves the view that the\n\t\t * actor holds NOW. A retry bound to the view that failed would take a host that\n\t\t * keeps the callback back onto the screen that threw.\n\t\t */\n\t\tconst retry = (): void => {\n\t\t\tif (guard.blocked()) return;\n\t\t\terrored.value = false;\n\t\t\tfailureLatch.clear();\n\t\t\tretryNonce.value++;\n\t\t};\n\n\t\t/** Sends a contained failure to `onError`, or to the console when none is given. */\n\t\tconst report = (err: unknown): void => {\n\t\t\tconst handler = props.onError;\n\t\t\tguard.report(err, handler ? () => handler(err, retry) : undefined);\n\t\t};\n\n\t\t// The configuration of the application, read one time. `canRaiseAgain` asks the two\n\t\t// fields that `handleError` of Vue reads on every error. It lives in error-source.ts,\n\t\t// because its production half answers a spelling that a mounted test reaches never.\n\t\tconst appConfig = getCurrentInstance()?.appContext.config;\n\n\t\t// The error that this boundary let through LAST, and that the NEXT raise can still\n\t\t// claim as a repeat.\n\t\t//\n\t\t// The second raise is SYNCHRONOUS and IMMEDIATE. `logError` of the development\n\t\t// bundle re-throws, and the throw unwinds straight back into `callWithErrorHandling`\n\t\t// of `flushJobs`, which reports the same value again. No other raise reaches this\n\t\t// hook in between, so ONE slot holds the complete window.\n\t\t//\n\t\t// EVERY raise that fails the render takes the slot, and not a component update\n\t\t// alone. A record that no raise takes can otherwise outlive its window.\n\t\t//\n\t\t// The record names the INSTANCE beside the error value. A repeat carries the\n\t\t// instance whose JOB ran when the error unwound, and that is the owner of the\n\t\t// watcher OR one of its ANCESTORS: a pre-flush watcher of a child runs inside the\n\t\t// update of the parent that passes it a prop, and `flushJobs` reports the throw\n\t\t// under the parent — or under the outermost ancestor whose update ran. The take\n\t\t// therefore walks the `$parent` chain of the recorded instance. A SIBLING stands on\n\t\t// that chain never, and a sibling that fails with the same cached error is one\n\t\t// loader with two consumers: that failure is a first incident, and the boundary\n\t\t// contains it.\n\t\t//\n\t\t// The microtask is the BACKSTOP, for a raise that neither a repeat nor another\n\t\t// raise follows. A record for the life of the provider would disarm the boundary\n\t\t// for that error for ever, and a descendant that keeps a failure and raises it\n\t\t// again from a LATER render is an ordinary shape.\n\t\t//\n\t\t// Each `remember` schedules a clear of ITS OWN record, and that clear takes no\n\t\t// other. A clear that removed whatever the slot holds could run after a take\n\t\t// emptied the slot and a later `remember` filled it again, and it would then\n\t\t// disarm the boundary for a repeat that is still to come.\n\t\tlet letThrough: { error: unknown; instance: ComponentPublicInstance | null } | null = null;\n\t\tconst remember = (error: unknown, instance: ComponentPublicInstance | null): void => {\n\t\t\tconst record = { error, instance };\n\t\t\tletThrough = record;\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tif (letThrough === record) letThrough = null;\n\t\t\t});\n\t\t};\n\t\t/**\n\t\t * Takes the record. It answers TRUE when the record names this error, and when the\n\t\t * instance that raises it is the recorded one or one of its ancestors.\n\t\t *\n\t\t * The comparison reads `$` on both sides, which is the INTERNAL instance. Vue hands\n\t\t * `onErrorCaptured` the `proxy` of a component, and `$parent` answers with the\n\t\t * `exposeProxy` of a parent that called `expose()` — which the compiler makes every\n\t\t * `<script setup>` component do. The two objects differ, so a walk that compared\n\t\t * them found an exposed ancestor never, and the boundary contained a watcher error\n\t\t * that it must let through. `$` resolves through both proxies and names one object.\n\t\t */\n\t\tconst takeRepeat = (error: unknown, instance: ComponentPublicInstance | null): boolean => {\n\t\t\tconst record = letThrough;\n\t\t\tletThrough = null;\n\t\t\tif (record === null || record.error !== error) return false;\n\t\t\tfor (let node = record.instance; node !== null; node = node.$parent) {\n\t\t\t\tif (node.$ === instance?.$) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\n\t\tonErrorCaptured((err, instance, info) => {\n\t\t\t// An error that did NOT fail the render leaves this boundary untouched: the\n\t\t\t// screen keeps its view, `onError` stays silent, and the return of undefined\n\t\t\t// sends the error on to `app.config.errorHandler`. The boundary of React, the\n\t\t\t// ErrorBoundary of Solid, and `<svelte:boundary>` each see such an error never,\n\t\t\t// so a call of `onError` here would give play-vue a wider contract than the\n\t\t\t// other four, AND it would report the same failure twice to a caller that wires\n\t\t\t// both `onError` and the global handler of the application.\n\t\t\tif (!failsTheRender(info)) {\n\t\t\t\t// Arm the record only where Vue WILL raise this error a second time. A record\n\t\t\t\t// that no repeat claims stands until the microtask, and a genuine failure of\n\t\t\t\t// a render in the same flush — the same descendant, the same cached error —\n\t\t\t\t// would then take it: no fallback, no `onError`, and no screen.\n\t\t\t\t//\n\t\t\t\t// `handleError` of Vue calls `app.config.errorHandler` where one exists, and\n\t\t\t\t// it reaches `logError` never; the production bundle throws only where the\n\t\t\t\t// application asks for `throwUnhandledErrorInProduction`. The one case that\n\t\t\t\t// this line cannot see is an ANCESTOR `errorCaptured` that answers false,\n\t\t\t\t// which stops Vue before `logError` too. That case stays open, and the doc of\n\t\t\t\t// `onError` names it.\n\t\t\t\tif (canRaiseAgain(info, appConfig)) remember(err, instance);\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Vue can raise the SAME error a SECOND time under another source. The\n\t\t\t// development bundle of `logError` re-throws an error that no\n\t\t\t// `app.config.errorHandler` took. A queued job then loses that throw to\n\t\t\t// `callWithErrorHandling(job, job.i, 15)` of `flushJobs` — a `watch` callback\n\t\t\t// with the default `pre` flush, for one — and `flushJobs` reports it again as\n\t\t\t// \"component update\". The classification above answers \"render failure\" for that\n\t\t\t// second spelling, so a watcher that threw would take the view off the screen\n\t\t\t// after all. One error is one incident: an error that this boundary let through\n\t\t\t// in this turn stays through.\n\t\t\t//\n\t\t\t// The gate asks for the SOURCE of the repeat as well. `flushJobs` reports a\n\t\t\t// job under \"component update\", and no other source can carry a second raise\n\t\t\t// to this hook, so a record of the turn can disarm the boundary for that one\n\t\t\t// spelling alone. Without the question a setup function or a render function\n\t\t\t// that throws the SAME error value in the same turn — a loader that caches its\n\t\t\t// rejection, for one — would reach no fallback, no `onError`, and no screen.\n\t\t\t//\n\t\t\t// It returns FALSE, and not undefined. The FIRST raise went on to\n\t\t\t// `app.config.errorHandler`, which is the contract of a non-render source, and\n\t\t\t// the development bundle re-threw it there — that throw IS this second report.\n\t\t\t// A second `undefined` sends it on one more time, so `logError` warns twice for\n\t\t\t// one incident and throws again, and that throw leaves `flushJobs`. The\n\t\t\t// `finally` of `flushJobs` runs `queue.length = 0`. `flushJobs` therefore drops\n\t\t\t// every job behind the one that failed, and a sibling of the descendant that\n\t\t\t// threw renders never. One incident reaches the caller ONE time, and the flush\n\t\t\t// of Vue finishes.\n\t\t\t// `takeRepeat` runs for EVERY raise that fails the render, and not inside the\n\t\t\t// condition below: a raise of another source ENDS the window of the repeat, so\n\t\t\t// the record can disarm the boundary for the one raise that follows it and for\n\t\t\t// no other.\n\t\t\tconst repeated = takeRepeat(err, instance);\n\t\t\tif (isComponentUpdate(info) && repeated) {\n\t\t\t\t// The value reaches no console without this line. `logError` of the\n\t\t\t\t// development bundle writes the SOURCE, and not the value. It then throws\n\t\t\t\t// the value, and that throw IS this second report, which the `return`\n\t\t\t\t// below stops. `report()` wrote such an error before this gate existed, so\n\t\t\t\t// a host with no `onError` prop could still debug a watcher or a loader\n\t\t\t\t// that failed. The production bundle writes the value on the FIRST raise,\n\t\t\t\t// and it throws instead where the application asks for\n\t\t\t\t// `throwUnhandledErrorInProduction` — that application gets the write here.\n\t\t\t\t// One incident reaches the console ONE time in both bundles.\n\t\t\t\t//\n\t\t\t\t// The line carries the name of this package, as every other console write\n\t\t\t\t// of this provider does. Without it a developer reads a bare error and\n\t\t\t\t// cannot tell that the provider wrote it, and not Vue.\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[@xmachines/play-vue] ActorProvider stopped a second report of this \" +\n\t\t\t\t\t\t\"error. Vue raised it again under the source of a component update, \" +\n\t\t\t\t\t\t\"and it failed no render.\",\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// The SCREEN comes first, and the report second, as `contain()` of play-dom\n\t\t\t// states it. A reporter can throw — one that re-throws to escalate, or a\n\t\t\t// transport that fails — and here the order alone is not enough: the flag\n\t\t\t// schedules a RENDER, and a throw that leaves this hook stops that render and\n\t\t\t// skips the `return false`. The GUARD holds that half, for the four renderers\n\t\t\t// that need it: it carries a `handlerThrew` message, so it contains the throw\n\t\t\t// of the handler and writes it to `console.error`.\n\t\t\t//\n\t\t\t// The five renderers hold the one rule: a handler of the host never defeats the\n\t\t\t// containment, and a host that must escalate raises the failure from a task of\n\t\t\t// its own.\n\t\t\terrored.value = true;\n\t\t\treport(err);\n\t\t\treturn false;\n\t\t});\n\n\t\t// Take the actor out of the reactive proxy of Vue, to reach the raw Signal objects.\n\t\t// A shallowRef holds the actor. Therefore the injected value and the signal watcher\n\t\t// can follow `props.actor` when the prop changes after the mount.\n\t\tconst actorRef = shallowRef(toRaw(props.actor as AbstractActor<AnyActorLogic> & Viewable));\n\n\t\t// Take the registryResult out of the proxy, and mark each component as raw, because the reactivity of Vue costs too much here\n\t\tconst rawRegistry = Object.fromEntries(\n\t\t\tObject.entries(toRaw(props.registryResult).registry).map(([k, v]) => [\n\t\t\t\tk,\n\t\t\t\tmarkRaw(v as object),\n\t\t\t]),\n\t\t) as DefineRegistryResult[\"registry\"];\n\n\t\t// Put the onRenderError prop into the registry. The property is not enumerable, and\n\t\t// it replaces the handler of the defineRegistry level. attachRenderErrorHandler makes\n\t\t// a copy, and it changes the registry of the caller never.\n\t\t// The code must call markRaw on the copy again, because a copy does not carry the\n\t\t// non-enumerable flag of markRaw.\n\t\tconst rawRegistryResult: DefineRegistryResult = {\n\t\t\t...toRaw(props.registryResult),\n\t\t\tregistry: markRaw(\n\t\t\t\tprops.onRenderError\n\t\t\t\t\t? attachRenderErrorHandler(rawRegistry, props.onRenderError)\n\t\t\t\t\t: rawRegistry,\n\t\t\t),\n\t\t};\n\n\t\t// Give the actor to each descendant, through the provide and inject mechanism of Vue.\n\t\t//\n\t\t// The code gives a stable Proxy, and not the raw actor. The pattern is the same as\n\t\t// the ViewKey proxy of ActorProviderInner. The injected value therefore follows the\n\t\t// CURRENT `props.actor`: each injector keeps the reference of the setup(), and every\n\t\t// trap resolves against the newest actor (actorRef.value) after a change of the prop.\n\t\t// The proxy forwards each relevant trap. Therefore it behaves like the actor below it\n\t\t// for a read, for a write, for a membership test (`in`), for an enumeration\n\t\t// (Object.keys and a spread), and for a prototype test (`instanceof`). Each method\n\t\t// binds to the current raw actor. Therefore `this`, and also each private field,\n\t\t// works exactly as in a direct call.\n\t\t//\n\t\t// IMPORTANT: this proxy is NOT identity-equal to `props.actor`, because it is a\n\t\t// separate object, and therefore a separate key of a WeakMap. A consumer that uses\n\t\t// the identity as its key must receive the `props.actor` value itself, and never this\n\t\t// injected proxy. The most important such consumer is the guard of one bridge for\n\t\t// each actor in RouterBridgeBase: it uses the identity of the actor as the key of a\n\t\t// WeakMap at the module level.\n\t\t//\n\t\t// The identity of a method: the code keeps each bound method, with the method below\n\t\t// it as the key. A repeated read therefore returns the SAME function\n\t\t// (actor.send === actor.send), and the hot path allocates nothing. A read binds the\n\t\t// method again only after a change of the method below it. The watch handler below\n\t\t// CLEARS the cache on a change of the actor. Therefore each prototype method binds to\n\t\t// the new actor.\n\t\tconst boundMethodCache = new Map<PropertyKey, { source: unknown; bound: unknown }>();\n\t\tprovideActor(\n\t\t\tnew Proxy({} as AnyPlayActor, {\n\t\t\t\tget(_target, prop) {\n\t\t\t\t\tconst current = actorRef.value;\n\t\t\t\t\tconst value = Reflect.get(current, prop, current) as unknown;\n\t\t\t\t\tif (typeof value !== \"function\") return value;\n\t\t\t\t\tconst cached = boundMethodCache.get(prop);\n\t\t\t\t\tif (cached && cached.source === value) return cached.bound;\n\t\t\t\t\tconst bound = (value as (...args: unknown[]) => unknown).bind(current);\n\t\t\t\t\tboundMethodCache.set(prop, { source: value, bound });\n\t\t\t\t\treturn bound;\n\t\t\t\t},\n\t\t\t\thas(_target, prop) {\n\t\t\t\t\treturn prop in actorRef.value;\n\t\t\t\t},\n\t\t\t\tset(_target, prop, value) {\n\t\t\t\t\treturn Reflect.set(actorRef.value, prop, value, actorRef.value);\n\t\t\t\t},\n\t\t\t\tdeleteProperty(_target, prop) {\n\t\t\t\t\treturn Reflect.deleteProperty(actorRef.value, prop);\n\t\t\t\t},\n\t\t\t\townKeys() {\n\t\t\t\t\treturn Reflect.ownKeys(actorRef.value);\n\t\t\t\t},\n\t\t\t\tgetOwnPropertyDescriptor(_target, prop) {\n\t\t\t\t\tconst desc = Reflect.getOwnPropertyDescriptor(actorRef.value, prop);\n\t\t\t\t\tif (!desc) return undefined;\n\t\t\t\t\t// An invariant of a Proxy: the target of this proxy is an empty {}. Therefore each\n\t\t\t\t\t// key of ownKeys must resolve to a configurable descriptor here. Without that, the\n\t\t\t\t\t// [[GetOwnProperty]] invariant throws, because a proxy must not report a property that\n\t\t\t\t\t// its target has not as non-configurable. The own props of the actor are mutable at\n\t\t\t\t\t// run time in each case. Therefore `configurable: true` is correct enough for the\n\t\t\t\t\t// enumeration of Object.keys and of a spread.\n\t\t\t\t\tdesc.configurable = true;\n\t\t\t\t\treturn desc;\n\t\t\t\t},\n\t\t\t\tgetPrototypeOf() {\n\t\t\t\t\treturn Reflect.getPrototypeOf(actorRef.value);\n\t\t\t\t},\n\t\t\t}) as AnyPlayActor,\n\t\t);\n\n\t\t// Seed the first value, then subscribe. Both steps are synchronous, and no scheduler\n\t\t// runs between them.\n\t\t// Solid does the same with createEffect, and Svelte does the same with $effect.\n\t\t// The pattern is therefore the same in each framework.\n\t\tconst view = ref<PlaySpec | null>(actorRef.value.currentView.get());\n\n\t\t// The store lifecycle: it seeds the store again on a change of the viewKey, it\n\t\t// refreshes /context in place in every other case, it resets the store on a change of\n\t\t// the actor, and it guards the identity cache. The shared coordinator comes from\n\t\t// @xmachines/play-actor. Only the wiring of the reactivity below belongs to Vue.\n\t\tconst storeLifecycle = createViewStoreLifecycle((seed) =>\n\t\t\txstateStoreStateStore({ atom: createAtom(seed) }),\n\t\t);\n\t\t// storeKey is the key of a SUBTREE REMOUNT. Therefore it must change only after a\n\t\t// new seed of the store, and never after a refresh of /context in place. Without this\n\t\t// rule, each update of the projection removes the subtree on the screen.\n\t\t// (The controlled mode seeds the store never again. Therefore the key changes never\n\t\t// there.)\n\t\tlet storeKey = 0;\n\n\t\t// The signal watcher connects the TC39 Signals to the reactivity of Vue.\n\t\t// The code assigns it again on a change of the actor. Therefore the cleanup always\n\t\t// releases the live watcher.\n\t\t// Every view emission clears the errored flag (see the reset rule above) before the\n\t\t// new view renders. A hit of the error boundary therefore retries here.\n\t\tlet unwatch = watchSignal(actorRef.value.currentView, (nextView) => {\n\t\t\terrored.value = false;\n\t\t\tfailureLatch.clear();\n\t\t\tview.value = nextView;\n\t\t});\n\n\t\t// The reset watches the STORE too. A render fails on it, so a caller that repairs a\n\t\t// controlled store — without a new emission — must see the children again.\n\t\t// play-react and play-solid hold the same rule, and the actor swap below covers the\n\t\t// third input.\n\t\twatch(\n\t\t\t() => props.store,\n\t\t\t() => {\n\t\t\t\terrored.value = false;\n\t\t\t\tfailureLatch.clear();\n\t\t\t},\n\t\t);\n\n\t\t// React to a change of `props.actor`: cancel the subscription to the currentView of\n\t\t// the OLD actor, seed the view from the NEW actor synchronously (`flush: \"sync\"`, so\n\t\t// that no scheduler runs between the steps, as in the seed and watch pattern of the\n\t\t// mount above), and subscribe to the new signal. The view of the new seed has a new\n\t\t// identity. Therefore the branch of the internal store below builds the store again,\n\t\t// and it changes storeKey, exactly like a normal view transition.\n\t\twatch(\n\t\t\t() => toRaw(props.actor as AbstractActor<AnyActorLogic> & Viewable),\n\t\t\t(nextActor) => {\n\t\t\t\tif (nextActor === actorRef.value) return;\n\t\t\t\tunwatch();\n\t\t\t\tactorRef.value = nextActor;\n\t\t\t\t// (The store lifecycle resets itself on a change of the actor, because a store that\n\t\t\t\t// stays alive must not outlive its actor.)\n\t\t\t\t// Clear the cache of the bound methods. Each prototype method therefore binds to the\n\t\t\t\t// new actor on the next read. See the get trap of the injected proxy.\n\t\t\t\tboundMethodCache.clear();\n\t\t\t\t// The new seed is a view emission — clear the errored flag with it.\n\t\t\t\terrored.value = false;\n\t\t\t\tfailureLatch.clear();\n\t\t\t\tview.value = nextActor.currentView.get();\n\t\t\t\tunwatch = watchSignal(nextActor.currentView, (nextView) => {\n\t\t\t\t\terrored.value = false;\n\t\t\t\t\tfailureLatch.clear();\n\t\t\t\t\tview.value = nextView;\n\t\t\t\t});\n\t\t\t},\n\t\t\t{ flush: \"sync\" },\n\t\t);\n\n\t\tonUnmounted(() => {\n\t\t\tguard.dispose();\n\t\t\tunwatch();\n\t\t});\n\n\t\treturn () => {\n\t\t\t// The nonce of the retry. The read makes this render function depend on it, so\n\t\t\t// the reset of the host renders again — see `retry` above.\n\t\t\tvoid retryNonce.value;\n\n\t\t\t// A contained error of a descendant: show the fallback slot, or show nothing,\n\t\t\t// until the next view emission clears the flag and retries the render.\n\t\t\tif (errored.value) {\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\n\t\t\t// No view: show the fallback slot, or show 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, which is external and controlled, or internal for each\n\t\t\t// viewKey, through the shared lifecycle. The children receive the store with the\n\t\t\t// guard, because /context is read-only to the spec. The machinery refreshes the\n\t\t\t// store through the reference without a guard, inside the coordinator.\n\t\t\t//\n\t\t\t// The try CONTAINS a failure of this resolution. `spec.state` can throw — a\n\t\t\t// projection with an accessor, or a seed of a store — and `onErrorCaptured`\n\t\t\t// above sees a throw of a DESCENDANT only, never one of this render function.\n\t\t\t// Without the try such a failure leaves the provider, reaches\n\t\t\t// app.config.errorHandler, and takes the application down, while `onError` and\n\t\t\t// the fallback slot both stay unused.\n\t\t\t//\n\t\t\t// The branch sets the errored flag NOT: a mutation of the state during a render\n\t\t\t// starts a second render. It returns the fallback for THIS render instead, and\n\t\t\t// the next view emission renders the children again — which is the reset rule,\n\t\t\t// with no flag to clear.\n\t\t\tlet resolution;\n\t\t\ttry {\n\t\t\t\tresolution = storeLifecycle.resolve(actorRef.value, spec, props.store);\n\t\t\t} catch (error) {\n\t\t\t\t// LATCH the failure, and report it ONE time. The render function runs again\n\t\t\t\t// for any tracked change, and a report on each attempt would call onError a\n\t\t\t\t// second and a third time for one failure — play-react, play-solid and\n\t\t\t\t// play-svelte each report once.\n\t\t\t\tif (failureLatch.shouldReport(actorRef.value, spec, props.store)) report(error);\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\t\t\tfailureLatch.clear();\n\t\t\tif (resolution.reseeded) storeKey++;\n\t\t\tconst store: StateStore = resolution.guardedStore;\n\n\t\t\t// ActorProviderInner renders inside StateProvider. Therefore 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":"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"}
|
|
1
|
+
{"version":3,"file":"ActorProvider.vue_vue_type_script_lang.js","names":[],"sources":["../src/ActorProvider.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * ActorProvider — the low-level provider of the XMachines Vue render architecture.\n *\n * It owns the complete actor lifecycle:\n * - the signal subscription (watchSignal), which connects the TC39 Signals to the Vue reactivity\n * - the StateStore lifecycle of each view, controlled and uncontrolled\n * - the resolution of the handlers, through ActorProviderInner, which must be inside StateProvider\n * - the ViewContextValue, which it provides through the ViewKey injection key\n * - the ActionProvider wrapper and the VisibilityProvider wrapper, for the Renderer below\n * - the injection of onRenderError into the registry\n *\n * The author of a library who needs this control uses the component directly.\n * An end user takes <PlayUIProvider> instead.\n *\n * @invariant Actor Authority - the Actor decides each state transition, with its guards\n * @invariant Passive Infrastructure - the component observes the signals, and it sends the events\n * @invariant Signal-Only Reactivity - the state of the business logic is in the actor signals\n */\n\nimport {\n\tdefineComponent,\n\tref,\n\tcomputed,\n\ttoRaw,\n\tmarkRaw,\n\tonUnmounted,\n\tonErrorCaptured,\n\th,\n\tprovide,\n\tshallowRef,\n\twatch,\n\tgetCurrentInstance,\n} from \"vue\";\nimport type { ComponentPublicInstance, PropType } from \"vue\";\nimport { watchSignal } from \"@xmachines/play-signals\";\nimport { canRaiseAgain, failsTheRender, isComponentUpdate } from \"./error-source.js\";\nimport {\n\tattachRenderErrorHandler,\n\tcreateFailureLatch,\n\tcreateReportGuard,\n\tcreateViewStoreLifecycle,\n} from \"@xmachines/play-view\";\nimport type { Viewable, PlaySpec, ViewActor } from \"@xmachines/play-view\";\nimport type {\n\tDefineRegistryResult,\n\tSetState,\n\tRenderErrorHandler,\n} from \"@xmachines/json-render-vue\";\n\nimport {\n\tStateProvider,\n\tuseStateStore,\n\tActionProvider,\n\tVisibilityProvider,\n} from \"@xmachines/json-render-vue\";\nimport type { StateStore } from \"@xmachines/json-render-core\";\nimport { createAtom } from \"@xstate/store\";\nimport { xstateStoreStateStore } from \"@xmachines/json-render-xstate\";\nimport { provideActor, type AnyPlayActor } from \"./useActor.js\";\nimport { ViewKey, type ViewContextValue } from \"./actor-provider-context.js\";\nimport type { ActorProviderProps } from \"./types.js\";\n\n// Re-export the props type and the context accessors, for a consumer that imports from this file\nexport type { ActorProviderProps } from \"./types.js\";\nexport { usePlayView } from \"./actor-provider-context.js\";\nexport type { ViewContextValue } from \"./actor-provider-context.js\";\n\n// ---------------------------------------------------------------------------\n// ActorProviderInner — it renders inside StateProvider, so that it can call useStateStore()\n// It gives the ViewContextValue, the ActionProvider, and the VisibilityProvider to the Renderer below.\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// The code uses a shallowRef for the value of the view context, because a deep\n\t\t// reactivity costs too much. The Proxy below lets inject() always read the newest\n\t\t// value.\n\t\tconst viewRef = shallowRef<ViewContextValue | null>(null);\n\n\t\t// Give the ViewContextValue through the inject and provide system of Vue.\n\t\t// The code calls provide synchronously in setup(), so that Vue registers the value on\n\t\t// the instance of the component. It uses a Proxy, so that each descendant always\n\t\t// receives the newest 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() during setup(), which is synchronous and before the return.\n\t\t// A Vue composable that uses inject() must run during setup(), and not in a render\n\t\t// function.\n\t\tconst stateStore = useStateStore();\n\n\t\t// Build a SetState adapter, because the factory of the handlers expects the pattern\n\t\t// of an updater function. The code defines it one time, in setup(). It reads\n\t\t// stateStore.getSnapshot and stateStore.update at the moment of the call. Therefore\n\t\t// it always works on the live state of the store, and it needs no new definition when\n\t\t// the identity of stateStore changes between two renders.\n\t\tconst setStateAdapter: SetState = (updater) => {\n\t\t\tconst prev = stateStore.getSnapshot();\n\t\t\tstateStore.update(updater(prev));\n\t\t};\n\n\t\t// Keep the handlers, with the identity of the registryResult prop as the key. The\n\t\t// getter functions are stable closures, and they hold the stateStore of setup().\n\t\t// Therefore they cause no new computation. The code computes the handlers again only\n\t\t// after a change of the definition of the registry, and not on each render of a view\n\t\t// update.\n\t\tconst handlers = computed(() =>\n\t\t\tprops.registryResult.handlers(\n\t\t\t\t() => setStateAdapter,\n\t\t\t\t() => stateStore.getSnapshot(),\n\t\t\t),\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\tviewRef.value = {\n\t\t\t\tspec: props.spec,\n\t\t\t\thandlers: handlers.value,\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 the children with an ActionProvider and a VisibilityProvider. The Renderer of\n\t\t\t// PlayRenderer therefore works also with ActorProvider alone, without PlayUIProvider\n\t\t\t// and without JSONUIProvider.\n\t\t\treturn h(ActionProvider, { handlers: handlers.value }, () =>\n\t\t\t\th(VisibilityProvider, {}, () => slots.default?.() ?? null),\n\t\t\t);\n\t\t};\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// ActorProvider — the 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\tonError: {\n\t\t\ttype: Function as PropType<(error: unknown, reset: () => void) => void>,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\t// The error boundary. It contains an error of the RENDER of a descendant — the\n\t\t// render function, the setup function, and a lifecycle hook — so that a subtree\n\t\t// that throws cannot take the whole application down. `onErrorCaptured` notifies\n\t\t// `onError`, it sets the errored flag, and the render function below then returns\n\t\t// the `fallback` slot instead of the children.\n\t\t//\n\t\t// `onErrorCaptured` of Vue also fires for an error of an event handler, and of a\n\t\t// watcher callback, which no other renderer of this workspace sees: the boundary\n\t\t// of React, the ErrorBoundary of Solid, and `<svelte:boundary>` each catch a\n\t\t// failure of the render only. A click handler that throws must therefore NOT\n\t\t// replace the view with the fallback. This boundary leaves such an error\n\t\t// COMPLETELY alone: it calls `onError` never, and it returns undefined, so the\n\t\t// error continues to `app.config.errorHandler` exactly as it does without this\n\t\t// provider. A report here would give play-vue a wider `onError` contract than the\n\t\t// other four renderers, and it would reach a caller that wires both this prop and\n\t\t// the global handler two times for one click.\n\t\t//\n\t\t// RESET RULE: the next view emission clears the flag (see the callbacks of\n\t\t// watchSignal and the handler of the actor swap). A view transition therefore\n\t\t// retries the render. Without the rule the user sends a recovery event, the actor\n\t\t// moves to a healthy view, and the screen keeps the fallback.\n\t\t//\n\t\t// The per-element boundary of @json-render catches a throw of a catalog component\n\t\t// upstream, and reports it through `onRenderError`. This boundary is the outer net\n\t\t// for everything else.\n\t\t//\n\t\t// The return of false stops a render error HERE. Without `onError` that error\n\t\t// therefore reaches neither `app.config.errorHandler` nor the console, and a\n\t\t// consumer that gives no handler loses it in silence. Write it to `console.error`\n\t\t// in that case, in the same way as the inner renderer does for a component that\n\t\t// throws without an `onRenderError` handler.\n\t\tconst errored = ref(false);\n\t\t/**\n\t\t * The report-once rule, from @xmachines/play-actor.\n\t\t *\n\t\t * It holds the actor, the view and the store of the failure that it reported. A\n\t\t * latch that compared the identity of the ERROR held nothing: an accessor that\n\t\t * throws builds a new one at each read, which is the ordinary shape of a failing\n\t\t * projection. play-svelte and play-solid use the same latch.\n\t\t */\n\t\tconst failureLatch = createFailureLatch();\n\n\t\t// The report guard, from @xmachines/play-actor: it blocks a reset that the host\n\t\t// calls from INSIDE the report, and every callback once the provider is gone. The\n\t\t// five renderers share the one implementation.\n\t\t//\n\t\t// The re-entry rule matters more here than a stack suggests: a write of a ref\n\t\t// renders on the NEXT tick, so a host with a retry-once policy loops with no stack\n\t\t// at all, and a flag around the retry itself would catch nothing.\n\t\t//\n\t\t// It CONTAINS a handler of the host that throws. The flag below schedules a\n\t\t// RENDER, and a throw that leaves the hook stops that render and skips the\n\t\t// `return false`, so the subtree that threw stays on the screen AND the error\n\t\t// reaches `app.config.errorHandler`.\n\t\tconst guard = createReportGuard({\n\t\t\tnoHandler:\n\t\t\t\t\"[@xmachines/play-vue] ActorProvider contained an error of a descendant. \" +\n\t\t\t\t\"Give an onError prop to receive it.\",\n\t\t\thandlerThrew:\n\t\t\t\t\"[@xmachines/play-vue] the onError handler of the host threw. \" +\n\t\t\t\t\"ActorProvider contained the failure of the view all the same.\",\n\t\t});\n\t\t// The retry renders again through a nonce that the render function reads. Clearing\n\t\t// the flags alone renders nothing: `errored.value = false` writes the value that\n\t\t// the ref holds already on the path of a failed RESOLUTION, and Vue schedules\n\t\t// nothing for a write that changes no value.\n\t\tconst retryNonce = ref(0);\n\n\t\t/**\n\t\t * Clears the contained error and renders again.\n\t\t *\n\t\t * The render function reads `view.value`, so the retry resolves the view that the\n\t\t * actor holds NOW. A retry bound to the view that failed would take a host that\n\t\t * keeps the callback back onto the screen that threw.\n\t\t */\n\t\tconst retry = (): void => {\n\t\t\tif (guard.blocked()) return;\n\t\t\terrored.value = false;\n\t\t\tfailureLatch.clear();\n\t\t\tretryNonce.value++;\n\t\t};\n\n\t\t/** Sends a contained failure to `onError`, or to the console when none is given. */\n\t\tconst report = (err: unknown): void => {\n\t\t\tconst handler = props.onError;\n\t\t\tguard.report(err, handler ? () => handler(err, retry) : undefined);\n\t\t};\n\n\t\t// The configuration of the application, read one time. `canRaiseAgain` asks the two\n\t\t// fields that `handleError` of Vue reads on every error. It lives in error-source.ts,\n\t\t// because its production half answers a spelling that a mounted test reaches never.\n\t\tconst appConfig = getCurrentInstance()?.appContext.config;\n\n\t\t// The error that this boundary let through LAST, and that the NEXT raise can still\n\t\t// claim as a repeat.\n\t\t//\n\t\t// The second raise is SYNCHRONOUS and IMMEDIATE. `logError` of the development\n\t\t// bundle re-throws, and the throw unwinds straight back into `callWithErrorHandling`\n\t\t// of `flushJobs`, which reports the same value again. No other raise reaches this\n\t\t// hook in between, so ONE slot holds the complete window.\n\t\t//\n\t\t// EVERY raise that fails the render takes the slot, and not a component update\n\t\t// alone. A record that no raise takes can otherwise outlive its window.\n\t\t//\n\t\t// The record names the INSTANCE beside the error value. A repeat carries the\n\t\t// instance whose JOB ran when the error unwound, and that is the owner of the\n\t\t// watcher OR one of its ANCESTORS: a pre-flush watcher of a child runs inside the\n\t\t// update of the parent that passes it a prop, and `flushJobs` reports the throw\n\t\t// under the parent — or under the outermost ancestor whose update ran. The take\n\t\t// therefore walks the `$parent` chain of the recorded instance. A SIBLING stands on\n\t\t// that chain never, and a sibling that fails with the same cached error is one\n\t\t// loader with two consumers: that failure is a first incident, and the boundary\n\t\t// contains it.\n\t\t//\n\t\t// The microtask is the BACKSTOP, for a raise that neither a repeat nor another\n\t\t// raise follows. A record for the life of the provider would disarm the boundary\n\t\t// for that error for ever, and a descendant that keeps a failure and raises it\n\t\t// again from a LATER render is an ordinary shape.\n\t\t//\n\t\t// Each `remember` schedules a clear of ITS OWN record, and that clear takes no\n\t\t// other. A clear that removed whatever the slot holds could run after a take\n\t\t// emptied the slot and a later `remember` filled it again, and it would then\n\t\t// disarm the boundary for a repeat that is still to come.\n\t\tlet letThrough: { error: unknown; instance: ComponentPublicInstance | null } | null = null;\n\t\tconst remember = (error: unknown, instance: ComponentPublicInstance | null): void => {\n\t\t\tconst record = { error, instance };\n\t\t\tletThrough = record;\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tif (letThrough === record) letThrough = null;\n\t\t\t});\n\t\t};\n\t\t/**\n\t\t * Takes the record. It answers TRUE when the record names this error, and when the\n\t\t * instance that raises it is the recorded one or one of its ancestors.\n\t\t *\n\t\t * The comparison reads `$` on both sides, which is the INTERNAL instance. Vue hands\n\t\t * `onErrorCaptured` the `proxy` of a component, and `$parent` answers with the\n\t\t * `exposeProxy` of a parent that called `expose()` — which the compiler makes every\n\t\t * `<script setup>` component do. The two objects differ, so a walk that compared\n\t\t * them found an exposed ancestor never, and the boundary contained a watcher error\n\t\t * that it must let through. `$` resolves through both proxies and names one object.\n\t\t */\n\t\tconst takeRepeat = (error: unknown, instance: ComponentPublicInstance | null): boolean => {\n\t\t\tconst record = letThrough;\n\t\t\tletThrough = null;\n\t\t\tif (record === null || record.error !== error) return false;\n\t\t\tfor (let node = record.instance; node !== null; node = node.$parent) {\n\t\t\t\tif (node.$ === instance?.$) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\n\t\tonErrorCaptured((err, instance, info) => {\n\t\t\t// An error that did NOT fail the render leaves this boundary untouched: the\n\t\t\t// screen keeps its view, `onError` stays silent, and the return of undefined\n\t\t\t// sends the error on to `app.config.errorHandler`. The boundary of React, the\n\t\t\t// ErrorBoundary of Solid, and `<svelte:boundary>` each see such an error never,\n\t\t\t// so a call of `onError` here would give play-vue a wider contract than the\n\t\t\t// other four, AND it would report the same failure twice to a caller that wires\n\t\t\t// both `onError` and the global handler of the application.\n\t\t\tif (!failsTheRender(info)) {\n\t\t\t\t// Arm the record only where Vue WILL raise this error a second time. A record\n\t\t\t\t// that no repeat claims stands until the microtask, and a genuine failure of\n\t\t\t\t// a render in the same flush — the same descendant, the same cached error —\n\t\t\t\t// would then take it: no fallback, no `onError`, and no screen.\n\t\t\t\t//\n\t\t\t\t// `handleError` of Vue calls `app.config.errorHandler` where one exists, and\n\t\t\t\t// it reaches `logError` never; the production bundle throws only where the\n\t\t\t\t// application asks for `throwUnhandledErrorInProduction`. The one case that\n\t\t\t\t// this line cannot see is an ANCESTOR `errorCaptured` that answers false,\n\t\t\t\t// which stops Vue before `logError` too. That case stays open, and the doc of\n\t\t\t\t// `onError` names it.\n\t\t\t\tif (canRaiseAgain(info, appConfig)) remember(err, instance);\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Vue can raise the SAME error a SECOND time under another source. The\n\t\t\t// development bundle of `logError` re-throws an error that no\n\t\t\t// `app.config.errorHandler` took. A queued job then loses that throw to\n\t\t\t// `callWithErrorHandling(job, job.i, 15)` of `flushJobs` — a `watch` callback\n\t\t\t// with the default `pre` flush, for one — and `flushJobs` reports it again as\n\t\t\t// \"component update\". The classification above answers \"render failure\" for that\n\t\t\t// second spelling, so a watcher that threw would take the view off the screen\n\t\t\t// after all. One error is one incident: an error that this boundary let through\n\t\t\t// in this turn stays through.\n\t\t\t//\n\t\t\t// The gate asks for the SOURCE of the repeat as well. `flushJobs` reports a\n\t\t\t// job under \"component update\", and no other source can carry a second raise\n\t\t\t// to this hook, so a record of the turn can disarm the boundary for that one\n\t\t\t// spelling alone. Without the question a setup function or a render function\n\t\t\t// that throws the SAME error value in the same turn — a loader that caches its\n\t\t\t// rejection, for one — would reach no fallback, no `onError`, and no screen.\n\t\t\t//\n\t\t\t// It returns FALSE, and not undefined. The FIRST raise went on to\n\t\t\t// `app.config.errorHandler`, which is the contract of a non-render source, and\n\t\t\t// the development bundle re-threw it there — that throw IS this second report.\n\t\t\t// A second `undefined` sends it on one more time, so `logError` warns twice for\n\t\t\t// one incident and throws again, and that throw leaves `flushJobs`. The\n\t\t\t// `finally` of `flushJobs` runs `queue.length = 0`. `flushJobs` therefore drops\n\t\t\t// every job behind the one that failed, and a sibling of the descendant that\n\t\t\t// threw renders never. One incident reaches the caller ONE time, and the flush\n\t\t\t// of Vue finishes.\n\t\t\t// `takeRepeat` runs for EVERY raise that fails the render, and not inside the\n\t\t\t// condition below: a raise of another source ENDS the window of the repeat, so\n\t\t\t// the record can disarm the boundary for the one raise that follows it and for\n\t\t\t// no other.\n\t\t\tconst repeated = takeRepeat(err, instance);\n\t\t\tif (isComponentUpdate(info) && repeated) {\n\t\t\t\t// The value reaches no console without this line. `logError` of the\n\t\t\t\t// development bundle writes the SOURCE, and not the value. It then throws\n\t\t\t\t// the value, and that throw IS this second report, which the `return`\n\t\t\t\t// below stops. `report()` wrote such an error before this gate existed, so\n\t\t\t\t// a host with no `onError` prop could still debug a watcher or a loader\n\t\t\t\t// that failed. The production bundle writes the value on the FIRST raise,\n\t\t\t\t// and it throws instead where the application asks for\n\t\t\t\t// `throwUnhandledErrorInProduction` — that application gets the write here.\n\t\t\t\t// One incident reaches the console ONE time in both bundles.\n\t\t\t\t//\n\t\t\t\t// The line carries the name of this package, as every other console write\n\t\t\t\t// of this provider does. Without it a developer reads a bare error and\n\t\t\t\t// cannot tell that the provider wrote it, and not Vue.\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[@xmachines/play-vue] ActorProvider stopped a second report of this \" +\n\t\t\t\t\t\t\"error. Vue raised it again under the source of a component update, \" +\n\t\t\t\t\t\t\"and it failed no render.\",\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// The SCREEN comes first, and the report second, as `contain()` of play-dom\n\t\t\t// states it. A reporter can throw — one that re-throws to escalate, or a\n\t\t\t// transport that fails — and here the order alone is not enough: the flag\n\t\t\t// schedules a RENDER, and a throw that leaves this hook stops that render and\n\t\t\t// skips the `return false`. The GUARD holds that half, for the four renderers\n\t\t\t// that need it: it carries a `handlerThrew` message, so it contains the throw\n\t\t\t// of the handler and writes it to `console.error`.\n\t\t\t//\n\t\t\t// The five renderers hold the one rule: a handler of the host never defeats the\n\t\t\t// containment, and a host that must escalate raises the failure from a task of\n\t\t\t// its own.\n\t\t\terrored.value = true;\n\t\t\treport(err);\n\t\t\treturn false;\n\t\t});\n\n\t\t// Take the actor out of the reactive proxy of Vue, to reach the raw Signal objects.\n\t\t// A shallowRef holds the actor. Therefore the injected value and the signal watcher\n\t\t// can follow `props.actor` when the prop changes after the mount.\n\t\tconst actorRef = shallowRef(toRaw(props.actor as ViewActor));\n\n\t\t// Take the registryResult out of the proxy, and mark each component as raw, because the reactivity of Vue costs too much here\n\t\tconst rawRegistry = Object.fromEntries(\n\t\t\tObject.entries(toRaw(props.registryResult).registry).map(([k, v]) => [\n\t\t\t\tk,\n\t\t\t\tmarkRaw(v as object),\n\t\t\t]),\n\t\t) as DefineRegistryResult[\"registry\"];\n\n\t\t// Put the onRenderError prop into the registry. The property is not enumerable, and\n\t\t// it replaces the handler of the defineRegistry level. attachRenderErrorHandler makes\n\t\t// a copy, and it changes the registry of the caller never.\n\t\t// The code must call markRaw on the copy again, because a copy does not carry the\n\t\t// non-enumerable flag of markRaw.\n\t\tconst rawRegistryResult: DefineRegistryResult = {\n\t\t\t...toRaw(props.registryResult),\n\t\t\tregistry: markRaw(\n\t\t\t\tprops.onRenderError\n\t\t\t\t\t? attachRenderErrorHandler(rawRegistry, props.onRenderError)\n\t\t\t\t\t: rawRegistry,\n\t\t\t),\n\t\t};\n\n\t\t// Give the actor to each descendant, through the provide and inject mechanism of Vue.\n\t\t//\n\t\t// The code gives a stable Proxy, and not the raw actor. The pattern is the same as\n\t\t// the ViewKey proxy of ActorProviderInner. The injected value therefore follows the\n\t\t// CURRENT `props.actor`: each injector keeps the reference of the setup(), and every\n\t\t// trap resolves against the newest actor (actorRef.value) after a change of the prop.\n\t\t// The proxy forwards each relevant trap. Therefore it behaves like the actor below it\n\t\t// for a read, for a write, for a membership test (`in`), for an enumeration\n\t\t// (Object.keys and a spread), and for a prototype test (`instanceof`). Each method\n\t\t// binds to the current raw actor. Therefore `this`, and also each private field,\n\t\t// works exactly as in a direct call.\n\t\t//\n\t\t// IMPORTANT: this proxy is NOT identity-equal to `props.actor`, because it is a\n\t\t// separate object, and therefore a separate key of a WeakMap. A consumer that uses\n\t\t// the identity as its key must receive the `props.actor` value itself, and never this\n\t\t// injected proxy. The most important such consumer is the guard of one bridge for\n\t\t// each actor in RouterBridgeBase: it uses the identity of the actor as the key of a\n\t\t// WeakMap at the module level.\n\t\t//\n\t\t// The identity of a method: the code keeps each bound method, with the method below\n\t\t// it as the key. A repeated read therefore returns the SAME function\n\t\t// (actor.send === actor.send), and the hot path allocates nothing. A read binds the\n\t\t// method again only after a change of the method below it. The watch handler below\n\t\t// CLEARS the cache on a change of the actor. Therefore each prototype method binds to\n\t\t// the new actor.\n\t\tconst boundMethodCache = new Map<PropertyKey, { source: unknown; bound: unknown }>();\n\t\tprovideActor(\n\t\t\tnew Proxy({} as AnyPlayActor, {\n\t\t\t\tget(_target, prop) {\n\t\t\t\t\tconst current = actorRef.value;\n\t\t\t\t\tconst value = Reflect.get(current, prop, current) as unknown;\n\t\t\t\t\tif (typeof value !== \"function\") return value;\n\t\t\t\t\tconst cached = boundMethodCache.get(prop);\n\t\t\t\t\tif (cached && cached.source === value) return cached.bound;\n\t\t\t\t\tconst bound = (value as (...args: unknown[]) => unknown).bind(current);\n\t\t\t\t\tboundMethodCache.set(prop, { source: value, bound });\n\t\t\t\t\treturn bound;\n\t\t\t\t},\n\t\t\t\thas(_target, prop) {\n\t\t\t\t\treturn prop in actorRef.value;\n\t\t\t\t},\n\t\t\t\tset(_target, prop, value) {\n\t\t\t\t\treturn Reflect.set(actorRef.value, prop, value, actorRef.value);\n\t\t\t\t},\n\t\t\t\tdeleteProperty(_target, prop) {\n\t\t\t\t\treturn Reflect.deleteProperty(actorRef.value, prop);\n\t\t\t\t},\n\t\t\t\townKeys() {\n\t\t\t\t\treturn Reflect.ownKeys(actorRef.value);\n\t\t\t\t},\n\t\t\t\tgetOwnPropertyDescriptor(_target, prop) {\n\t\t\t\t\tconst desc = Reflect.getOwnPropertyDescriptor(actorRef.value, prop);\n\t\t\t\t\tif (!desc) return undefined;\n\t\t\t\t\t// An invariant of a Proxy: the target of this proxy is an empty {}. Therefore each\n\t\t\t\t\t// key of ownKeys must resolve to a configurable descriptor here. Without that, the\n\t\t\t\t\t// [[GetOwnProperty]] invariant throws, because a proxy must not report a property that\n\t\t\t\t\t// its target has not as non-configurable. The own props of the actor are mutable at\n\t\t\t\t\t// run time in each case. Therefore `configurable: true` is correct enough for the\n\t\t\t\t\t// enumeration of Object.keys and of a spread.\n\t\t\t\t\tdesc.configurable = true;\n\t\t\t\t\treturn desc;\n\t\t\t\t},\n\t\t\t\tgetPrototypeOf() {\n\t\t\t\t\treturn Reflect.getPrototypeOf(actorRef.value);\n\t\t\t\t},\n\t\t\t}) as AnyPlayActor,\n\t\t);\n\n\t\t// Seed the first value, then subscribe. Both steps are synchronous, and no scheduler\n\t\t// runs between them.\n\t\t// Solid does the same with createEffect, and Svelte does the same with $effect.\n\t\t// The pattern is therefore the same in each framework.\n\t\tconst view = ref<PlaySpec | null>(actorRef.value.currentView.get());\n\n\t\t// The store lifecycle: it seeds the store again on a change of the viewKey, it\n\t\t// refreshes /context in place in every other case, it resets the store on a change of\n\t\t// the actor, and it guards the identity cache. The shared coordinator comes from\n\t\t// @xmachines/play-actor. Only the wiring of the reactivity below belongs to Vue.\n\t\tconst storeLifecycle = createViewStoreLifecycle((seed) =>\n\t\t\txstateStoreStateStore({ atom: createAtom(seed) }),\n\t\t);\n\t\t// storeKey is the key of a SUBTREE REMOUNT. Therefore it must change only after a\n\t\t// new seed of the store, and never after a refresh of /context in place. Without this\n\t\t// rule, each update of the projection removes the subtree on the screen.\n\t\t// (The controlled mode seeds the store never again. Therefore the key changes never\n\t\t// there.)\n\t\tlet storeKey = 0;\n\n\t\t// The signal watcher connects the TC39 Signals to the reactivity of Vue.\n\t\t// The code assigns it again on a change of the actor. Therefore the cleanup always\n\t\t// releases the live watcher.\n\t\t// Every view emission clears the errored flag (see the reset rule above) before the\n\t\t// new view renders. A hit of the error boundary therefore retries here.\n\t\tlet unwatch = watchSignal(actorRef.value.currentView, (nextView) => {\n\t\t\terrored.value = false;\n\t\t\tfailureLatch.clear();\n\t\t\tview.value = nextView;\n\t\t});\n\n\t\t// The reset watches the STORE too. A render fails on it, so a caller that repairs a\n\t\t// controlled store — without a new emission — must see the children again.\n\t\t// play-react and play-solid hold the same rule, and the actor swap below covers the\n\t\t// third input.\n\t\twatch(\n\t\t\t() => props.store,\n\t\t\t() => {\n\t\t\t\terrored.value = false;\n\t\t\t\tfailureLatch.clear();\n\t\t\t},\n\t\t);\n\n\t\t// React to a change of `props.actor`: cancel the subscription to the currentView of\n\t\t// the OLD actor, seed the view from the NEW actor synchronously (`flush: \"sync\"`, so\n\t\t// that no scheduler runs between the steps, as in the seed and watch pattern of the\n\t\t// mount above), and subscribe to the new signal. The view of the new seed has a new\n\t\t// identity. Therefore the branch of the internal store below builds the store again,\n\t\t// and it changes storeKey, exactly like a normal view transition.\n\t\twatch(\n\t\t\t() => toRaw(props.actor as ViewActor),\n\t\t\t(nextActor) => {\n\t\t\t\tif (nextActor === actorRef.value) return;\n\t\t\t\tunwatch();\n\t\t\t\tactorRef.value = nextActor;\n\t\t\t\t// (The store lifecycle resets itself on a change of the actor, because a store that\n\t\t\t\t// stays alive must not outlive its actor.)\n\t\t\t\t// Clear the cache of the bound methods. Each prototype method therefore binds to the\n\t\t\t\t// new actor on the next read. See the get trap of the injected proxy.\n\t\t\t\tboundMethodCache.clear();\n\t\t\t\t// The new seed is a view emission — clear the errored flag with it.\n\t\t\t\terrored.value = false;\n\t\t\t\tfailureLatch.clear();\n\t\t\t\tview.value = nextActor.currentView.get();\n\t\t\t\tunwatch = watchSignal(nextActor.currentView, (nextView) => {\n\t\t\t\t\terrored.value = false;\n\t\t\t\t\tfailureLatch.clear();\n\t\t\t\t\tview.value = nextView;\n\t\t\t\t});\n\t\t\t},\n\t\t\t{ flush: \"sync\" },\n\t\t);\n\n\t\tonUnmounted(() => {\n\t\t\tguard.dispose();\n\t\t\tunwatch();\n\t\t});\n\n\t\treturn () => {\n\t\t\t// The nonce of the retry. The read makes this render function depend on it, so\n\t\t\t// the reset of the host renders again — see `retry` above.\n\t\t\tvoid retryNonce.value;\n\n\t\t\t// A contained error of a descendant: show the fallback slot, or show nothing,\n\t\t\t// until the next view emission clears the flag and retries the render.\n\t\t\tif (errored.value) {\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\n\t\t\t// No view: show the fallback slot, or show 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, which is external and controlled, or internal for each\n\t\t\t// viewKey, through the shared lifecycle. The children receive the store with the\n\t\t\t// guard, because /context is read-only to the spec. The machinery refreshes the\n\t\t\t// store through the reference without a guard, inside the coordinator.\n\t\t\t//\n\t\t\t// The try CONTAINS a failure of this resolution. `spec.state` can throw — a\n\t\t\t// projection with an accessor, or a seed of a store — and `onErrorCaptured`\n\t\t\t// above sees a throw of a DESCENDANT only, never one of this render function.\n\t\t\t// Without the try such a failure leaves the provider, reaches\n\t\t\t// app.config.errorHandler, and takes the application down, while `onError` and\n\t\t\t// the fallback slot both stay unused.\n\t\t\t//\n\t\t\t// The branch sets the errored flag NOT: a mutation of the state during a render\n\t\t\t// starts a second render. It returns the fallback for THIS render instead, and\n\t\t\t// the next view emission renders the children again — which is the reset rule,\n\t\t\t// with no flag to clear.\n\t\t\tlet resolution;\n\t\t\ttry {\n\t\t\t\tresolution = storeLifecycle.resolve(actorRef.value, spec, props.store);\n\t\t\t} catch (error) {\n\t\t\t\t// LATCH the failure, and report it ONE time. The render function runs again\n\t\t\t\t// for any tracked change, and a report on each attempt would call onError a\n\t\t\t\t// second and a third time for one failure — play-react, play-solid and\n\t\t\t\t// play-svelte each report once.\n\t\t\t\tif (failureLatch.shouldReport(actorRef.value, spec, props.store)) report(error);\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\t\t\tfailureLatch.clear();\n\t\t\tif (resolution.reseeded) storeKey++;\n\t\t\tconst store: StateStore = resolution.guardedStore;\n\n\t\t\t// ActorProviderInner renders inside StateProvider. Therefore 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":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayUIProvider.js","names":[],"sources":["../src/PlayUIProvider.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * PlayUIProvider — the composite provider of an actor-driven UI.\n *\n * It wraps ActorProvider and JSONUIProvider, from @xmachines/json-render-vue. One\n * component therefore serves the complete actor lifecycle and the complete UI\n * rendering.\n *\n * The standard use:\n * ```ts\n * <PlayUIProvider :actor=\"actor\" :registryResult=\"registryResult\">\n * <PlayRenderer />\n * </PlayUIProvider>\n * ```\n *\n * For a custom provider composition, use <ActorProvider> directly.\n *\n * @packageDocumentation\n */\n\nimport { defineComponent, h } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { JSONUIProvider } from \"@xmachines/json-render-vue\";\nimport type { DefineRegistryResult, RenderErrorHandler } from \"@xmachines/json-render-vue\";\n\nimport type { StateStore } from \"@xmachines/json-render-core\";\nimport type {
|
|
1
|
+
{"version":3,"file":"PlayUIProvider.js","names":[],"sources":["../src/PlayUIProvider.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * PlayUIProvider — the composite provider of an actor-driven UI.\n *\n * It wraps ActorProvider and JSONUIProvider, from @xmachines/json-render-vue. One\n * component therefore serves the complete actor lifecycle and the complete UI\n * rendering.\n *\n * The standard use:\n * ```ts\n * <PlayUIProvider :actor=\"actor\" :registryResult=\"registryResult\">\n * <PlayRenderer />\n * </PlayUIProvider>\n * ```\n *\n * For a custom provider composition, use <ActorProvider> directly.\n *\n * @packageDocumentation\n */\n\nimport { defineComponent, h } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { JSONUIProvider } from \"@xmachines/json-render-vue\";\nimport type { DefineRegistryResult, RenderErrorHandler } from \"@xmachines/json-render-vue\";\n\nimport type { StateStore } from \"@xmachines/json-render-core\";\nimport type { Viewable, ViewActor } from \"@xmachines/play-view\";\nimport ActorProvider from \"./ActorProvider.vue\";\nimport { usePlayView } from \"./actor-provider-context.js\";\nimport type { PlayUIProviderProps } from \"./types.js\";\n\n// Re-export the props type, for a consumer that imports from this file\nexport type { PlayUIProviderProps } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// JSONUIBridge — the inner component inside the tree of ActorProvider\n// It reads the ViewContextValue with usePlayView(), and it gives the handlers\n// and the registry to JSONUIProvider. Therefore ActorProvider and JSONUIProvider\n// share one state.\n// ---------------------------------------------------------------------------\n\nconst JSONUIBridge = defineComponent({\n\tname: \"JSONUIBridge\",\n\tprops: {\n\t\tnavigate: {\n\t\t\ttype: Function as PropType<(path: string) => void>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tvalidationFunctions: {\n\t\t\ttype: Object as PropType<\n\t\t\t\tRecord<string, (value: unknown, args?: Record<string, unknown>) => boolean>\n\t\t\t>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tfunctions: {\n\t\t\ttype: Object as PropType<\n\t\t\t\tRecord<\n\t\t\t\t\tstring,\n\t\t\t\t\t(args?: Record<string, unknown>, state?: Record<string, unknown>) => unknown\n\t\t\t\t>\n\t\t\t>,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\treturn () => {\n\t\t\tconst view = usePlayView();\n\t\t\treturn h(\n\t\t\t\tJSONUIProvider,\n\t\t\t\t{\n\t\t\t\t\tregistry: view.registry,\n\t\t\t\t\thandlers: view.handlers,\n\t\t\t\t\tstore: view.store,\n\t\t\t\t\t...(props.navigate ? { navigate: props.navigate } : {}),\n\t\t\t\t\t...(props.validationFunctions\n\t\t\t\t\t\t? { validationFunctions: props.validationFunctions }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(props.functions ? { functions: props.functions } : {}),\n\t\t\t\t},\n\t\t\t\tslots,\n\t\t\t);\n\t\t};\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// PlayUIProvider — the main export\n// ---------------------------------------------------------------------------\n\nexport default defineComponent({\n\tname: \"PlayUIProvider\",\n\tprops: {\n\t\tactor: {\n\t\t\ttype: Object as PropType<ViewActor>,\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\tonError: {\n\t\t\ttype: Function as PropType<(error: unknown, reset: () => void) => void>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tnavigate: {\n\t\t\ttype: Function as PropType<PlayUIProviderProps[\"navigate\"]>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tvalidationFunctions: {\n\t\t\ttype: Object as PropType<PlayUIProviderProps[\"validationFunctions\"]>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tfunctions: {\n\t\t\ttype: Object as PropType<PlayUIProviderProps[\"functions\"]>,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\treturn () =>\n\t\t\th(\n\t\t\t\tActorProvider,\n\t\t\t\t{\n\t\t\t\t\tactor: props.actor,\n\t\t\t\t\tregistryResult: props.registryResult,\n\t\t\t\t\t...(props.store ? { store: props.store } : {}),\n\t\t\t\t\t...(props.onRenderError ? { onRenderError: props.onRenderError } : {}),\n\t\t\t\t\t...(props.onError ? { onError: props.onError } : {}),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdefault: () =>\n\t\t\t\t\t\th(\n\t\t\t\t\t\t\tJSONUIBridge,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t...(props.navigate ? { navigate: props.navigate } : {}),\n\t\t\t\t\t\t\t\t...(props.validationFunctions\n\t\t\t\t\t\t\t\t\t? { validationFunctions: props.validationFunctions }\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t...(props.functions ? { functions: props.functions } : {}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tslots,\n\t\t\t\t\t\t),\n\t\t\t\t\tfallback: slots.fallback,\n\t\t\t\t},\n\t\t\t);\n\t},\n});\n</script>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayUIProvider.vue_vue_type_script_lang.js","names":[],"sources":["../src/PlayUIProvider.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * PlayUIProvider — the composite provider of an actor-driven UI.\n *\n * It wraps ActorProvider and JSONUIProvider, from @xmachines/json-render-vue. One\n * component therefore serves the complete actor lifecycle and the complete UI\n * rendering.\n *\n * The standard use:\n * ```ts\n * <PlayUIProvider :actor=\"actor\" :registryResult=\"registryResult\">\n * <PlayRenderer />\n * </PlayUIProvider>\n * ```\n *\n * For a custom provider composition, use <ActorProvider> directly.\n *\n * @packageDocumentation\n */\n\nimport { defineComponent, h } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { JSONUIProvider } from \"@xmachines/json-render-vue\";\nimport type { DefineRegistryResult, RenderErrorHandler } from \"@xmachines/json-render-vue\";\n\nimport type { StateStore } from \"@xmachines/json-render-core\";\nimport type {
|
|
1
|
+
{"version":3,"file":"PlayUIProvider.vue_vue_type_script_lang.js","names":[],"sources":["../src/PlayUIProvider.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * PlayUIProvider — the composite provider of an actor-driven UI.\n *\n * It wraps ActorProvider and JSONUIProvider, from @xmachines/json-render-vue. One\n * component therefore serves the complete actor lifecycle and the complete UI\n * rendering.\n *\n * The standard use:\n * ```ts\n * <PlayUIProvider :actor=\"actor\" :registryResult=\"registryResult\">\n * <PlayRenderer />\n * </PlayUIProvider>\n * ```\n *\n * For a custom provider composition, use <ActorProvider> directly.\n *\n * @packageDocumentation\n */\n\nimport { defineComponent, h } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { JSONUIProvider } from \"@xmachines/json-render-vue\";\nimport type { DefineRegistryResult, RenderErrorHandler } from \"@xmachines/json-render-vue\";\n\nimport type { StateStore } from \"@xmachines/json-render-core\";\nimport type { Viewable, ViewActor } from \"@xmachines/play-view\";\nimport ActorProvider from \"./ActorProvider.vue\";\nimport { usePlayView } from \"./actor-provider-context.js\";\nimport type { PlayUIProviderProps } from \"./types.js\";\n\n// Re-export the props type, for a consumer that imports from this file\nexport type { PlayUIProviderProps } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// JSONUIBridge — the inner component inside the tree of ActorProvider\n// It reads the ViewContextValue with usePlayView(), and it gives the handlers\n// and the registry to JSONUIProvider. Therefore ActorProvider and JSONUIProvider\n// share one state.\n// ---------------------------------------------------------------------------\n\nconst JSONUIBridge = defineComponent({\n\tname: \"JSONUIBridge\",\n\tprops: {\n\t\tnavigate: {\n\t\t\ttype: Function as PropType<(path: string) => void>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tvalidationFunctions: {\n\t\t\ttype: Object as PropType<\n\t\t\t\tRecord<string, (value: unknown, args?: Record<string, unknown>) => boolean>\n\t\t\t>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tfunctions: {\n\t\t\ttype: Object as PropType<\n\t\t\t\tRecord<\n\t\t\t\t\tstring,\n\t\t\t\t\t(args?: Record<string, unknown>, state?: Record<string, unknown>) => unknown\n\t\t\t\t>\n\t\t\t>,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\treturn () => {\n\t\t\tconst view = usePlayView();\n\t\t\treturn h(\n\t\t\t\tJSONUIProvider,\n\t\t\t\t{\n\t\t\t\t\tregistry: view.registry,\n\t\t\t\t\thandlers: view.handlers,\n\t\t\t\t\tstore: view.store,\n\t\t\t\t\t...(props.navigate ? { navigate: props.navigate } : {}),\n\t\t\t\t\t...(props.validationFunctions\n\t\t\t\t\t\t? { validationFunctions: props.validationFunctions }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(props.functions ? { functions: props.functions } : {}),\n\t\t\t\t},\n\t\t\t\tslots,\n\t\t\t);\n\t\t};\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// PlayUIProvider — the main export\n// ---------------------------------------------------------------------------\n\nexport default defineComponent({\n\tname: \"PlayUIProvider\",\n\tprops: {\n\t\tactor: {\n\t\t\ttype: Object as PropType<ViewActor>,\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\tonError: {\n\t\t\ttype: Function as PropType<(error: unknown, reset: () => void) => void>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tnavigate: {\n\t\t\ttype: Function as PropType<PlayUIProviderProps[\"navigate\"]>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tvalidationFunctions: {\n\t\t\ttype: Object as PropType<PlayUIProviderProps[\"validationFunctions\"]>,\n\t\t\tdefault: undefined,\n\t\t},\n\t\tfunctions: {\n\t\t\ttype: Object as PropType<PlayUIProviderProps[\"functions\"]>,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\treturn () =>\n\t\t\th(\n\t\t\t\tActorProvider,\n\t\t\t\t{\n\t\t\t\t\tactor: props.actor,\n\t\t\t\t\tregistryResult: props.registryResult,\n\t\t\t\t\t...(props.store ? { store: props.store } : {}),\n\t\t\t\t\t...(props.onRenderError ? { onRenderError: props.onRenderError } : {}),\n\t\t\t\t\t...(props.onError ? { onError: props.onError } : {}),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdefault: () =>\n\t\t\t\t\t\th(\n\t\t\t\t\t\t\tJSONUIBridge,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t...(props.navigate ? { navigate: props.navigate } : {}),\n\t\t\t\t\t\t\t\t...(props.validationFunctions\n\t\t\t\t\t\t\t\t\t? { validationFunctions: props.validationFunctions }\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t...(props.functions ? { functions: props.functions } : {}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tslots,\n\t\t\t\t\t\t),\n\t\t\t\t\tfallback: slots.fallback,\n\t\t\t\t},\n\t\t\t);\n\t},\n});\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyCA,IAAM,eAAe,gBAAgB;CACpC,MAAM;CACN,OAAO;EACN,UAAU;GACT,MAAM;GACN,SAAS,KAAA;EACV;EACA,qBAAqB;GACpB,MAAM;GAGN,SAAS,KAAA;EACV;EACA,WAAW;GACV,MAAM;GAMN,SAAS,KAAA;EACV;CACD;CACA,MAAM,OAAO,EAAE,SAAS;EACvB,aAAa;GACZ,MAAM,OAAO,YAAY;GACzB,OAAO,EACN,gBACA;IACC,UAAU,KAAK;IACf,UAAU,KAAK;IACf,OAAO,KAAK;IACZ,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;IACrD,GAAI,MAAM,sBACP,EAAE,qBAAqB,MAAM,oBAAoB,IACjD,CAAC;IACJ,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;GACzD,GACA,KACD;EACD;CACD;AACD,CAAC;AAMD,IAAA,kDAAe,gBAAgB;CAC9B,MAAM;CACN,OAAO;EACN,OAAO;GACN,MAAM;GACN,UAAU;EACX;EACA,gBAAgB;GACf,MAAM;GACN,UAAU;EACX;EACA,OAAO;GACN,MAAM;GACN,SAAS,KAAA;EACV;EACA,eAAe;GACd,MAAM;GACN,SAAS,KAAA;EACV;EACA,SAAS;GACR,MAAM;GACN,SAAS,KAAA;EACV;EACA,UAAU;GACT,MAAM;GACN,SAAS,KAAA;EACV;EACA,qBAAqB;GACpB,MAAM;GACN,SAAS,KAAA;EACV;EACA,WAAW;GACV,MAAM;GACN,SAAS,KAAA;EACV;CACD;CACA,MAAM,OAAO,EAAE,SAAS;EACvB,aACC,EACC,uBACA;GACC,OAAO,MAAM;GACb,gBAAgB,MAAM;GACtB,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;GAC5C,GAAI,MAAM,gBAAgB,EAAE,eAAe,MAAM,cAAc,IAAI,CAAC;GACpE,GAAI,MAAM,UAAU,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;EACnD,GACA;GACC,eACC,EACC,cACA;IACC,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;IACrD,GAAI,MAAM,sBACP,EAAE,qBAAqB,MAAM,oBAAoB,IACjD,CAAC;IACJ,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;GACzD,GACA,KACD;GACD,UAAU,MAAM;EACjB,CACD;CACF;AACD,CAAC"}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { InjectionKey } from "vue";
|
|
12
12
|
import type { ComponentRegistry } from "@xmachines/json-render-vue";
|
|
13
|
-
import type { BaseViewContextValue } from "@xmachines/play-
|
|
13
|
+
import type { BaseViewContextValue } from "@xmachines/play-view";
|
|
14
14
|
/**
|
|
15
15
|
* The context value that ActorProvider provides and PlayRenderer, the leaf without
|
|
16
16
|
* props, reads. usePlayView() gives it inside every ActorProvider tree and
|
|
@@ -44,10 +44,4 @@ export declare const ViewKey: InjectionKey<ViewContextValue>;
|
|
|
44
44
|
* warning. play-svelte and play-solid hold the same rule, for the same reason.
|
|
45
45
|
*/
|
|
46
46
|
export declare function usePlayView(): ViewContextValue;
|
|
47
|
-
/**
|
|
48
|
-
* Access the current ViewContextValue from inside an ActorProvider tree.
|
|
49
|
-
*
|
|
50
|
-
* @deprecated Renamed to {@link usePlayView}; will be removed in the next major.
|
|
51
|
-
*/
|
|
52
|
-
export declare const getPlayViewContext: typeof usePlayView;
|
|
53
47
|
//# sourceMappingURL=actor-provider-context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actor-provider-context.d.ts","sourceRoot":"","sources":["../src/actor-provider-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAExC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"actor-provider-context.d.ts","sourceRoot":"","sources":["../src/actor-provider-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAExC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEjE;;;;GAIG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;CAAG;AAEpF;;;;;GAKG;AACH,eAAO,MAAM,OAAO,EAAE,YAAY,CAAC,gBAAgB,CAA4B,CAAC;AAEhF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,IAAI,gBAAgB,CAE9C"}
|
|
@@ -39,13 +39,7 @@ var ViewKey = Symbol("xmachines.view");
|
|
|
39
39
|
function usePlayView() {
|
|
40
40
|
return assertNonNullable(inject(ViewKey), "ViewKey");
|
|
41
41
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Access the current ViewContextValue from inside an ActorProvider tree.
|
|
44
|
-
*
|
|
45
|
-
* @deprecated Renamed to {@link usePlayView}; will be removed in the next major.
|
|
46
|
-
*/
|
|
47
|
-
var getPlayViewContext = usePlayView;
|
|
48
42
|
//#endregion
|
|
49
|
-
export { ViewKey,
|
|
43
|
+
export { ViewKey, usePlayView };
|
|
50
44
|
|
|
51
45
|
//# sourceMappingURL=actor-provider-context.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actor-provider-context.js","names":[],"sources":["../src/actor-provider-context.ts"],"sourcesContent":["/**\n * ViewContextValue and usePlayView — the shared context types of the Vue provider architecture.\n *\n * This file holds them, and not ActorProvider.vue, so that TypeScript can\n * re-export the types from index.ts. (The file vue-shim.d.ts declares a default\n * export of each *.vue file only. Therefore TypeScript does not see a named export\n * of a .vue SFC when it resolves a re-export.)\n *\n * @packageDocumentation\n */\n\nimport { inject } from \"vue\";\nimport type { InjectionKey } from \"vue\";\nimport { assertNonNullable } from \"@xmachines/play\";\nimport type { ComponentRegistry } from \"@xmachines/json-render-vue\";\nimport type { BaseViewContextValue } from \"@xmachines/play-
|
|
1
|
+
{"version":3,"file":"actor-provider-context.js","names":[],"sources":["../src/actor-provider-context.ts"],"sourcesContent":["/**\n * ViewContextValue and usePlayView — the shared context types of the Vue provider architecture.\n *\n * This file holds them, and not ActorProvider.vue, so that TypeScript can\n * re-export the types from index.ts. (The file vue-shim.d.ts declares a default\n * export of each *.vue file only. Therefore TypeScript does not see a named export\n * of a .vue SFC when it resolves a re-export.)\n *\n * @packageDocumentation\n */\n\nimport { inject } from \"vue\";\nimport type { InjectionKey } from \"vue\";\nimport { assertNonNullable } from \"@xmachines/play\";\nimport type { ComponentRegistry } from \"@xmachines/json-render-vue\";\nimport type { BaseViewContextValue } from \"@xmachines/play-view\";\n\n/**\n * The context value that ActorProvider provides and PlayRenderer, the leaf without\n * props, reads. usePlayView() gives it inside every ActorProvider tree and\n * PlayUIProvider tree.\n */\nexport interface ViewContextValue extends BaseViewContextValue<ComponentRegistry> {}\n\n/**\n * The injection key of the ViewContextValue. This file exports it, so that\n * ActorProvider.vue uses it as the canonical key and declares no second key.\n *\n * @internal Use usePlayView() as the public API. Never inject ViewKey directly.\n */\nexport const ViewKey: InjectionKey<ViewContextValue> = Symbol(\"xmachines.view\");\n\n/**\n * Returns the current ViewContextValue inside an ActorProvider tree.\n *\n * @throws {Error} When the caller is outside an <ActorProvider> tree or a <PlayUIProvider> tree\n *\n * @example\n * ```typescript\n * import { usePlayView } from \"@xmachines/play-vue\";\n *\n * const view = usePlayView();\n * // view.spec, view.handlers, view.registry\n * ```\n *\n * **Read a field where you use it, and destructure it not.** The return value is a Proxy\n * over the live context of the provider, so `const { spec } = usePlayView()` freezes at\n * the first read and the screen stops following the actor — with no error and no\n * warning. play-svelte and play-solid hold the same rule, for the same reason.\n */\nexport function usePlayView(): ViewContextValue {\n\treturn assertNonNullable(inject(ViewKey), \"ViewKey\");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA8BA,IAAa,UAA0C,OAAO,gBAAgB;;;;;;;;;;;;;;;;;;;AAoB9E,SAAgB,cAAgC;CAC/C,OAAO,kBAAkB,OAAO,OAAO,GAAG,SAAS;AACpD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
export { default as PlayRenderer } from "./PlayRenderer.vue";
|
|
16
16
|
export { default as ActorProvider } from "./ActorProvider.vue";
|
|
17
17
|
export { default as PlayUIProvider } from "./PlayUIProvider.vue";
|
|
18
|
-
export type { ActorProviderProps, PlayUIProviderProps, RenderErrorHandler
|
|
19
|
-
export { usePlayView
|
|
18
|
+
export type { ActorProviderProps, PlayUIProviderProps, RenderErrorHandler } from "./types.js";
|
|
19
|
+
export { usePlayView } from "./actor-provider-context.js";
|
|
20
20
|
export type { ViewContextValue } from "./actor-provider-context.js";
|
|
21
21
|
export { useActor } from "./useActor.js";
|
|
22
22
|
export type { AnyPlayActor } from "./useActor.js";
|
|
@@ -24,4 +24,5 @@ export { defineRegistry } from "./define-registry.js";
|
|
|
24
24
|
export type { DefineRegistryOptions, ComponentsMap, ComponentEntry } from "./define-registry.js";
|
|
25
25
|
export { JSONUIProvider, StateProvider, ActionProvider, VisibilityProvider, ValidationProvider, Renderer, useBoundProp, useFieldValidation, } from "@xmachines/json-render-vue";
|
|
26
26
|
export type { JSONUIProviderProps, StateProviderProps, ActionProviderProps, ValidationProviderProps, RendererProps, ComponentFn, ComponentContext, DefineRegistryResult, } from "@xmachines/json-render-vue";
|
|
27
|
+
export { schema } from "@xmachines/json-render-vue";
|
|
27
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGjE,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGjE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAGpE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGjG,OAAO,EACN,cAAc,EACd,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,YAAY,EACZ,kBAAkB,GAClB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACX,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,oBAAoB,GACpB,MAAM,4BAA4B,CAAC;AAQpC,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { usePlayView } from "./actor-provider-context.js";
|
|
2
2
|
import PlayRenderer_default from "./PlayRenderer.js";
|
|
3
3
|
import { useActor } from "./useActor.js";
|
|
4
4
|
import ActorProvider_default from "./ActorProvider.js";
|
|
5
5
|
import PlayUIProvider_default from "./PlayUIProvider.js";
|
|
6
6
|
import { defineRegistry } from "./define-registry.js";
|
|
7
|
-
import { ActionProvider, JSONUIProvider, Renderer, StateProvider, ValidationProvider, VisibilityProvider, useBoundProp, useFieldValidation } from "@xmachines/json-render-vue";
|
|
8
|
-
export { ActionProvider, ActorProvider_default as ActorProvider, JSONUIProvider, PlayRenderer_default as PlayRenderer, PlayUIProvider_default as PlayUIProvider, Renderer, StateProvider, ValidationProvider, VisibilityProvider, defineRegistry,
|
|
7
|
+
import { ActionProvider, JSONUIProvider, Renderer, StateProvider, ValidationProvider, VisibilityProvider, schema, useBoundProp, useFieldValidation } from "@xmachines/json-render-vue";
|
|
8
|
+
export { ActionProvider, ActorProvider_default as ActorProvider, JSONUIProvider, PlayRenderer_default as PlayRenderer, PlayUIProvider_default as PlayUIProvider, Renderer, StateProvider, ValidationProvider, VisibilityProvider, defineRegistry, schema, useActor, useBoundProp, useFieldValidation, usePlayView };
|
package/dist/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/
|
|
6
6
|
import type { DefineRegistryResult } from "@xmachines/json-render-vue";
|
|
7
|
-
import type { BaseActorProviderProps } from "@xmachines/play-
|
|
7
|
+
import type { BaseActorProviderProps } from "@xmachines/play-view";
|
|
8
8
|
/**
|
|
9
9
|
* The props of the ActorProvider component.
|
|
10
10
|
* This file holds them, so that TypeScript can re-export them. The vue-shim has a
|
|
@@ -77,11 +77,6 @@ export interface ActorProviderProps extends BaseActorProviderProps<DefineRegistr
|
|
|
77
77
|
* The props of PlayUIProvider — every ActorProvider prop, and also the props of JSONUIProvider.
|
|
78
78
|
*/
|
|
79
79
|
export type { RenderErrorHandler } from "@xmachines/json-render-vue";
|
|
80
|
-
/**
|
|
81
|
-
* @deprecated Unused; will be removed in the next major.
|
|
82
|
-
*/
|
|
83
|
-
export interface VisibilityProviderProps {
|
|
84
|
-
}
|
|
85
80
|
export interface PlayUIProviderProps extends ActorProviderProps {
|
|
86
81
|
/** The navigate function. The provider gives it to JSONUIProvider, which resolves each link with it */
|
|
87
82
|
navigate?: (path: string) => void;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAmB,SAAQ,sBAAsB,CAAC,oBAAoB,CAAC;IACvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuDG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CACtD;AAED;;GAEG;AACH,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAErE,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC9D,uGAAuG;IACvG,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,MAAM,CAC3B,MAAM,EACN,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAC3D,CAAC;IACF,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CACjB,MAAM,EACN,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAC5E,CAAC;CACF"}
|
package/dist/useActor.d.ts
CHANGED
|
@@ -34,12 +34,19 @@
|
|
|
34
34
|
* @packageDocumentation
|
|
35
35
|
*/
|
|
36
36
|
import type { InjectionKey } from "vue";
|
|
37
|
-
import type {
|
|
38
|
-
import type { AnyActorLogic } from "xstate";
|
|
37
|
+
import type { PlayActor } from "@xmachines/play-actor";
|
|
39
38
|
/**
|
|
40
|
-
* The
|
|
39
|
+
* The actor type of the context of this renderer.
|
|
40
|
+
*
|
|
41
|
+
* It is the `PlayActor` contract, which is `state` and `send`, and it names no engine, no
|
|
42
|
+
* routing and no view. A component inside the render tree reads the context to SEND an
|
|
43
|
+
* event, for example a retry from a fallback, and nothing reads `currentView` through it:
|
|
44
|
+
* a renderer receives the view from its provider, and not from this context.
|
|
45
|
+
*
|
|
46
|
+
* It was `AbstractActor<AnyActorLogic>`, which demanded the whole `Actor` surface of
|
|
47
|
+
* XState from a context that reads two members.
|
|
41
48
|
*/
|
|
42
|
-
export type AnyPlayActor =
|
|
49
|
+
export type AnyPlayActor = PlayActor;
|
|
43
50
|
export declare const ActorKey: InjectionKey<AnyPlayActor>;
|
|
44
51
|
/**
|
|
45
52
|
* Provides the actor to every descendant component, through the inject and provide
|
package/dist/useActor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useActor.d.ts","sourceRoot":"","sources":["../src/useActor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAExC,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"useActor.d.ts","sourceRoot":"","sources":["../src/useActor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAExC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC;AAErC,eAAO,MAAM,QAAQ,EAAE,YAAY,CAAC,YAAY,CAA6B,CAAC;AAE9E;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAEtD;AAED,wBAAgB,QAAQ,IAAI,YAAY,CAEvC"}
|
package/dist/useActor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useActor.js","names":[],"sources":["../src/useActor.ts"],"sourcesContent":["/**\n * useActor — the Vue composable that gives the actor inside a PlayRenderer tree.\n *\n * A component inside PlayRenderer calls useActor() to reach the actor instance. The\n * actor is then not necessary as a prop.\n *\n * The composable returns a **proxy that follows a swap**, and not the raw actor\n * object:\n * - The proxy is a stable reference for the life of the injection. It forwards\n * every operation (a property read, a property write, `in`, `Object.keys`, a\n * spread, and `instanceof`) to the actor that the tree provides at that moment.\n * Therefore it follows a swap of `props.actor` on `<ActorProvider>`, and a\n * consumer does not inject the actor again.\n * - The identity of each method is stable: `actor.send === actor.send` across\n * repeated reads, with no allocation. The proxy binds the method again only when\n * the method below it changes, or when the tree provides a different actor.\n * - The proxy is NOT `===` the `actor` prop of `<ActorProvider>`, because it is a\n * separate Proxy object. Therefore give the actor prop itself, and not the value\n * of `useActor()`, to each consumer that uses the identity of the actor as a key.\n * The most important such consumer is a router bridge that extends\n * `RouterBridgeBase`: its guard of one bridge for each actor uses the actor\n * instance as the key of a WeakMap.\n *\n * @throws {Error} When the caller is outside a PlayRenderer tree\n *\n * @example\n * ```typescript\n * import { useActor } from \"@xmachines/play-vue\";\n *\n * const actor = useActor();\n * actor.send({ type: \"SUBMIT\" });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { inject, provide } from \"vue\";\nimport type { InjectionKey } from \"vue\";\nimport { assertNonNullable } from \"@xmachines/play\";\nimport type {
|
|
1
|
+
{"version":3,"file":"useActor.js","names":[],"sources":["../src/useActor.ts"],"sourcesContent":["/**\n * useActor — the Vue composable that gives the actor inside a PlayRenderer tree.\n *\n * A component inside PlayRenderer calls useActor() to reach the actor instance. The\n * actor is then not necessary as a prop.\n *\n * The composable returns a **proxy that follows a swap**, and not the raw actor\n * object:\n * - The proxy is a stable reference for the life of the injection. It forwards\n * every operation (a property read, a property write, `in`, `Object.keys`, a\n * spread, and `instanceof`) to the actor that the tree provides at that moment.\n * Therefore it follows a swap of `props.actor` on `<ActorProvider>`, and a\n * consumer does not inject the actor again.\n * - The identity of each method is stable: `actor.send === actor.send` across\n * repeated reads, with no allocation. The proxy binds the method again only when\n * the method below it changes, or when the tree provides a different actor.\n * - The proxy is NOT `===` the `actor` prop of `<ActorProvider>`, because it is a\n * separate Proxy object. Therefore give the actor prop itself, and not the value\n * of `useActor()`, to each consumer that uses the identity of the actor as a key.\n * The most important such consumer is a router bridge that extends\n * `RouterBridgeBase`: its guard of one bridge for each actor uses the actor\n * instance as the key of a WeakMap.\n *\n * @throws {Error} When the caller is outside a PlayRenderer tree\n *\n * @example\n * ```typescript\n * import { useActor } from \"@xmachines/play-vue\";\n *\n * const actor = useActor();\n * actor.send({ type: \"SUBMIT\" });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { inject, provide } from \"vue\";\nimport type { InjectionKey } from \"vue\";\nimport { assertNonNullable } from \"@xmachines/play\";\nimport type { PlayActor } from \"@xmachines/play-actor\";\n\n/**\n * The actor type of the context of this renderer.\n *\n * It is the `PlayActor` contract, which is `state` and `send`, and it names no engine, no\n * routing and no view. A component inside the render tree reads the context to SEND an\n * event, for example a retry from a fallback, and nothing reads `currentView` through it:\n * a renderer receives the view from its provider, and not from this context.\n *\n * It was `AbstractActor<AnyActorLogic>`, which demanded the whole `Actor` surface of\n * XState from a context that reads two members.\n */\nexport type AnyPlayActor = PlayActor;\n\nexport const ActorKey: InjectionKey<AnyPlayActor> = Symbol(\"xmachines.actor\");\n\n/**\n * Provides the actor to every descendant component, through the inject and provide\n * mechanism of Vue.\n *\n * The `setup()` function of `PlayRenderer.vue` calls it. The actor is then\n * available to each child component that calls `useActor()`. You need this function\n * outside the framework internals only when you build your own renderer wrapper.\n *\n * @param actor - The actor instance to inject into the component tree.\n */\nexport function provideActor(actor: AnyPlayActor): void {\n\tprovide(ActorKey, actor);\n}\n\nexport function useActor(): AnyPlayActor {\n\treturn assertNonNullable(inject(ActorKey), \"ActorKey\");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,IAAa,WAAuC,OAAO,iBAAiB;;;;;;;;;;;AAY5E,SAAgB,aAAa,OAA2B;CACvD,QAAQ,UAAU,KAAK;AACxB;AAEA,SAAgB,WAAyB;CACxC,OAAO,kBAAkB,OAAO,QAAQ,GAAG,UAAU;AACtD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmachines/play-vue",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Vue renderer for XMachines Play architecture",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"reactive",
|
|
@@ -39,45 +39,48 @@
|
|
|
39
39
|
"build": "vite build && tsc --build",
|
|
40
40
|
"clean": "rm -rf dist *.tsbuildinfo coverage .vitest-attachments test/browser/__screenshots__ node_modules/.svelte2tsx-* node_modules/.vite*",
|
|
41
41
|
"lint": "oxlint .",
|
|
42
|
+
"lint:security": "node ../../scripts/semgrep-scan.mjs",
|
|
42
43
|
"format": "oxfmt .",
|
|
43
44
|
"test": "vitest",
|
|
44
45
|
"test:coverage": "vitest run --coverage",
|
|
45
46
|
"test:watch": "vitest"
|
|
46
47
|
},
|
|
47
48
|
"dependencies": {
|
|
48
|
-
"@xmachines/play": "
|
|
49
|
-
"@xmachines/play-
|
|
50
|
-
"@xmachines/play-signals": "3.0.0"
|
|
49
|
+
"@xmachines/play-actor": "4.0.0",
|
|
50
|
+
"@xmachines/play-view": "4.0.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@testing-library/jest-dom": "^
|
|
54
|
-
"@types/node": "^26.2
|
|
55
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
56
|
-
"@vitest/browser-playwright": "^
|
|
57
|
-
"@vue/test-utils": "^2.
|
|
53
|
+
"@testing-library/jest-dom": "^7.0.1",
|
|
54
|
+
"@types/node": "^26.6.2",
|
|
55
|
+
"@vitejs/plugin-vue": "^6.0.9",
|
|
56
|
+
"@vitest/browser-playwright": "^5.0.1",
|
|
57
|
+
"@vue/test-utils": "^2.5.0",
|
|
58
58
|
"@xmachines/json-render-core": "^0.20.0-xm.4",
|
|
59
59
|
"@xmachines/json-render-vue": "^0.20.0-xm.4",
|
|
60
60
|
"@xmachines/json-render-xstate": "^0.20.0-xm.4",
|
|
61
|
-
"@
|
|
62
|
-
"
|
|
63
|
-
"
|
|
61
|
+
"@xmachines/play": "4.0.0",
|
|
62
|
+
"@xmachines/play-signals": "4.0.0",
|
|
63
|
+
"@xstate/store": "^3.17.5",
|
|
64
|
+
"oxfmt": "^0.68.0",
|
|
65
|
+
"oxlint": "^1.83.0",
|
|
64
66
|
"typescript": "^5.9.3 || ^6.0.3",
|
|
65
|
-
"vite": "^8.0
|
|
66
|
-
"vite-plugin-dts": "^
|
|
67
|
-
"vitest": "^
|
|
68
|
-
"vue": "^3.5.
|
|
69
|
-
"xstate": "^5.
|
|
70
|
-
"zod": "^4.
|
|
67
|
+
"vite": "^8.3.0",
|
|
68
|
+
"vite-plugin-dts": "^5.1.0",
|
|
69
|
+
"vitest": "^5.0.1",
|
|
70
|
+
"vue": "^3.5.42",
|
|
71
|
+
"xstate": "^5.33.0",
|
|
72
|
+
"zod": "^4.6.5"
|
|
71
73
|
},
|
|
72
74
|
"peerDependencies": {
|
|
73
75
|
"@xmachines/json-render-core": "^0.20.0-xm.4",
|
|
74
76
|
"@xmachines/json-render-vue": "^0.20.0-xm.4",
|
|
75
77
|
"@xmachines/json-render-xstate": "^0.20.0-xm.4",
|
|
78
|
+
"@xmachines/play": "4.0.0",
|
|
79
|
+
"@xmachines/play-signals": "4.0.0",
|
|
76
80
|
"@xstate/store": "^3.17.0",
|
|
77
|
-
"vue": "^3.5.0"
|
|
78
|
-
"xstate": "^5.31.0"
|
|
81
|
+
"vue": "^3.5.0"
|
|
79
82
|
},
|
|
80
83
|
"engines": {
|
|
81
|
-
"node": ">=
|
|
84
|
+
"node": ">=24.0.0"
|
|
82
85
|
}
|
|
83
86
|
}
|