bhooai-nexus 2.0.13 → 3.0.4
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 +113 -43
- package/package.json +23 -14
- package/packages/nexus-admin/package.json +1 -1
- package/packages/nexus-admin/src/App.tsx +280 -37
- package/packages/nexus-admin/src/api.ts +56 -0
- package/packages/nexus-admin/src/index.css +10 -1
- package/packages/nexus-ads/package.json +1 -1
- package/packages/nexus-ai-client/package.json +22 -20
- package/packages/nexus-ai-client/src/client.ts +3 -0
- package/packages/nexus-auth/package.json +1 -1
- package/packages/nexus-cache/package.json +1 -1
- package/packages/nexus-cli/package.json +6 -3
- package/packages/nexus-cli/src/commands/add.ts +32 -10
- package/packages/nexus-cli/src/commands/dev.ts +206 -127
- package/packages/nexus-cli/src/commands/init.ts +45 -12
- package/packages/nexus-cli/src/commands/license.ts +174 -0
- package/packages/nexus-cli/src/devPanel.ts +1381 -990
- package/packages/nexus-cli/src/devServiceManager.ts +416 -40
- package/packages/nexus-cli/src/features.ts +58 -11
- package/packages/nexus-cli/src/fusion/server.ts +251 -0
- package/packages/nexus-cli/src/index.ts +2 -0
- package/packages/nexus-cli/src/launcher.ts +35 -3
- package/packages/nexus-cli/src/ports.ts +112 -61
- package/packages/nexus-cli/src/tui.ts +272 -15
- package/packages/nexus-cli/src/wizard.ts +210 -23
- package/packages/nexus-cli/templates/base/apps/admin/vite.config.ts.ejs +15 -9
- package/packages/nexus-cli/templates/base/apps/backend/package.json.ejs +1 -0
- package/packages/nexus-cli/templates/base/apps/backend/src/live/root.live.js.ejs +26 -0
- package/packages/nexus-cli/templates/base/apps/backend/src/main.ts +1 -1
- package/packages/nexus-cli/templates/base/apps/backend/src/routes/index.ts.ejs +2 -0
- package/packages/nexus-cli/templates/base/apps/backend/src/routes/upload.ts.ejs +31 -0
- package/packages/nexus-cli/templates/base/apps/frontend/index.html.ejs +1 -0
- package/packages/nexus-cli/templates/base/apps/frontend/src/App.tsx.ejs +7 -2
- package/packages/nexus-cli/templates/base/apps/frontend/src/NotFound.tsx.ejs +56 -0
- package/packages/nexus-cli/templates/base/apps/frontend/vite.config.ts.ejs +15 -8
- package/packages/nexus-cli/templates/base/apps/pyserver/main.py.ejs +19 -0
- package/packages/nexus-cli/templates/base/apps/pyserver/requirements.txt.ejs +20 -0
- package/packages/nexus-cli/templates/base/apps/pyserver/routers/.gitkeep +0 -0
- package/packages/nexus-cli/templates/base/apps/pyserver/scripts/chat.py.ejs +40 -0
- package/packages/nexus-cli/templates/base/apps/pyserver/scripts/hello.py.ejs +5 -0
- package/packages/nexus-cli/templates/base/dockerignore +0 -1
- package/packages/nexus-cli/templates/base/gitignore +4 -1
- package/packages/nexus-cli/templates/base/nexus.config.ts.ejs +37 -1
- package/packages/nexus-cli/templates/base/package.json.ejs +12 -11
- package/packages/nexus-core/package.json +3 -2
- package/packages/nexus-core/src/app/Storage.ts +616 -9
- package/packages/nexus-core/src/app/Upload.ts +114 -0
- package/packages/nexus-core/src/app/adminModule.ts +256 -52
- package/packages/nexus-core/src/app/authModule.ts +56 -30
- package/packages/nexus-core/src/app/createNexusApp.ts +171 -28
- package/packages/nexus-core/src/app/databaseAdminModule.ts +171 -2
- package/packages/nexus-core/src/app/discover.ts +4 -2
- package/packages/nexus-core/src/app/errorPages.ts +21 -5
- package/packages/nexus-core/src/app/fusionEngine.ts +53 -0
- package/packages/nexus-core/src/app/index.ts +3 -0
- package/packages/nexus-core/src/app/preflightModule.ts +41 -4
- package/packages/nexus-core/src/app/userStore.ts +321 -0
- package/packages/nexus-core/src/certs/generator.ts +224 -0
- package/packages/nexus-core/src/config/dbAccess.ts +26 -0
- package/packages/nexus-core/src/config/defaults.ts +34 -4
- package/packages/nexus-core/src/config/index.ts +1 -0
- package/packages/nexus-core/src/config/schema.ts +72 -6
- package/packages/nexus-core/src/config/types.ts +94 -1
- package/packages/nexus-core/src/http/Server.ts +27 -3
- package/packages/nexus-core/src/http/static.ts +1 -1
- package/packages/nexus-crypto/package.json +1 -1
- package/packages/nexus-crypto/src/index.ts +2 -1
- package/packages/nexus-crypto/src/license.ts +141 -0
- package/packages/nexus-data/package.json +1 -1
- package/packages/nexus-data/src/errors.ts +11 -3
- package/packages/nexus-data/src/model/Model.ts +25 -1
- package/packages/nexus-data/src/schema/Schema.ts +1 -1
- package/packages/nexus-email/package.json +1 -1
- package/packages/nexus-examples/examples/chat/apps/backend/src/ws/chat.room.ts +11 -0
- package/packages/nexus-examples/examples/emotion-garden/apps/backend/src/live/_nav.js +17 -0
- package/packages/nexus-examples/examples/emotion-garden/apps/backend/src/live/garden.live.js +105 -0
- package/packages/nexus-examples/examples/file-storage/README.md +22 -4
- package/packages/nexus-examples/examples/file-storage/apps/backend/src/graphql/app.graph.ts +60 -2
- package/packages/nexus-examples/examples/file-storage/apps/backend/src/handlers/file.handler.ts +164 -6
- package/packages/nexus-examples/examples/file-storage/apps/frontend/src/main.tsx +32 -7
- package/packages/nexus-examples/examples/fusion-tree/README.md +169 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/backend/src/handlers/ai.handler.ts +368 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/backend/src/handlers/cache.handler.ts +79 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/backend/src/handlers/fusion.handler.ts +437 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/backend/src/routes/cache.ts +21 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/backend/src/routes/fusion.ts +40 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/backend/src/services/AiService.ts +184 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/backend/src/services/CacheService.ts +65 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/backend/src/services/FusionService.ts +141 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/backend/src/services/QueryCache.ts +169 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/frontend/src/AiPanel.tsx +179 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/frontend/src/CachePanel.tsx +114 -0
- package/packages/nexus-examples/examples/fusion-tree/apps/frontend/src/main.tsx +412 -0
- package/packages/nexus-examples/examples/future-more/apps/backend/src/live/_nav.js +29 -0
- package/packages/nexus-examples/examples/future-more/apps/backend/src/live/ai.live.js +84 -0
- package/packages/nexus-examples/examples/future-more/apps/backend/src/live/all-examples.live.js +42 -0
- package/packages/nexus-examples/examples/future-more/apps/backend/src/live/chat.live.js +32 -0
- package/packages/nexus-examples/examples/future-more/apps/backend/src/live/counter.live.js +24 -0
- package/packages/nexus-examples/examples/future-more/apps/backend/src/live/gallery.live.js +46 -0
- package/packages/nexus-examples/examples/future-more/apps/backend/src/live/posts.live.js +32 -0
- package/packages/nexus-examples/examples/future-more/apps/backend/src/live/presence.live.js +47 -0
- package/packages/nexus-examples/examples/future-more/apps/backend/src/live/stats.live.js +50 -0
- package/packages/nexus-examples/examples/image-lab/README.md +26 -0
- package/packages/nexus-examples/examples/image-lab/apps/backend/src/routes/images.ts +184 -0
- package/packages/nexus-examples/examples/image-lab/apps/frontend/src/App.tsx +235 -0
- package/packages/nexus-examples/examples/image-lab/apps/frontend/src/main.tsx +6 -0
- package/packages/nexus-examples/examples/mario-platformer/README.md +13 -0
- package/packages/nexus-examples/examples/mario-platformer/apps/backend/src/live/_nav.js +16 -0
- package/packages/nexus-examples/examples/mario-platformer/apps/backend/src/live/root.live.js +178 -0
- package/packages/nexus-examples/examples/morph-ui/apps/backend/src/live/_nav.js +19 -0
- package/packages/nexus-examples/examples/morph-ui/apps/backend/src/live/cards.live.js +58 -0
- package/packages/nexus-examples/examples/morph-ui/apps/backend/src/live/list.live.js +84 -0
- package/packages/nexus-examples/examples/morph-ui/apps/backend/src/live/morphui.live.js +189 -0
- package/packages/nexus-examples/examples/python-bridge/README.md +12 -0
- package/packages/nexus-examples/examples/python-bridge/_setup.mjs +27 -0
- package/packages/nexus-examples/examples/python-bridge/apps/backend/src/routes/python-bridge.ts +120 -0
- package/packages/nexus-examples/examples/python-bridge/apps/frontend/src/App.tsx +61 -0
- package/packages/nexus-examples/examples/python-bridge-future/README.md +10 -0
- package/packages/nexus-examples/examples/python-bridge-future/_setup.mjs +27 -0
- package/packages/nexus-examples/examples/python-bridge-future/apps/backend/src/live/_nav.js +16 -0
- package/packages/nexus-examples/examples/python-bridge-future/apps/backend/src/live/p.live.js +12 -0
- package/packages/nexus-examples/examples/python-bridge-future/apps/backend/src/live/p1.live.js +13 -0
- package/packages/nexus-examples/examples/python-bridge-future/apps/backend/src/live/root.live.js +53 -0
- package/packages/nexus-examples/examples/saas-starter/apps/backend/src/graphql/app.graph.ts +4 -3
- package/packages/nexus-examples/examples/saas-starter/apps/frontend/src/main.tsx +6 -3
- package/packages/nexus-examples/package.json +1 -1
- package/packages/nexus-examples/src/index.ts +84 -1
- package/packages/nexus-fusion/package.json +41 -0
- package/packages/nexus-fusion/src/adapters/MongoMirror.ts +115 -0
- package/packages/nexus-fusion/src/adapters/RedisBridge.ts +89 -0
- package/packages/nexus-fusion/src/adapters/index.ts +3 -0
- package/packages/nexus-fusion/src/cache/FusionCache.ts +422 -0
- package/packages/nexus-fusion/src/cache/index.ts +2 -0
- package/packages/nexus-fusion/src/firebase/offlineQueue.ts +89 -0
- package/packages/nexus-fusion/src/firebase/onSnapshot.ts +98 -0
- package/packages/nexus-fusion/src/firebase/refs.ts +422 -0
- package/packages/nexus-fusion/src/firebase/securityRules.ts +121 -0
- package/packages/nexus-fusion/src/firebase/transforms.ts +73 -0
- package/packages/nexus-fusion/src/firebase/types.ts +45 -0
- package/packages/nexus-fusion/src/index.ts +248 -0
- package/packages/nexus-fusion/src/native/index.d.ts +0 -0
- package/packages/nexus-fusion/src/native/loader.ts +168 -0
- package/packages/nexus-fusion/src/playground/playgroundHtml.ts +461 -0
- package/packages/nexus-fusion/src/realtime/Emitter.ts +30 -0
- package/packages/nexus-fusion/src/schemas/auth.ts +32 -0
- package/packages/nexus-fusion/src/schemas/cms.ts +40 -0
- package/packages/nexus-fusion/src/schemas/core.ts +155 -0
- package/packages/nexus-fusion/src/schemas/ecommerce.ts +46 -0
- package/packages/nexus-fusion/src/schemas/index.ts +19 -0
- package/packages/nexus-fusion-core/package.json +20 -0
- package/packages/nexus-fusion-core/src/cache.rs +196 -0
- package/packages/nexus-fusion-core/src/engine.rs +772 -0
- package/packages/nexus-fusion-core/src/errors.rs +51 -0
- package/packages/nexus-fusion-core/src/idgen.rs +100 -0
- package/packages/nexus-fusion-core/src/lib.rs +17 -0
- package/packages/nexus-fusion-core/src/napi_glue.rs +333 -0
- package/packages/nexus-fusion-core/src/query.rs +513 -0
- package/packages/nexus-fusion-core/src/tree.rs +282 -0
- package/packages/nexus-fusion-core/src/types.rs +147 -0
- package/packages/nexus-fusion-core/src/wal.rs +1877 -0
- package/packages/nexus-future/LICENSE +21 -0
- package/packages/nexus-future/package.json +63 -0
- package/packages/nexus-future/src/ai/autoui.js +229 -0
- package/packages/nexus-future/src/ai/crud.js +144 -0
- package/packages/nexus-future/src/ai/fields.js +141 -0
- package/packages/nexus-future/src/ai/http.js +180 -0
- package/packages/nexus-future/src/ai/index.js +132 -0
- package/packages/nexus-future/src/ai/mock.js +14 -0
- package/packages/nexus-future/src/html/index.js +22 -0
- package/packages/nexus-future/src/html/signals.js +191 -0
- package/packages/nexus-future/src/html/template.js +459 -0
- package/packages/nexus-future/src/index.js +50 -0
- package/packages/nexus-future/src/live/client.js +580 -0
- package/packages/nexus-future/src/live/index.js +5 -0
- package/packages/nexus-future/src/live/morph.js +198 -0
- package/packages/nexus-future/src/live/rpc.js +53 -0
- package/packages/nexus-future/src/live/server.js +464 -0
- package/packages/nexus-future/src/live/stream.js +104 -0
- package/packages/nexus-future/src/page/index.js +180 -0
- package/packages/nexus-future/src/server/mount.js +192 -0
- package/packages/nexus-future/src/ui/canvas.js +142 -0
- package/packages/nexus-future/src/ui/index.js +2 -0
- package/packages/nexus-future/src/ui/sense.js +233 -0
- package/packages/nexus-graphql/package.json +1 -1
- package/packages/nexus-payments/package.json +23 -22
- package/packages/nexus-payments/src/index.ts +4 -0
- package/packages/nexus-plugins/package.json +1 -1
- package/packages/nexus-postcss/package.json +1 -1
- package/packages/nexus-realtime/package.json +1 -1
- package/packages/nexus-safe-goto/package.json +1 -1
- package/packages/nexus-telemetry/package.json +1 -1
- package/src/index.js +34 -0
- package/docs/ARCHITECTURE.md +0 -169
- package/docs/CLI.md +0 -147
- package/docs/CONVENTIONS.md +0 -243
- package/docs/MULTI_APP.md +0 -107
- package/docs/STORAGE.md +0 -118
- package/src/index.ts +0 -53
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* morph.js — morphdom-lite.
|
|
3
|
+
*
|
|
4
|
+
* Diffs two HTML strings / node trees and patches a live DOM element in
|
|
5
|
+
* place: keyed children (data-key) move instead of rebuild, other children
|
|
6
|
+
* patch positionally, attributes update individually, text nodes update in
|
|
7
|
+
* place. Event handler attributes (data-fx*) are preserved — never diffed
|
|
8
|
+
* away — because the server re-issues fresh ids each render.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Patch `target` (a live DOM element) to match `html`.
|
|
13
|
+
* `html` is a full element string (e.g. '<div id="t">…</div>') whose root
|
|
14
|
+
* should correspond to `target` itself — attributes patch on target,
|
|
15
|
+
* children morph recursively.
|
|
16
|
+
* @param {Element} target
|
|
17
|
+
* @param {string} html
|
|
18
|
+
*/
|
|
19
|
+
export function morph(target, html) {
|
|
20
|
+
const tpl = target.ownerDocument.createElement('template');
|
|
21
|
+
tpl.innerHTML = html;
|
|
22
|
+
const root = tpl.content.firstElementChild;
|
|
23
|
+
if (!root) return;
|
|
24
|
+
morphNode(target, root);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Patch the children of `target` to match the children of `source`.
|
|
29
|
+
* @param {Element | DocumentFragment} target
|
|
30
|
+
* @param {Element | DocumentFragment} source
|
|
31
|
+
*/
|
|
32
|
+
export function morphChildren(target, source) {
|
|
33
|
+
const src = [...source.childNodes];
|
|
34
|
+
const dst = [...target.childNodes];
|
|
35
|
+
|
|
36
|
+
// Keyed pass: when both lists carry data-key children, reconcile by key —
|
|
37
|
+
// existing nodes are morphed in place and moved to their new position;
|
|
38
|
+
// missing keys are inserted; vanished keys are removed.
|
|
39
|
+
const srcKeyed = src.filter((n) => n instanceof Element && n.hasAttribute('data-key'));
|
|
40
|
+
const dstKeyed = dst.filter((n) => n instanceof Element && n.hasAttribute('data-key'));
|
|
41
|
+
if (srcKeyed.length > 0 && dstKeyed.length > 0) {
|
|
42
|
+
morphKeyed(target, src, dst);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let srcIdx = 0;
|
|
47
|
+
let dstIdx = 0;
|
|
48
|
+
|
|
49
|
+
// Positional pass: pair up same-position children.
|
|
50
|
+
while (dstIdx < dst.length || srcIdx < src.length) {
|
|
51
|
+
const s = src[srcIdx];
|
|
52
|
+
const d = dst[dstIdx];
|
|
53
|
+
|
|
54
|
+
if (s === undefined && d === undefined) break;
|
|
55
|
+
|
|
56
|
+
if (s !== undefined && d !== undefined && sameType(s, d)) {
|
|
57
|
+
morphNode(/** @type {Node} */ (d), s);
|
|
58
|
+
srcIdx++;
|
|
59
|
+
dstIdx++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (s !== undefined && d === undefined) {
|
|
64
|
+
target.appendChild(cloneForMorph(s));
|
|
65
|
+
srcIdx++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (s === undefined && d !== undefined) {
|
|
69
|
+
target.removeChild(/** @type {Node} */ (d));
|
|
70
|
+
dstIdx++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
// Different types at same position: replace.
|
|
74
|
+
target.replaceChild(cloneForMorph(s), /** @type {Node} */ (d));
|
|
75
|
+
srcIdx++;
|
|
76
|
+
dstIdx++;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Keyed reconciliation for children lists where (a subset of) children have
|
|
82
|
+
* data-key attributes. Non-keyed children pair positionally at their spots.
|
|
83
|
+
* @param {Element | DocumentFragment} target
|
|
84
|
+
* @param {Node[]} src
|
|
85
|
+
* @param {Node[]} dst
|
|
86
|
+
*/
|
|
87
|
+
function morphKeyed(target, src, dst) {
|
|
88
|
+
/** @type {Map<string, Element>} */
|
|
89
|
+
const dstByKey = new Map();
|
|
90
|
+
for (const n of dst) {
|
|
91
|
+
if (n instanceof Element && n.hasAttribute('data-key')) {
|
|
92
|
+
dstByKey.set(String(n.getAttribute('data-key')), n);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** @type {Node | null} */
|
|
97
|
+
let cursor = null; // last placed node
|
|
98
|
+
|
|
99
|
+
for (const s of src) {
|
|
100
|
+
const key = s instanceof Element ? s.getAttribute('data-key') : null;
|
|
101
|
+
/** @type {Node} */
|
|
102
|
+
let node;
|
|
103
|
+
if (key) {
|
|
104
|
+
const existing = dstByKey.get(String(key));
|
|
105
|
+
if (existing) {
|
|
106
|
+
morphNode(existing, s);
|
|
107
|
+
node = existing;
|
|
108
|
+
dstByKey.delete(String(key));
|
|
109
|
+
} else {
|
|
110
|
+
node = cloneForMorph(s);
|
|
111
|
+
}
|
|
112
|
+
// Place at correct position (move if needed).
|
|
113
|
+
/** @type {Node | null} */
|
|
114
|
+
const expectedNext = cursor ? cursor.nextSibling : target.firstChild;
|
|
115
|
+
if (node !== expectedNext) {
|
|
116
|
+
target.insertBefore(node, /** @type {Node} */ (expectedNext));
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
// Non-keyed node: append in order after cursor.
|
|
120
|
+
node = cloneForMorph(s);
|
|
121
|
+
target.insertBefore(node, cursor ? cursor.nextSibling : null);
|
|
122
|
+
}
|
|
123
|
+
cursor = node;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Remove dst leftovers not consumed by key matching.
|
|
127
|
+
for (const leftover of dstByKey.values()) {
|
|
128
|
+
if (leftover.parentNode) leftover.parentNode.removeChild(leftover);
|
|
129
|
+
}
|
|
130
|
+
// Remove non-keyed destination nodes beyond what source consumed.
|
|
131
|
+
for (const d of dst) {
|
|
132
|
+
if (d instanceof Element && d.hasAttribute('data-key')) continue;
|
|
133
|
+
if (d.parentNode && !src.includes(d) && !([...target.childNodes]).includes(/** @type {ChildNode} */ (d))) {
|
|
134
|
+
/** @type {ChildNode} */ (d).remove();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Do two nodes have compatible types (morphable into each other)?
|
|
141
|
+
* @param {Node} a — live node
|
|
142
|
+
* @param {Node} b — incoming node
|
|
143
|
+
*/
|
|
144
|
+
function sameType(a, b) {
|
|
145
|
+
if (a.nodeType === 3 && b.nodeType === 3) return true; // text
|
|
146
|
+
if (a.nodeType === 8 && b.nodeType === 8) return true; // comment
|
|
147
|
+
if (a instanceof Element && b instanceof Element) {
|
|
148
|
+
return a.tagName === b.tagName;
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Patch live node `a` to match incoming `b`. b stays untouched (owned by the
|
|
155
|
+
* parsed template); a's children recurse.
|
|
156
|
+
* @param {Node} a
|
|
157
|
+
* @param {Node} b
|
|
158
|
+
*/
|
|
159
|
+
function morphNode(a, b) {
|
|
160
|
+
if (a.nodeType === 3) {
|
|
161
|
+
const next = String(/** @type {Text} */ (b).data);
|
|
162
|
+
if (/** @type {Text} */ (a).data !== next) /** @type {Text} */ (a).data = next;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (a.nodeType === 8) {
|
|
166
|
+
const next = String(/** @type {Comment} */ (b).data);
|
|
167
|
+
if (/** @type {Comment} */ (a).data !== next) /** @type {Comment} */ (a).data = next;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (!(a instanceof Element) || !(b instanceof Element)) return;
|
|
171
|
+
|
|
172
|
+
// Attributes: apply incoming, remove stale (except data-fx* which are
|
|
173
|
+
// server-issued per-render and replaced wholesale below).
|
|
174
|
+
/** @type {Set<string>} */
|
|
175
|
+
const incoming = new Set();
|
|
176
|
+
for (const attr of b.attributes) {
|
|
177
|
+
incoming.add(attr.name);
|
|
178
|
+
if (a.getAttribute(attr.name) !== attr.value) {
|
|
179
|
+
a.setAttribute(attr.name, attr.value);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const attr of [...a.attributes]) {
|
|
183
|
+
if (attr.name.startsWith('data-fx')) continue; // managed by handler binding
|
|
184
|
+
if (!incoming.has(attr.name)) a.removeAttribute(attr.name);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
morphChildren(a, b);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Clone a template node into a live node. Elements are cloned deeply;
|
|
192
|
+
* data-fx attributes survive so the delegated listener finds fresh ids.
|
|
193
|
+
* @param {Node} node
|
|
194
|
+
* @returns {Node}
|
|
195
|
+
*/
|
|
196
|
+
function cloneForMorph(node) {
|
|
197
|
+
return node.cloneNode(true);
|
|
198
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rpc.js — live() RPC protocol.
|
|
3
|
+
*
|
|
4
|
+
* Server-side handlers are captured per-render with captureHandlers() and
|
|
5
|
+
* stored in the LiveServer's session. The browser runtime sends:
|
|
6
|
+
*
|
|
7
|
+
* { t: 'rpc', rid, id, args? }
|
|
8
|
+
*
|
|
9
|
+
* over the WS connection; the server invokes the handler, then pushes a
|
|
10
|
+
* patch (or patch set) produced by re-rendering the live view.
|
|
11
|
+
*
|
|
12
|
+
* The rid (render id) routes to the correct handler set: each page render
|
|
13
|
+
* creates a fresh set, and the browser always sends the rid it last
|
|
14
|
+
* received, so stale clicks after a re-render fail safely (no-op + warn).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { captureHandlers } from '../html/index.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Run `render()` with handler capture. Returns the HTML string plus the
|
|
21
|
+
* captured handler map.
|
|
22
|
+
* @param {() => import('../html/template.js').HtmlResult} render
|
|
23
|
+
*/
|
|
24
|
+
export function renderWithHandlers(render) {
|
|
25
|
+
/** @type {Map<string, Function>} */
|
|
26
|
+
const handlers = new Map();
|
|
27
|
+
const stop = captureHandlers(/** @type {any} */ (handlers));
|
|
28
|
+
let html = '';
|
|
29
|
+
try {
|
|
30
|
+
html = String(render());
|
|
31
|
+
} finally {
|
|
32
|
+
stop();
|
|
33
|
+
}
|
|
34
|
+
return { html, handlers };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Invoke a captured handler by id.
|
|
39
|
+
* @param {Map<string, Function>} handlers
|
|
40
|
+
* @param {string} id
|
|
41
|
+
* @param {unknown[]} args
|
|
42
|
+
* @returns {{ ok: boolean, error?: string }}
|
|
43
|
+
*/
|
|
44
|
+
export function invokeHandler(handlers, id, args) {
|
|
45
|
+
const fn = handlers.get(id);
|
|
46
|
+
if (typeof fn !== 'function') return { ok: false, error: `unknown handler ${id}` };
|
|
47
|
+
try {
|
|
48
|
+
fn(...(Array.isArray(args) ? args : []));
|
|
49
|
+
return { ok: true };
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
52
|
+
}
|
|
53
|
+
}
|