@xmachines/play-vue 1.0.0-beta.2 → 1.0.0-beta.3
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/dist/PlayRenderer.js +9 -0
- package/dist/PlayRenderer.js.map +1 -0
- package/dist/PlayRenderer.vue_vue_type_script_lang.js +35 -0
- package/dist/PlayRenderer.vue_vue_type_script_lang.js.map +1 -0
- package/dist/_virtual/_plugin-vue_export-helper.js +7 -9
- package/dist/index.css +2 -1
- package/dist/index.js +2 -5
- package/package.json +18 -18
- package/dist/PlayRenderer.vue.js +0 -8
- package/dist/PlayRenderer.vue.js.map +0 -1
- package/dist/PlayRenderer.vue2.js +0 -48
- package/dist/PlayRenderer.vue2.js.map +0 -1
- package/dist/_virtual/_plugin-vue_export-helper.js.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import e from "./PlayRenderer.vue_vue_type_script_lang.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
import t from "./_virtual/_plugin-vue_export-helper.js";
|
|
4
|
+
//#region src/PlayRenderer.vue
|
|
5
|
+
var n = /* @__PURE__ */ t(e, [["__scopeId", "data-v-275eda6e"]]);
|
|
6
|
+
//#endregion
|
|
7
|
+
export { n as default };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=PlayRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlayRenderer.js","names":[],"sources":["../src/PlayRenderer.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * PlayRenderer - Main Vue renderer component for XMachines Play architecture\n *\n * Architecture (per RESEARCH.md Pattern 1):\n * - Subscribes to actor.currentView signal via Signal.subtle.Watcher\n * - Dynamically renders catalog components based on view.component string\n * - Forwards user events to actor via actor.send()\n * - Vue ref only for triggering renders, NOT business logic\n *\n * Signal bridge uses one-shot re-watch pattern:\n * TC39 Signal watchers stop watching after notification, so watcher.watch()\n * must be called inside a microtask after getPending() to re-arm for the\n * next notification.\n *\n * CRITICAL: Never call signal.get() or signal.set() inside the Watcher's\n * notify callback. The callback runs synchronously during the signal graph's\n * dirty-propagation phase. All reads must be deferred to a queueMicrotask.\n *\n * @invariant Actor Authority - Actor decides all state transitions via guards\n * @invariant Passive Infrastructure - Component observes signals, sends events\n * @invariant Signal-Only Reactivity - Business logic state lives in actor signals\n */\n\nimport { defineComponent, ref, computed, toRaw, onUnmounted, h, type PropType } from \"vue\";\nimport { Signal } from \"@xmachines/play-signals\";\nimport type { PlayRendererProps } from \"./types.js\";\nimport type { AbstractActor, Viewable } from \"@xmachines/play-actor\";\nimport type { AnyActorLogic } from \"xstate\";\nimport type { Component } from \"vue\";\n\nexport default defineComponent({\n\tname: \"PlayRenderer\",\n\tprops: {\n\t\tactor: {\n\t\t\ttype: Object as PropType<AbstractActor<AnyActorLogic> & Viewable>,\n\t\t\trequired: true,\n\t\t},\n\t\tcomponents: {\n\t\t\ttype: Object as PropType<Record<string, Component>>,\n\t\t\trequired: true,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\t// Unwrap actor from Vue's reactive proxy to access raw Signal objects\n\t\t// CRITICAL: toRaw() preserves Signal's 'this' binding for .get() and watcher operations\n\t\tconst actor = toRaw(props.actor);\n\n\t\t// Get initial value from unwrapped signal\n\t\tconst initialView = actor.currentView.get();\n\n\t\t// Vue ref for triggering re-renders (NOT business logic state)\n\t\t// Signal is source of truth, ref is just Vue's render trigger\n\t\tconst view = ref<{ component: string; props: Record<string, unknown> } | null>(initialView);\n\n\t\t// Signal watcher for bridging TC39 Signals to Vue reactivity\n\t\t// Created immediately (not in onMounted) so signal changes during the\n\t\t// synchronous mount phase are captured.\n\t\t//\n\t\t// The notify callback runs synchronously during the signal graph's\n\t\t// dirty-propagation phase. NEVER read (.get()) or write (.set()) signals\n\t\t// inside it. Only schedule a microtask to do the actual work.\n\t\tconst watcher = new Signal.subtle.Watcher(() => {\n\t\t\tqueueMicrotask(() => {\n\t\t\t\t// Step 1: Acknowledge notification (clears watcher's dirty flags)\n\t\t\t\twatcher.getPending();\n\n\t\t\t\t// Step 2: Read signal value (safe — notification phase is over)\n\t\t\t\tview.value = actor.currentView.get();\n\n\t\t\t\t// Step 3: Re-watch for next notification (one-shot pattern)\n\t\t\t\t// TC39 watchers stop notifying after first notification until re-armed\n\t\t\t\twatcher.watch(actor.currentView);\n\t\t\t});\n\t\t});\n\n\t\t// Start watching the signal\n\t\twatcher.watch(actor.currentView);\n\n\t\tonUnmounted(() => {\n\t\t\t// Unwatch to stop receiving notifications\n\t\t\twatcher.unwatch(actor.currentView);\n\t\t});\n\n\t\t// Bind send function to actor for correct 'this' context\n\t\tconst sendBound = actor.send.bind(actor);\n\n\t\t// Compute Component from view.component lookup\n\t\tconst ResolvedComponent = computed(() => {\n\t\t\tif (!view.value) return null;\n\n\t\t\t// Handle null/undefined components catalog gracefully\n\t\t\tif (!props.components) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Components catalog is ${props.components === null ? \"null\" : \"undefined\"}. ` +\n\t\t\t\t\t\t`Cannot render component \"${view.value.component}\".`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Look up component from catalog\n\t\t\tconst comp = toRaw(props.components[view.value.component]);\n\n\t\t\tif (!comp) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Component \"${view.value.component}\" not found in catalog. ` +\n\t\t\t\t\t\t`Available components: ${Object.keys(props.components).join(\", \")}`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn comp;\n\t\t});\n\n\t\t// Use render function to avoid Vue 3.5 SFC template <slot> + jsdom renderSlot crash\n\t\treturn () => {\n\t\t\t// No view — show fallback slot\n\t\t\tif (!view.value) {\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\n\t\t\t// View exists but component not found\n\t\t\tif (!ResolvedComponent.value) {\n\t\t\t\treturn h(\n\t\t\t\t\t\"div\",\n\t\t\t\t\t{ class: \"play-renderer-error\" },\n\t\t\t\t\t`Component \"${view.value.component}\" not found in catalog`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Render matched component dynamically\n\t\t\treturn h(ResolvedComponent.value, {\n\t\t\t\t...view.value.props,\n\t\t\t\tsend: sendBound,\n\t\t\t});\n\t\t};\n\t},\n});\n</script>\n\n<style scoped>\n.play-renderer-error {\n\tpadding: 1rem;\n\tbackground-color: #fee;\n\tborder: 1px solid #fcc;\n\tborder-radius: 4px;\n\tcolor: #c00;\n\tfont-family: monospace;\n}\n</style>\n"],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { computed as e, defineComponent as t, h as n, onUnmounted as r, ref as i, toRaw as a } from "vue";
|
|
2
|
+
import { Signal as o } from "@xmachines/play-signals";
|
|
3
|
+
//#region src/PlayRenderer.vue?vue&type=script&lang.ts
|
|
4
|
+
var s = t({
|
|
5
|
+
name: "PlayRenderer",
|
|
6
|
+
props: {
|
|
7
|
+
actor: {
|
|
8
|
+
type: Object,
|
|
9
|
+
required: !0
|
|
10
|
+
},
|
|
11
|
+
components: {
|
|
12
|
+
type: Object,
|
|
13
|
+
required: !0
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
setup(t, { slots: s }) {
|
|
17
|
+
let c = a(t.actor), l = i(c.currentView.get()), u = new o.subtle.Watcher(() => {
|
|
18
|
+
queueMicrotask(() => {
|
|
19
|
+
u.getPending(), l.value = c.currentView.get(), u.watch(c.currentView);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
u.watch(c.currentView), r(() => {
|
|
23
|
+
u.unwatch(c.currentView);
|
|
24
|
+
});
|
|
25
|
+
let d = c.send.bind(c), f = e(() => l.value ? t.components ? a(t.components[l.value.component]) || (console.error(`Component "${l.value.component}" not found in catalog. Available components: ${Object.keys(t.components).join(", ")}`), null) : (console.error(`Components catalog is ${t.components === null ? "null" : "undefined"}. Cannot render component "${l.value.component}".`), null) : null);
|
|
26
|
+
return () => l.value ? f.value ? n(f.value, {
|
|
27
|
+
...l.value.props,
|
|
28
|
+
send: d
|
|
29
|
+
}) : n("div", { class: "play-renderer-error" }, `Component "${l.value.component}" not found in catalog`) : s.fallback ? s.fallback() : null;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
//#endregion
|
|
33
|
+
export { s as default };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=PlayRenderer.vue_vue_type_script_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlayRenderer.vue_vue_type_script_lang.js","names":[],"sources":["../src/PlayRenderer.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * PlayRenderer - Main Vue renderer component for XMachines Play architecture\n *\n * Architecture (per RESEARCH.md Pattern 1):\n * - Subscribes to actor.currentView signal via Signal.subtle.Watcher\n * - Dynamically renders catalog components based on view.component string\n * - Forwards user events to actor via actor.send()\n * - Vue ref only for triggering renders, NOT business logic\n *\n * Signal bridge uses one-shot re-watch pattern:\n * TC39 Signal watchers stop watching after notification, so watcher.watch()\n * must be called inside a microtask after getPending() to re-arm for the\n * next notification.\n *\n * CRITICAL: Never call signal.get() or signal.set() inside the Watcher's\n * notify callback. The callback runs synchronously during the signal graph's\n * dirty-propagation phase. All reads must be deferred to a queueMicrotask.\n *\n * @invariant Actor Authority - Actor decides all state transitions via guards\n * @invariant Passive Infrastructure - Component observes signals, sends events\n * @invariant Signal-Only Reactivity - Business logic state lives in actor signals\n */\n\nimport { defineComponent, ref, computed, toRaw, onUnmounted, h, type PropType } from \"vue\";\nimport { Signal } from \"@xmachines/play-signals\";\nimport type { PlayRendererProps } from \"./types.js\";\nimport type { AbstractActor, Viewable } from \"@xmachines/play-actor\";\nimport type { AnyActorLogic } from \"xstate\";\nimport type { Component } from \"vue\";\n\nexport default defineComponent({\n\tname: \"PlayRenderer\",\n\tprops: {\n\t\tactor: {\n\t\t\ttype: Object as PropType<AbstractActor<AnyActorLogic> & Viewable>,\n\t\t\trequired: true,\n\t\t},\n\t\tcomponents: {\n\t\t\ttype: Object as PropType<Record<string, Component>>,\n\t\t\trequired: true,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\t// Unwrap actor from Vue's reactive proxy to access raw Signal objects\n\t\t// CRITICAL: toRaw() preserves Signal's 'this' binding for .get() and watcher operations\n\t\tconst actor = toRaw(props.actor);\n\n\t\t// Get initial value from unwrapped signal\n\t\tconst initialView = actor.currentView.get();\n\n\t\t// Vue ref for triggering re-renders (NOT business logic state)\n\t\t// Signal is source of truth, ref is just Vue's render trigger\n\t\tconst view = ref<{ component: string; props: Record<string, unknown> } | null>(initialView);\n\n\t\t// Signal watcher for bridging TC39 Signals to Vue reactivity\n\t\t// Created immediately (not in onMounted) so signal changes during the\n\t\t// synchronous mount phase are captured.\n\t\t//\n\t\t// The notify callback runs synchronously during the signal graph's\n\t\t// dirty-propagation phase. NEVER read (.get()) or write (.set()) signals\n\t\t// inside it. Only schedule a microtask to do the actual work.\n\t\tconst watcher = new Signal.subtle.Watcher(() => {\n\t\t\tqueueMicrotask(() => {\n\t\t\t\t// Step 1: Acknowledge notification (clears watcher's dirty flags)\n\t\t\t\twatcher.getPending();\n\n\t\t\t\t// Step 2: Read signal value (safe — notification phase is over)\n\t\t\t\tview.value = actor.currentView.get();\n\n\t\t\t\t// Step 3: Re-watch for next notification (one-shot pattern)\n\t\t\t\t// TC39 watchers stop notifying after first notification until re-armed\n\t\t\t\twatcher.watch(actor.currentView);\n\t\t\t});\n\t\t});\n\n\t\t// Start watching the signal\n\t\twatcher.watch(actor.currentView);\n\n\t\tonUnmounted(() => {\n\t\t\t// Unwatch to stop receiving notifications\n\t\t\twatcher.unwatch(actor.currentView);\n\t\t});\n\n\t\t// Bind send function to actor for correct 'this' context\n\t\tconst sendBound = actor.send.bind(actor);\n\n\t\t// Compute Component from view.component lookup\n\t\tconst ResolvedComponent = computed(() => {\n\t\t\tif (!view.value) return null;\n\n\t\t\t// Handle null/undefined components catalog gracefully\n\t\t\tif (!props.components) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Components catalog is ${props.components === null ? \"null\" : \"undefined\"}. ` +\n\t\t\t\t\t\t`Cannot render component \"${view.value.component}\".`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Look up component from catalog\n\t\t\tconst comp = toRaw(props.components[view.value.component]);\n\n\t\t\tif (!comp) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Component \"${view.value.component}\" not found in catalog. ` +\n\t\t\t\t\t\t`Available components: ${Object.keys(props.components).join(\", \")}`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn comp;\n\t\t});\n\n\t\t// Use render function to avoid Vue 3.5 SFC template <slot> + jsdom renderSlot crash\n\t\treturn () => {\n\t\t\t// No view — show fallback slot\n\t\t\tif (!view.value) {\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\n\t\t\t// View exists but component not found\n\t\t\tif (!ResolvedComponent.value) {\n\t\t\t\treturn h(\n\t\t\t\t\t\"div\",\n\t\t\t\t\t{ class: \"play-renderer-error\" },\n\t\t\t\t\t`Component \"${view.value.component}\" not found in catalog`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Render matched component dynamically\n\t\t\treturn h(ResolvedComponent.value, {\n\t\t\t\t...view.value.props,\n\t\t\t\tsend: sendBound,\n\t\t\t});\n\t\t};\n\t},\n});\n</script>\n\n<style scoped>\n.play-renderer-error {\n\tpadding: 1rem;\n\tbackground-color: #fee;\n\tborder: 1px solid #fcc;\n\tborder-radius: 4px;\n\tcolor: #c00;\n\tfont-family: monospace;\n}\n</style>\n"],"mappings":";;;AA+BA,IAAA,IAAe,EAAgB;CAC9B,MAAM;CACN,OAAO;EACN,OAAO;GACN,MAAM;GACN,UAAU;GACV;EACD,YAAY;GACX,MAAM;GACN,UAAU;GACV;EACD;CACD,MAAM,GAAO,EAAE,YAAS;EAGvB,IAAM,IAAQ,EAAM,EAAM,MAAM,EAO1B,IAAO,EAJO,EAAM,YAAY,KAAK,CAIgD,EASrF,IAAU,IAAI,EAAO,OAAO,cAAc;AAC/C,wBAAqB;AASpB,IAPA,EAAQ,YAAY,EAGpB,EAAK,QAAQ,EAAM,YAAY,KAAK,EAIpC,EAAQ,MAAM,EAAM,YAAY;KAC/B;IACD;AAKF,EAFA,EAAQ,MAAM,EAAM,YAAY,EAEhC,QAAkB;AAEjB,KAAQ,QAAQ,EAAM,YAAY;IACjC;EAGF,IAAM,IAAY,EAAM,KAAK,KAAK,EAAM,EAGlC,IAAoB,QACpB,EAAK,QAGL,EAAM,aASE,EAAM,EAAM,WAAW,EAAK,MAAM,WAAW,KAGzD,QAAQ,MACP,cAAc,EAAK,MAAM,UAAU,gDACT,OAAO,KAAK,EAAM,WAAW,CAAC,KAAK,KAAK,GAClE,EACM,SAfP,QAAQ,MACP,yBAAyB,EAAM,eAAe,OAAO,SAAS,YAAY,6BAC7C,EAAK,MAAM,UAAU,IAClD,EACM,QARgB,KAuBvB;AAGF,eAEM,EAAK,QAKL,EAAkB,QAShB,EAAE,EAAkB,OAAO;GACjC,GAAG,EAAK,MAAM;GACd,MAAM;GACN,CAAC,GAXM,EACN,OACA,EAAE,OAAO,uBAAuB,EAChC,cAAc,EAAK,MAAM,UAAU,wBACnC,GATM,EAAM,WAAW,EAAM,UAAS,GAAI;;CAmB9C,CAAC"}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
//#region \0plugin-vue:export-helper
|
|
2
|
+
var e = (e, t) => {
|
|
3
|
+
let n = e.__vccOpts || e;
|
|
4
|
+
for (let [e, r] of t) n[e] = r;
|
|
5
|
+
return n;
|
|
6
6
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
-
//# sourceMappingURL=_plugin-vue_export-helper.js.map
|
|
7
|
+
//#endregion
|
|
8
|
+
export { e as default };
|
package/dist/index.css
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
.play-renderer-error[data-v-275eda6e]{
|
|
1
|
+
.play-renderer-error[data-v-275eda6e]{color:#c00;background-color:#fee;border:1px solid #fcc;border-radius:4px;padding:1rem;font-family:monospace}
|
|
2
|
+
/*$vite$:1*/
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmachines/play-vue",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.3",
|
|
4
4
|
"description": "Vue renderer for XMachines Play architecture",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"reactive",
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"type": "git",
|
|
15
15
|
"url": "git@gitlab.com:xmachin-es/xmachines-js.git"
|
|
16
16
|
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
17
22
|
"type": "module",
|
|
18
23
|
"main": "./dist/index.js",
|
|
19
24
|
"types": "./dist/index.d.ts",
|
|
@@ -23,6 +28,9 @@
|
|
|
23
28
|
"default": "./dist/index.js"
|
|
24
29
|
}
|
|
25
30
|
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
26
34
|
"scripts": {
|
|
27
35
|
"build": "vite build && tsc --build --force",
|
|
28
36
|
"clean": "rm -rf dist",
|
|
@@ -32,28 +40,20 @@
|
|
|
32
40
|
"prepublishOnly": "npm run build"
|
|
33
41
|
},
|
|
34
42
|
"dependencies": {
|
|
35
|
-
"@xmachines/play-actor": "1.0.0-beta.
|
|
36
|
-
"@xmachines/play-catalog": "1.0.0-beta.
|
|
37
|
-
"@xmachines/play-signals": "1.0.0-beta.
|
|
43
|
+
"@xmachines/play-actor": "1.0.0-beta.3",
|
|
44
|
+
"@xmachines/play-catalog": "1.0.0-beta.3",
|
|
45
|
+
"@xmachines/play-signals": "1.0.0-beta.3"
|
|
38
46
|
},
|
|
39
47
|
"devDependencies": {
|
|
40
|
-
"@types/node": "^25.
|
|
41
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
48
|
+
"@types/node": "^25.5.0",
|
|
49
|
+
"@vitejs/plugin-vue": "^6.0.5",
|
|
42
50
|
"@vue/test-utils": "^2.4.6",
|
|
43
|
-
"jsdom": "^
|
|
44
|
-
"typescript": "^5.
|
|
45
|
-
"vitest": "^4.0
|
|
51
|
+
"jsdom": "^29.0.0",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"vitest": "^4.1.0",
|
|
46
54
|
"vue": "^3.5.30"
|
|
47
55
|
},
|
|
48
56
|
"peerDependencies": {
|
|
49
57
|
"vue": "^3.5.30"
|
|
50
|
-
}
|
|
51
|
-
"publishConfig": {
|
|
52
|
-
"access": "public"
|
|
53
|
-
},
|
|
54
|
-
"files": [
|
|
55
|
-
"dist",
|
|
56
|
-
"README.md",
|
|
57
|
-
"LICENSE"
|
|
58
|
-
]
|
|
58
|
+
}
|
|
59
59
|
}
|
package/dist/PlayRenderer.vue.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PlayRenderer.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { defineComponent as m, toRaw as a, ref as d, onUnmounted as p, computed as f, h as l } from "vue";
|
|
2
|
-
import { Signal as v } from "@xmachines/play-signals";
|
|
3
|
-
const g = m({
|
|
4
|
-
name: "PlayRenderer",
|
|
5
|
-
props: {
|
|
6
|
-
actor: {
|
|
7
|
-
type: Object,
|
|
8
|
-
required: !0
|
|
9
|
-
},
|
|
10
|
-
components: {
|
|
11
|
-
type: Object,
|
|
12
|
-
required: !0
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
setup(t, { slots: r }) {
|
|
16
|
-
const n = a(t.actor), i = n.currentView.get(), e = d(i), o = new v.subtle.Watcher(() => {
|
|
17
|
-
queueMicrotask(() => {
|
|
18
|
-
o.getPending(), e.value = n.currentView.get(), o.watch(n.currentView);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
o.watch(n.currentView), p(() => {
|
|
22
|
-
o.unwatch(n.currentView);
|
|
23
|
-
});
|
|
24
|
-
const s = n.send.bind(n), c = f(() => {
|
|
25
|
-
if (!e.value) return null;
|
|
26
|
-
if (!t.components)
|
|
27
|
-
return console.error(
|
|
28
|
-
`Components catalog is ${t.components === null ? "null" : "undefined"}. Cannot render component "${e.value.component}".`
|
|
29
|
-
), null;
|
|
30
|
-
const u = a(t.components[e.value.component]);
|
|
31
|
-
return u || (console.error(
|
|
32
|
-
`Component "${e.value.component}" not found in catalog. Available components: ${Object.keys(t.components).join(", ")}`
|
|
33
|
-
), null);
|
|
34
|
-
});
|
|
35
|
-
return () => e.value ? c.value ? l(c.value, {
|
|
36
|
-
...e.value.props,
|
|
37
|
-
send: s
|
|
38
|
-
}) : l(
|
|
39
|
-
"div",
|
|
40
|
-
{ class: "play-renderer-error" },
|
|
41
|
-
`Component "${e.value.component}" not found in catalog`
|
|
42
|
-
) : r.fallback ? r.fallback() : null;
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
export {
|
|
46
|
-
g as default
|
|
47
|
-
};
|
|
48
|
-
//# sourceMappingURL=PlayRenderer.vue2.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PlayRenderer.vue2.js","sources":["../src/PlayRenderer.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * PlayRenderer - Main Vue renderer component for XMachines Play architecture\n *\n * Architecture (per RESEARCH.md Pattern 1):\n * - Subscribes to actor.currentView signal via Signal.subtle.Watcher\n * - Dynamically renders catalog components based on view.component string\n * - Forwards user events to actor via actor.send()\n * - Vue ref only for triggering renders, NOT business logic\n *\n * Signal bridge uses one-shot re-watch pattern:\n * TC39 Signal watchers stop watching after notification, so watcher.watch()\n * must be called inside a microtask after getPending() to re-arm for the\n * next notification.\n *\n * CRITICAL: Never call signal.get() or signal.set() inside the Watcher's\n * notify callback. The callback runs synchronously during the signal graph's\n * dirty-propagation phase. All reads must be deferred to a queueMicrotask.\n *\n * @invariant Actor Authority - Actor decides all state transitions via guards\n * @invariant Passive Infrastructure - Component observes signals, sends events\n * @invariant Signal-Only Reactivity - Business logic state lives in actor signals\n */\n\nimport { defineComponent, ref, computed, toRaw, onUnmounted, h, type PropType } from \"vue\";\nimport { Signal } from \"@xmachines/play-signals\";\nimport type { PlayRendererProps } from \"./types.js\";\nimport type { AbstractActor, Viewable } from \"@xmachines/play-actor\";\nimport type { AnyActorLogic } from \"xstate\";\nimport type { Component } from \"vue\";\n\nexport default defineComponent({\n\tname: \"PlayRenderer\",\n\tprops: {\n\t\tactor: {\n\t\t\ttype: Object as PropType<AbstractActor<AnyActorLogic> & Viewable>,\n\t\t\trequired: true,\n\t\t},\n\t\tcomponents: {\n\t\t\ttype: Object as PropType<Record<string, Component>>,\n\t\t\trequired: true,\n\t\t},\n\t},\n\tsetup(props, { slots }) {\n\t\t// Unwrap actor from Vue's reactive proxy to access raw Signal objects\n\t\t// CRITICAL: toRaw() preserves Signal's 'this' binding for .get() and watcher operations\n\t\tconst actor = toRaw(props.actor);\n\n\t\t// Get initial value from unwrapped signal\n\t\tconst initialView = actor.currentView.get();\n\n\t\t// Vue ref for triggering re-renders (NOT business logic state)\n\t\t// Signal is source of truth, ref is just Vue's render trigger\n\t\tconst view = ref<{ component: string; props: Record<string, unknown> } | null>(initialView);\n\n\t\t// Signal watcher for bridging TC39 Signals to Vue reactivity\n\t\t// Created immediately (not in onMounted) so signal changes during the\n\t\t// synchronous mount phase are captured.\n\t\t//\n\t\t// The notify callback runs synchronously during the signal graph's\n\t\t// dirty-propagation phase. NEVER read (.get()) or write (.set()) signals\n\t\t// inside it. Only schedule a microtask to do the actual work.\n\t\tconst watcher = new Signal.subtle.Watcher(() => {\n\t\t\tqueueMicrotask(() => {\n\t\t\t\t// Step 1: Acknowledge notification (clears watcher's dirty flags)\n\t\t\t\twatcher.getPending();\n\n\t\t\t\t// Step 2: Read signal value (safe — notification phase is over)\n\t\t\t\tview.value = actor.currentView.get();\n\n\t\t\t\t// Step 3: Re-watch for next notification (one-shot pattern)\n\t\t\t\t// TC39 watchers stop notifying after first notification until re-armed\n\t\t\t\twatcher.watch(actor.currentView);\n\t\t\t});\n\t\t});\n\n\t\t// Start watching the signal\n\t\twatcher.watch(actor.currentView);\n\n\t\tonUnmounted(() => {\n\t\t\t// Unwatch to stop receiving notifications\n\t\t\twatcher.unwatch(actor.currentView);\n\t\t});\n\n\t\t// Bind send function to actor for correct 'this' context\n\t\tconst sendBound = actor.send.bind(actor);\n\n\t\t// Compute Component from view.component lookup\n\t\tconst ResolvedComponent = computed(() => {\n\t\t\tif (!view.value) return null;\n\n\t\t\t// Handle null/undefined components catalog gracefully\n\t\t\tif (!props.components) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Components catalog is ${props.components === null ? \"null\" : \"undefined\"}. ` +\n\t\t\t\t\t\t`Cannot render component \"${view.value.component}\".`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Look up component from catalog\n\t\t\tconst comp = toRaw(props.components[view.value.component]);\n\n\t\t\tif (!comp) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Component \"${view.value.component}\" not found in catalog. ` +\n\t\t\t\t\t\t`Available components: ${Object.keys(props.components).join(\", \")}`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn comp;\n\t\t});\n\n\t\t// Use render function to avoid Vue 3.5 SFC template <slot> + jsdom renderSlot crash\n\t\treturn () => {\n\t\t\t// No view — show fallback slot\n\t\t\tif (!view.value) {\n\t\t\t\treturn slots.fallback ? slots.fallback() : null;\n\t\t\t}\n\n\t\t\t// View exists but component not found\n\t\t\tif (!ResolvedComponent.value) {\n\t\t\t\treturn h(\n\t\t\t\t\t\"div\",\n\t\t\t\t\t{ class: \"play-renderer-error\" },\n\t\t\t\t\t`Component \"${view.value.component}\" not found in catalog`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Render matched component dynamically\n\t\t\treturn h(ResolvedComponent.value, {\n\t\t\t\t...view.value.props,\n\t\t\t\tsend: sendBound,\n\t\t\t});\n\t\t};\n\t},\n});\n</script>\n\n<style scoped>\n.play-renderer-error {\n\tpadding: 1rem;\n\tbackground-color: #fee;\n\tborder: 1px solid #fcc;\n\tborder-radius: 4px;\n\tcolor: #c00;\n\tfont-family: monospace;\n}\n</style>\n"],"names":["_sfc_main","defineComponent","props","slots","actor","toRaw","initialView","view","ref","watcher","Signal","onUnmounted","sendBound","ResolvedComponent","computed","comp","h"],"mappings":";;AA+BA,MAAAA,IAAeC,EAAgB;AAAA,EAC9B,MAAM;AAAA,EACN,OAAO;AAAA,IACN,OAAO;AAAA,MACN,MAAM;AAAA,MACN,UAAU;AAAA,IAAA;AAAA,IAEX,YAAY;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,IAAA;AAAA,EACX;AAAA,EAED,MAAMC,GAAO,EAAE,OAAAC,KAAS;AAGvB,UAAMC,IAAQC,EAAMH,EAAM,KAAK,GAGzBI,IAAcF,EAAM,YAAY,IAAA,GAIhCG,IAAOC,EAAkEF,CAAW,GASpFG,IAAU,IAAIC,EAAO,OAAO,QAAQ,MAAM;AAC/C,qBAAe,MAAM;AAEpB,QAAAD,EAAQ,WAAA,GAGRF,EAAK,QAAQH,EAAM,YAAY,IAAA,GAI/BK,EAAQ,MAAML,EAAM,WAAW;AAAA,MAChC,CAAC;AAAA,IACF,CAAC;AAGD,IAAAK,EAAQ,MAAML,EAAM,WAAW,GAE/BO,EAAY,MAAM;AAEjB,MAAAF,EAAQ,QAAQL,EAAM,WAAW;AAAA,IAClC,CAAC;AAGD,UAAMQ,IAAYR,EAAM,KAAK,KAAKA,CAAK,GAGjCS,IAAoBC,EAAS,MAAM;AACxC,UAAI,CAACP,EAAK,MAAO,QAAO;AAGxB,UAAI,CAACL,EAAM;AACV,uBAAQ;AAAA,UACP,yBAAyBA,EAAM,eAAe,OAAO,SAAS,WAAW,8BAC5CK,EAAK,MAAM,SAAS;AAAA,QAAA,GAE3C;AAIR,YAAMQ,IAAOV,EAAMH,EAAM,WAAWK,EAAK,MAAM,SAAS,CAAC;AAEzD,aAAKQ,MACJ,QAAQ;AAAA,QACP,cAAcR,EAAK,MAAM,SAAS,iDACR,OAAO,KAAKL,EAAM,UAAU,EAAE,KAAK,IAAI,CAAC;AAAA,MAAA,GAE5D;AAAA,IAIT,CAAC;AAGD,WAAO,MAEDK,EAAK,QAKLM,EAAkB,QAShBG,EAAEH,EAAkB,OAAO;AAAA,MACjC,GAAGN,EAAK,MAAM;AAAA,MACd,MAAMK;AAAA,IAAA,CACN,IAXOI;AAAA,MACN;AAAA,MACA,EAAE,OAAO,sBAAA;AAAA,MACT,cAAcT,EAAK,MAAM,SAAS;AAAA,IAAA,IAR5BJ,EAAM,WAAWA,EAAM,SAAA,IAAa;AAAA,EAkB9C;AACD,CAAC;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_plugin-vue_export-helper.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|