@vmz/core 0.0.2 → 0.0.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/README.md +4 -2
- package/dist/dom.d.ts +7 -10
- package/dist/dom.js +59 -67
- package/dist/serve-host.mjs +2 -2
- package/dist/server.d.ts +1 -2
- package/dist/server.js +7 -8
- package/package.json +2 -2
- package/dist/serve-host.js +0 -736
package/README.md
CHANGED
|
@@ -31,7 +31,8 @@ state write
|
|
|
31
31
|
-> direct patch / switch / reconcile
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
There is no default detour through “execute every component that might matter, build virtual nodes, then compare them.”
|
|
34
|
+
There is no default detour through “execute every component that might matter, build virtual nodes, then compare them.”
|
|
35
|
+
If analysis must widen, it widens to a safe region and should preserve the reason.
|
|
35
36
|
|
|
36
37
|
## More than DOM updates
|
|
37
38
|
|
|
@@ -41,7 +42,8 @@ There is no default detour through “execute every component that might matter,
|
|
|
41
42
|
- **Resumption:** the browser attaches to server-produced work at the smallest reachable boundary.
|
|
42
43
|
- **Zero-JS delivery:** pages with no interactive requirement do not need an eager framework shell.
|
|
43
44
|
|
|
44
|
-
The runtime should not grow a second compiler made of reflection, string dependencies, or generic proxies. Its quality
|
|
45
|
+
The runtime should not grow a second compiler made of reflection, string dependencies, or generic proxies. Its quality
|
|
46
|
+
comes from faithfully executing the generated plan while keeping the production surface focused.
|
|
45
47
|
|
|
46
48
|
## License
|
|
47
49
|
|
package/dist/dom.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* VMZ DOM / SSR runtime — precise patches, no VDOM diff.
|
|
3
3
|
*
|
|
4
|
-
* Design: 规划设计/vmz/04 · Gate 3 (no production `render()`)
|
|
5
4
|
*
|
|
6
5
|
* Direct components expose `__vmzCreate` / `__vmzSerialize` / `__vmzPlan`.
|
|
7
6
|
* Mount, SSR, hydrate, and resume all run that same schedule.
|
|
@@ -14,7 +13,7 @@ export declare function __vmzTraceEnable(on?: boolean): void;
|
|
|
14
13
|
export declare function __vmzPrecisionReset(): void;
|
|
15
14
|
export declare function __vmzTraceReset(): void;
|
|
16
15
|
/**
|
|
17
|
-
*
|
|
16
|
+
* StableId event snapshot (`vmz.dx.trace.v0` shape without schema stamp —
|
|
18
17
|
* host may wrap via ingestRuntimeTrace).
|
|
19
18
|
* @returns {{ schema: string, events: typeof traceBuf.events, status: string }}
|
|
20
19
|
*/
|
|
@@ -58,14 +57,14 @@ export declare function renderToString(Component: any, props?: {}): Promise<any>
|
|
|
58
57
|
export declare function renderToStream(Component: any, props?: {}, opts?: {}): AsyncGenerator<any, void, unknown>;
|
|
59
58
|
/**
|
|
60
59
|
* Mount once; later updates are dep patches only (never re-run structure).
|
|
61
|
-
* Requires compiler `__vmzCreate` (
|
|
60
|
+
* Requires compiler `__vmzCreate` (production Direct emit — no blueprint fallback).
|
|
62
61
|
* @param {new (props?: object) => any} Component
|
|
63
62
|
* @param {Element} container
|
|
64
63
|
* @param {object} [props]
|
|
65
64
|
*/
|
|
66
65
|
export declare function mount(Component: any, container: any, props?: {}): Promise<any>;
|
|
67
66
|
/**
|
|
68
|
-
* Snapshot plain state/prop field values for Island HMR (
|
|
67
|
+
* Snapshot plain state/prop field values for Island HMR (session).
|
|
69
68
|
* @param {object} inst
|
|
70
69
|
* @returns {Record<string, unknown> | null}
|
|
71
70
|
*/
|
|
@@ -76,7 +75,7 @@ export declare function snapshotInstanceState(inst: any): {};
|
|
|
76
75
|
*/
|
|
77
76
|
export declare function applyPreservedState(inst: any, state: any): void;
|
|
78
77
|
/**
|
|
79
|
-
*
|
|
78
|
+
* resume: attach to existing Island DOM without re-running construct structure or onMount.
|
|
80
79
|
* Consumes ResumeEntry product (`data-vmz-resume`) derived from the same Execution Plan.
|
|
81
80
|
* @param {new (props?: object) => any} Component
|
|
82
81
|
* @param {HTMLElement} container
|
|
@@ -105,12 +104,12 @@ export declare function hydrate(Component: any, container: any, props?: {}, opts
|
|
|
105
104
|
/**
|
|
106
105
|
* Tear down binders and stop patches. Safe to call more than once.
|
|
107
106
|
* Field writes after destroy no longer update DOM (values may still change).
|
|
108
|
-
|
|
107
|
+
*: also dispose owned DOM trees (child __vmzInst / region __vmzDispose).
|
|
109
108
|
* @param {object} inst
|
|
110
109
|
*/
|
|
111
110
|
export declare function destroy(inst: any): void;
|
|
112
111
|
/**
|
|
113
|
-
|
|
112
|
+
*: walk a DOM subtree and run lifetime dispose hooks + nested instance destroy.
|
|
114
113
|
* Does not mark the *calling* parent destroyed; safe from destroy(inst).
|
|
115
114
|
* @param {Node | null | undefined} root
|
|
116
115
|
*/
|
|
@@ -135,7 +134,6 @@ export declare function __vmzCancelTasks(inst: any): void;
|
|
|
135
134
|
export declare function __vmzTaskStatus(inst: any, key: any): any;
|
|
136
135
|
/**
|
|
137
136
|
* Mark a plain object as intentionally shared across ownership boundaries.
|
|
138
|
-
* Suppresses cross-component shared diagnostics (规划设计/vmz/13 §7.3).
|
|
139
137
|
* @param {any} value
|
|
140
138
|
*/
|
|
141
139
|
export declare function __vmzAllowShared(value: any): any;
|
|
@@ -167,7 +165,6 @@ export declare function __vmzReadPath(inst: any, root: any, segs: any): any;
|
|
|
167
165
|
*/
|
|
168
166
|
export declare function __vmzWritePathLogical(inst: any, root: any, segs: any, kind: any, rhs: any): any;
|
|
169
167
|
/**
|
|
170
|
-
* Compiler-inserted path write barrier (规划设计/vmz/13 §7.3).
|
|
171
168
|
* Mutates a plain owned object/array and schedules the same path notice Proxy would.
|
|
172
169
|
*
|
|
173
170
|
* Root-array index assigns (`tags[0] = x`) notify as field replace (structural),
|
|
@@ -193,7 +190,7 @@ export declare function __vmzWritePath(inst: any, root: any, segs: any, value: a
|
|
|
193
190
|
*/
|
|
194
191
|
export declare function __vmzArrayMutate(inst: any, root: any, baseSegs: any, method: any, args: any): any;
|
|
195
192
|
/**
|
|
196
|
-
*
|
|
193
|
+
* WriteBarrier: true when value is an owned plain object with path barriers (no Proxy).
|
|
197
194
|
* @param {any} value
|
|
198
195
|
*/
|
|
199
196
|
export declare function __vmzIsWriteBarrierOwned(value: any): boolean;
|
package/dist/dom.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* VMZ DOM / SSR runtime — precise patches, no VDOM diff.
|
|
4
4
|
*
|
|
5
|
-
* Design: 规划设计/vmz/04 · Gate 3 (no production `render()`)
|
|
6
5
|
*
|
|
7
6
|
* Direct components expose `__vmzCreate` / `__vmzSerialize` / `__vmzPlan`.
|
|
8
7
|
* Mount, SSR, hydrate, and resume all run that same schedule.
|
|
@@ -12,7 +11,6 @@
|
|
|
12
11
|
const components = Object.create(null);
|
|
13
12
|
/**
|
|
14
13
|
* Precision lab counters (test / MCP / benchmarks — not a user API).
|
|
15
|
-
* Design: 规划设计/vmz/12 §7
|
|
16
14
|
* Primary keys: BindingId (IR). `*ByDep` is transitional stable-string adapter.
|
|
17
15
|
*/
|
|
18
16
|
const precision = {
|
|
@@ -35,7 +33,7 @@ const precision = {
|
|
|
35
33
|
/** @type {Record<string, number>} BindingId → count */
|
|
36
34
|
patchesByBinding: Object.create(null),
|
|
37
35
|
};
|
|
38
|
-
/**
|
|
36
|
+
/** optional StableId event ring (enabled with precision or __vmzTraceEnable). */
|
|
39
37
|
const TRACE_CAP = 256;
|
|
40
38
|
/** @type {{ enabled: boolean, events: Array<{ kind: string, stableId: { kind: string, id: string }, dep?: string|null, t?: number, chunkId?: string|null }> }} */
|
|
41
39
|
const traceBuf = {
|
|
@@ -86,7 +84,7 @@ export function __vmzTraceReset() {
|
|
|
86
84
|
traceBuf.events = [];
|
|
87
85
|
}
|
|
88
86
|
/**
|
|
89
|
-
*
|
|
87
|
+
* StableId event snapshot (`vmz.dx.trace.v0` shape without schema stamp —
|
|
90
88
|
* host may wrap via ingestRuntimeTrace).
|
|
91
89
|
* @returns {{ schema: string, events: typeof traceBuf.events, status: string }}
|
|
92
90
|
*/
|
|
@@ -117,7 +115,7 @@ export function __vmzPrecisionSnapshot() {
|
|
|
117
115
|
};
|
|
118
116
|
}
|
|
119
117
|
/**
|
|
120
|
-
* @param {
|
|
118
|
+
* @param { => any} fn
|
|
121
119
|
* @param {string | null} [depKey]
|
|
122
120
|
* @param {number | string | null} [bindingId]
|
|
123
121
|
*/
|
|
@@ -173,9 +171,9 @@ export async function renderToString(Component, props = {}) {
|
|
|
173
171
|
if (typeof inst.onMount === 'function') {
|
|
174
172
|
await inst.onMount();
|
|
175
173
|
}
|
|
176
|
-
//
|
|
174
|
+
// production Direct emit: SSR only via Direct serialize schedule — never `render`.
|
|
177
175
|
if (!(Component && Component.__vmzDirect && typeof Component.__vmzCreate === 'function')) {
|
|
178
|
-
throw new Error(`vmz:dom renderToString() requires __vmzCreate (Direct); blueprint render() removed (
|
|
176
|
+
throw new Error(`vmz:dom renderToString() requires __vmzCreate (Direct); blueprint render() removed (production Direct emit)`);
|
|
179
177
|
}
|
|
180
178
|
const root = await runDirectSerializeTreeWithMounts(Component, inst);
|
|
181
179
|
return flattenSerializeNode(root);
|
|
@@ -202,7 +200,7 @@ export async function* renderToStream(Component, props = {}, opts = {}) {
|
|
|
202
200
|
if (aborted())
|
|
203
201
|
return;
|
|
204
202
|
if (!(Component && Component.__vmzDirect && typeof Component.__vmzCreate === 'function')) {
|
|
205
|
-
throw new Error(`vmz:dom renderToStream() requires __vmzCreate (Direct); blueprint render() removed (
|
|
203
|
+
throw new Error(`vmz:dom renderToStream() requires __vmzCreate (Direct); blueprint render() removed (production Direct emit)`);
|
|
206
204
|
}
|
|
207
205
|
const root = await runDirectSerializeTreeWithMounts(Component, inst);
|
|
208
206
|
if (aborted())
|
|
@@ -216,13 +214,13 @@ export async function* renderToStream(Component, props = {}, opts = {}) {
|
|
|
216
214
|
}
|
|
217
215
|
}
|
|
218
216
|
finally {
|
|
219
|
-
// Abort and normal completion both dispose the SSR instance (
|
|
217
|
+
// Abort and normal completion both dispose the SSR instance (lifetime).
|
|
220
218
|
destroy(inst);
|
|
221
219
|
}
|
|
222
220
|
}
|
|
223
221
|
/**
|
|
224
222
|
* Mount once; later updates are dep patches only (never re-run structure).
|
|
225
|
-
* Requires compiler `__vmzCreate` (
|
|
223
|
+
* Requires compiler `__vmzCreate` (production Direct emit — no blueprint fallback).
|
|
226
224
|
* @param {new (props?: object) => any} Component
|
|
227
225
|
* @param {Element} container
|
|
228
226
|
* @param {object} [props]
|
|
@@ -250,7 +248,7 @@ export async function mount(Component, container, props = {}) {
|
|
|
250
248
|
return inst;
|
|
251
249
|
}
|
|
252
250
|
/**
|
|
253
|
-
* Nested Direct `component
|
|
251
|
+
* Nested Direct `component` schedules child onMount asynchronously; drain before return
|
|
254
252
|
* so SSR/hydrate callers see post-mount DOM (e.g. UserCard Ada, not Loading…).
|
|
255
253
|
* @param {object} inst
|
|
256
254
|
*/
|
|
@@ -275,7 +273,7 @@ async function settlePendingChildMounts(inst) {
|
|
|
275
273
|
}
|
|
276
274
|
}
|
|
277
275
|
/**
|
|
278
|
-
* Direct create only (
|
|
276
|
+
* Direct create only (production Direct emit).
|
|
279
277
|
* @param {new (props?: object) => any} Component
|
|
280
278
|
* @param {object} inst
|
|
281
279
|
*/
|
|
@@ -283,7 +281,7 @@ async function createFromComponent(Component, inst) {
|
|
|
283
281
|
if (Component && Component.__vmzDirect && typeof Component.__vmzCreate === 'function') {
|
|
284
282
|
return runDirectCreate(Component, inst);
|
|
285
283
|
}
|
|
286
|
-
throw new Error(`vmz:dom mount requires __vmzCreate (Direct); blueprint render() removed (
|
|
284
|
+
throw new Error(`vmz:dom mount requires __vmzCreate (Direct); blueprint render() removed (production Direct emit)`);
|
|
287
285
|
}
|
|
288
286
|
/**
|
|
289
287
|
* @param {new (props?: object) => any} Component
|
|
@@ -299,7 +297,7 @@ function runDirectCreate(Component, inst) {
|
|
|
299
297
|
}
|
|
300
298
|
}
|
|
301
299
|
/**
|
|
302
|
-
*
|
|
300
|
+
* SSR: run the same __vmzCreate schedule against a serialize host (no render).
|
|
303
301
|
* @param {new (props?: object) => any} Component
|
|
304
302
|
* @param {object} inst
|
|
305
303
|
*/
|
|
@@ -609,7 +607,7 @@ const serializeApi = {
|
|
|
609
607
|
resolved[k] = v;
|
|
610
608
|
}
|
|
611
609
|
if (client) {
|
|
612
|
-
//
|
|
610
|
+
// resume: Island SSR includes body + ResumeEntry slice (same Direct schedule).
|
|
613
611
|
const child = serializeApi._ssrChildInstance(Ctor, resolved);
|
|
614
612
|
let body = null;
|
|
615
613
|
if (Ctor.__vmzDirect && typeof Ctor.__vmzCreate === 'function') {
|
|
@@ -683,15 +681,15 @@ const serializeApi = {
|
|
|
683
681
|
const directApi = {
|
|
684
682
|
/** @type {object | null} */
|
|
685
683
|
_inst: null,
|
|
686
|
-
/** @type {Array<{ deps: string[], fn:
|
|
684
|
+
/** @type {Array<{ deps: string[], fn: => any, bindingId?: number|string|null }> | null} */
|
|
687
685
|
_branchBinds: null,
|
|
688
|
-
/** @type {Array<
|
|
686
|
+
/** @type {Array< => void> | null} */
|
|
689
687
|
_itemPatches: null,
|
|
690
688
|
/**
|
|
691
|
-
* Active keyed-each context (
|
|
689
|
+
* Active keyed-each context (/): item binds + event delegation.
|
|
692
690
|
* @type {null | {
|
|
693
|
-
*
|
|
694
|
-
*
|
|
691
|
+
* noteItemBind: (bindingId: number|string|null, deps: string[], fn: => void) => void,
|
|
692
|
+
* needDelegate: (type: string) => void,
|
|
695
693
|
* }}
|
|
696
694
|
*/
|
|
697
695
|
_eachCtx: null,
|
|
@@ -735,9 +733,9 @@ const directApi = {
|
|
|
735
733
|
* @param {object} inst
|
|
736
734
|
* @param {number|string|null} bindingId
|
|
737
735
|
* @param {string[]} deps
|
|
738
|
-
* @param {
|
|
736
|
+
* @param { => any} get
|
|
739
737
|
* @param {Text} textNode
|
|
740
|
-
* @param {{ stable: string[], branches: Array<{ cond?:
|
|
738
|
+
* @param {{ stable: string[], branches: Array<{ cond?: => any, deps: string[] }> } | null | undefined} [cf]
|
|
741
739
|
*/
|
|
742
740
|
bindText(inst, bindingId, deps, get, textNode, cf) {
|
|
743
741
|
wireDirectBind(inst, bindingId, deps, get, (raw) => {
|
|
@@ -748,10 +746,10 @@ const directApi = {
|
|
|
748
746
|
* @param {object} inst
|
|
749
747
|
* @param {number|string|null} bindingId
|
|
750
748
|
* @param {string[]} deps
|
|
751
|
-
* @param {
|
|
749
|
+
* @param { => any} get
|
|
752
750
|
* @param {Element} el
|
|
753
751
|
* @param {string} name
|
|
754
|
-
* @param {{ stable: string[], branches: Array<{ cond?:
|
|
752
|
+
* @param {{ stable: string[], branches: Array<{ cond?: => any, deps: string[] }> } | null | undefined} [cf]
|
|
755
753
|
*/
|
|
756
754
|
bindAttr(inst, bindingId, deps, get, el, name, cf) {
|
|
757
755
|
wireDirectBind(inst, bindingId, deps, get, (raw) => {
|
|
@@ -774,9 +772,9 @@ const directApi = {
|
|
|
774
772
|
* @param {object} inst
|
|
775
773
|
* @param {number|string|null} bindingId
|
|
776
774
|
* @param {string[]} deps
|
|
777
|
-
* @param {
|
|
775
|
+
* @param { => any} get
|
|
778
776
|
* @param {Element} el
|
|
779
|
-
* @param {{ stable: string[], branches: Array<{ cond?:
|
|
777
|
+
* @param {{ stable: string[], branches: Array<{ cond?: => any, deps: string[] }> } | null | undefined} [cf]
|
|
780
778
|
*/
|
|
781
779
|
bindHtml(inst, bindingId, deps, get, el, cf) {
|
|
782
780
|
wireDirectBind(inst, bindingId, deps, get, (raw) => {
|
|
@@ -811,7 +809,7 @@ const directApi = {
|
|
|
811
809
|
if (isEventEntryStrategy(String(client))) {
|
|
812
810
|
host.setAttribute('data-vmz-entry', 'event');
|
|
813
811
|
}
|
|
814
|
-
//
|
|
812
|
+
// resume: resume on schedule; EventEntry may lazy-load chunk via __vmzLoadComponent.
|
|
815
813
|
scheduleClientOn(host, String(client), async () => {
|
|
816
814
|
const Ctor = await resolveComponent(name);
|
|
817
815
|
if (!Ctor)
|
|
@@ -848,7 +846,7 @@ const directApi = {
|
|
|
848
846
|
* @param {object} inst
|
|
849
847
|
* @param {number|string|null} bindingId
|
|
850
848
|
* @param {string[]} deps
|
|
851
|
-
* @param {Array<{ cond?:
|
|
849
|
+
* @param {Array<{ cond?: => any, create: (api: typeof directApi) => Node }>} branches
|
|
852
850
|
* @param {number|string|null} [regionId]
|
|
853
851
|
*/
|
|
854
852
|
ifBlock(inst, bindingId, deps, branches, regionId = null) {
|
|
@@ -859,7 +857,7 @@ const directApi = {
|
|
|
859
857
|
host.setAttribute('data-vmz-region', String(regionId));
|
|
860
858
|
/** @type {Array<Node | null>} */
|
|
861
859
|
const cached = branches.map(() => null);
|
|
862
|
-
/** @type {Array<Array<{ deps: string[], fn:
|
|
860
|
+
/** @type {Array<Array<{ deps: string[], fn: => any, bindingId?: number|string|null }>>} */
|
|
863
861
|
const branchBinds = branches.map(() => []);
|
|
864
862
|
let active = -1;
|
|
865
863
|
let gen = 0;
|
|
@@ -945,7 +943,7 @@ const directApi = {
|
|
|
945
943
|
registerBind(inst, deps || [], apply, bindingId);
|
|
946
944
|
if (directApi._itemPatches)
|
|
947
945
|
directApi._itemPatches.push(apply);
|
|
948
|
-
//
|
|
946
|
+
// parent destroy disposes all cached branch trees (pause ≠ destroy on switch).
|
|
949
947
|
host.__vmzDispose = () => {
|
|
950
948
|
for (let i = 0; i < cached.length; i++) {
|
|
951
949
|
unwireBranch(i);
|
|
@@ -960,11 +958,11 @@ const directApi = {
|
|
|
960
958
|
},
|
|
961
959
|
/**
|
|
962
960
|
* Direct keyed each — no blueprint `kind: "each"` dispatch.
|
|
963
|
-
*
|
|
961
|
+
* /: Set/Map + Fragment batch insert; item-local binds; host selected; event delegate.
|
|
964
962
|
* @param {object} inst
|
|
965
963
|
* @param {number|string|null} bindingId
|
|
966
964
|
* @param {string[]} deps
|
|
967
|
-
* @param {{ as?: string, list:
|
|
965
|
+
* @param {{ as?: string, list: => any, key?: (box: {item:any,index:number}) => any, createItem: (api: typeof directApi, box: {item:any,index:number}) => Node }} spec
|
|
968
966
|
* @param {number|string|null} [regionId]
|
|
969
967
|
*/
|
|
970
968
|
eachBlock(inst, bindingId, deps, spec, regionId = null) {
|
|
@@ -975,10 +973,10 @@ const directApi = {
|
|
|
975
973
|
const frag = document.createDocumentFragment();
|
|
976
974
|
frag.appendChild(start);
|
|
977
975
|
frag.appendChild(end);
|
|
978
|
-
/** @type {Map<any, { box: { item: any, index: number }, dom: Node, patches: Array<
|
|
976
|
+
/** @type {Map<any, { box: { item: any, index: number }, dom: Node, patches: Array< => void> }>} */
|
|
979
977
|
const keyed = new Map();
|
|
980
978
|
let gen = 0;
|
|
981
|
-
/** @type {Map<string,
|
|
979
|
+
/** @type {Map<string, => void>} */
|
|
982
980
|
const listDispatchers = new Map();
|
|
983
981
|
/** @type {Set<string>} */
|
|
984
982
|
const hostDispatchers = new Set();
|
|
@@ -1227,7 +1225,6 @@ const directApi = {
|
|
|
1227
1225
|
const box = { item: list[i], index: i };
|
|
1228
1226
|
const k = itemKey(box);
|
|
1229
1227
|
if (seen.has(k)) {
|
|
1230
|
-
console.error(`vmz:dom each: duplicate key ${String(k)} — undefined reuse; fix the key expression (规划设计/vmz/10 §7)`);
|
|
1231
1228
|
}
|
|
1232
1229
|
seen.add(k);
|
|
1233
1230
|
let entry = keyed.get(k);
|
|
@@ -1318,7 +1315,7 @@ const directApi = {
|
|
|
1318
1315
|
entry.box.index = i;
|
|
1319
1316
|
tagItemPatches(entry.patches, i);
|
|
1320
1317
|
for (const p of entry.patches) {
|
|
1321
|
-
// Leaf BindingId patches are owned by list/host dispatchers
|
|
1318
|
+
// Leaf BindingId patches are owned by list/host dispatchers .
|
|
1322
1319
|
if (p.__vmzBindingId != null)
|
|
1323
1320
|
continue;
|
|
1324
1321
|
if (patchHasBindingId(inst, p))
|
|
@@ -1340,7 +1337,7 @@ const directApi = {
|
|
|
1340
1337
|
/**
|
|
1341
1338
|
* @param {object} inst
|
|
1342
1339
|
* @param {string[]} deps
|
|
1343
|
-
* @param {
|
|
1340
|
+
* @param { => any} fn
|
|
1344
1341
|
* @param {number|string|null|undefined} bindingId
|
|
1345
1342
|
*/
|
|
1346
1343
|
function trackDirectBind(inst, deps, fn, bindingId = null) {
|
|
@@ -1350,7 +1347,7 @@ function trackDirectBind(inst, deps, fn, bindingId = null) {
|
|
|
1350
1347
|
directApi._itemPatches.push(fn);
|
|
1351
1348
|
return;
|
|
1352
1349
|
}
|
|
1353
|
-
//
|
|
1350
|
+
// item binds stay on entry.patches; eachBlock registers one dispatcher per BindingId.
|
|
1354
1351
|
if (directApi._itemPatches) {
|
|
1355
1352
|
fn.__vmzItemLocal = true;
|
|
1356
1353
|
directApi._itemPatches.push(fn);
|
|
@@ -1365,9 +1362,9 @@ function trackDirectBind(inst, deps, fn, bindingId = null) {
|
|
|
1365
1362
|
* @param {object} inst
|
|
1366
1363
|
* @param {number|string|null} bindingId
|
|
1367
1364
|
* @param {string[]} deps
|
|
1368
|
-
* @param {
|
|
1365
|
+
* @param { => any} get
|
|
1369
1366
|
* @param {(raw: any) => void} write
|
|
1370
|
-
* @param {{ stable: string[], branches: Array<{ cond?:
|
|
1367
|
+
* @param {{ stable: string[], branches: Array<{ cond?: => any, deps: string[] }> } | null | undefined} [cf]
|
|
1371
1368
|
*/
|
|
1372
1369
|
function wireDirectBind(inst, bindingId, deps, get, write, cf) {
|
|
1373
1370
|
let activeBranch = -1;
|
|
@@ -1416,7 +1413,7 @@ function wireDirectBind(inst, bindingId, deps, get, write, cf) {
|
|
|
1416
1413
|
const branch = cf.branches[next];
|
|
1417
1414
|
const nextDeps = [...(cf.stable || []), ...((branch && branch.deps) || [])];
|
|
1418
1415
|
const uniq = [...new Set(nextDeps)];
|
|
1419
|
-
// Item-local binds must never enter the global binder table (
|
|
1416
|
+
// Item-local binds must never enter the global binder table ( / jfb select).
|
|
1420
1417
|
if (apply.__vmzItemLocal) {
|
|
1421
1418
|
liveDeps = uniq;
|
|
1422
1419
|
return;
|
|
@@ -1475,7 +1472,7 @@ function eachHostApi(start, end) {
|
|
|
1475
1472
|
};
|
|
1476
1473
|
}
|
|
1477
1474
|
/**
|
|
1478
|
-
* Snapshot plain state/prop field values for Island HMR (
|
|
1475
|
+
* Snapshot plain state/prop field values for Island HMR (session).
|
|
1479
1476
|
* @param {object} inst
|
|
1480
1477
|
* @returns {Record<string, unknown> | null}
|
|
1481
1478
|
*/
|
|
@@ -1515,7 +1512,7 @@ export function applyPreservedState(inst, state) {
|
|
|
1515
1512
|
}
|
|
1516
1513
|
}
|
|
1517
1514
|
/**
|
|
1518
|
-
*
|
|
1515
|
+
* resume: attach to existing Island DOM without re-running construct structure or onMount.
|
|
1519
1516
|
* Consumes ResumeEntry product (`data-vmz-resume`) derived from the same Execution Plan.
|
|
1520
1517
|
* @param {new (props?: object) => any} Component
|
|
1521
1518
|
* @param {HTMLElement} container
|
|
@@ -1565,14 +1562,14 @@ export async function resume(Component, container, slice = null) {
|
|
|
1565
1562
|
}
|
|
1566
1563
|
}
|
|
1567
1564
|
else {
|
|
1568
|
-
// Island leaf adopt: preserve Element identity (
|
|
1565
|
+
// Island leaf adopt: preserve Element identity (resume nodeIdentity).
|
|
1569
1566
|
const node = runDirectResume(Component, inst, container);
|
|
1570
1567
|
if (node)
|
|
1571
1568
|
inst.__vmzDomRoot = node;
|
|
1572
1569
|
}
|
|
1573
1570
|
}
|
|
1574
1571
|
else {
|
|
1575
|
-
throw new Error(`vmz:dom resume() requires __vmzCreate (Direct); blueprint render() removed (
|
|
1572
|
+
throw new Error(`vmz:dom resume() requires __vmzCreate (Direct); blueprint render() removed (production Direct emit)`);
|
|
1576
1573
|
}
|
|
1577
1574
|
container.__vmzInst = inst;
|
|
1578
1575
|
container.__vmzResumed = true;
|
|
@@ -1628,7 +1625,7 @@ export function attachEventEntries(root = globalThis.document) {
|
|
|
1628
1625
|
}
|
|
1629
1626
|
}
|
|
1630
1627
|
/**
|
|
1631
|
-
* Adopt existing Island DOM while running the same `__vmzCreate` schedule (
|
|
1628
|
+
* Adopt existing Island DOM while running the same `__vmzCreate` schedule (resume).
|
|
1632
1629
|
* @param {new (props?: object) => any} Component
|
|
1633
1630
|
* @param {object} inst
|
|
1634
1631
|
* @param {Element} container
|
|
@@ -1712,9 +1709,9 @@ export async function hydrate(Component, container, props = {}, opts = {}) {
|
|
|
1712
1709
|
if (preserved) {
|
|
1713
1710
|
applyPreservedState(inst, preserved);
|
|
1714
1711
|
}
|
|
1715
|
-
//
|
|
1712
|
+
// production Direct emit: hydrate uses the same Direct schedule as resume (no render).
|
|
1716
1713
|
if (!(Component && Component.__vmzDirect && typeof Component.__vmzCreate === 'function')) {
|
|
1717
|
-
throw new Error(`vmz:dom hydrate() requires __vmzCreate (Direct); blueprint render() removed (
|
|
1714
|
+
throw new Error(`vmz:dom hydrate() requires __vmzCreate (Direct); blueprint render() removed (production Direct emit)`);
|
|
1718
1715
|
}
|
|
1719
1716
|
// Wire DOM + events BEFORE awaiting onMount. SSR shell is already visible; if we
|
|
1720
1717
|
// wait on RPC/bootstrap first, buttons look real but have no listeners (dead UI).
|
|
@@ -1747,7 +1744,7 @@ export async function hydrate(Component, container, props = {}, opts = {}) {
|
|
|
1747
1744
|
/**
|
|
1748
1745
|
* Tear down binders and stop patches. Safe to call more than once.
|
|
1749
1746
|
* Field writes after destroy no longer update DOM (values may still change).
|
|
1750
|
-
|
|
1747
|
+
*: also dispose owned DOM trees (child __vmzInst / region __vmzDispose).
|
|
1751
1748
|
* @param {object} inst
|
|
1752
1749
|
*/
|
|
1753
1750
|
export function destroy(inst) {
|
|
@@ -1755,7 +1752,7 @@ export function destroy(inst) {
|
|
|
1755
1752
|
return;
|
|
1756
1753
|
inst.__vmzDestroyed = true;
|
|
1757
1754
|
inst.__vmzFlushScheduled = false;
|
|
1758
|
-
//
|
|
1755
|
+
// async cancel: abort in-flight tasks before tearing down DOM.
|
|
1759
1756
|
__vmzCancelTasks(inst);
|
|
1760
1757
|
if (inst.__vmzDomRoot) {
|
|
1761
1758
|
disposeDomTree(inst.__vmzDomRoot);
|
|
@@ -1780,7 +1777,7 @@ export function destroy(inst) {
|
|
|
1780
1777
|
}
|
|
1781
1778
|
}
|
|
1782
1779
|
/**
|
|
1783
|
-
|
|
1780
|
+
*: walk a DOM subtree and run lifetime dispose hooks + nested instance destroy.
|
|
1784
1781
|
* Does not mark the *calling* parent destroyed; safe from destroy(inst).
|
|
1785
1782
|
* @param {Node | null | undefined} root
|
|
1786
1783
|
*/
|
|
@@ -1819,7 +1816,7 @@ export function disposeDomTree(root) {
|
|
|
1819
1816
|
* @param {ParentNode} [root]
|
|
1820
1817
|
*/
|
|
1821
1818
|
export function hydrateIslands(root = globalThis.document) {
|
|
1822
|
-
//
|
|
1819
|
+
// resume: hydrateIslands is an alias for resumeIslands (same Plan attach).
|
|
1823
1820
|
return resumeIslands(root);
|
|
1824
1821
|
}
|
|
1825
1822
|
export function scheduleClient(strategy, fn) {
|
|
@@ -1841,7 +1838,7 @@ function eventEntryType(strategy) {
|
|
|
1841
1838
|
}
|
|
1842
1839
|
function scheduleClientOn(el, strategy, fn) {
|
|
1843
1840
|
const run = () => {
|
|
1844
|
-
Promise.resolve(fn
|
|
1841
|
+
Promise.resolve(fn).catch((err) => console.error('vmz:dom island', err));
|
|
1845
1842
|
};
|
|
1846
1843
|
if (isEventEntryStrategy(strategy)) {
|
|
1847
1844
|
if (!el || typeof el.addEventListener !== 'function') {
|
|
@@ -1918,7 +1915,7 @@ export function __vmzRunTask(inst, key, fn) {
|
|
|
1918
1915
|
status: 'pending',
|
|
1919
1916
|
};
|
|
1920
1917
|
inst.__vmzTasks[k] = entry;
|
|
1921
|
-
// Invoke synchronously so event handlers can call preventDefault
|
|
1918
|
+
// Invoke synchronously so event handlers can call preventDefault before
|
|
1922
1919
|
// the browser continues the default action (form submit → native navigation).
|
|
1923
1920
|
// Async work still continues via the returned Promise.
|
|
1924
1921
|
let syncResult;
|
|
@@ -1992,7 +1989,7 @@ function createInstance(Component, props = {}) {
|
|
|
1992
1989
|
inst.__vmzDepToBindings = Object.create(null);
|
|
1993
1990
|
makeReactive(inst, Component.__vmzState || []);
|
|
1994
1991
|
makeReactive(inst, Component.__vmzProps || []);
|
|
1995
|
-
//
|
|
1992
|
+
// WriteBarrier: path / array writes call Component helpers (no import needed).
|
|
1996
1993
|
Component.__vmzWritePath = __vmzWritePath;
|
|
1997
1994
|
Component.__vmzWritePathLogical = __vmzWritePathLogical;
|
|
1998
1995
|
Component.__vmzReadPath = __vmzReadPath;
|
|
@@ -2003,13 +2000,12 @@ function createInstance(Component, props = {}) {
|
|
|
2003
2000
|
}
|
|
2004
2001
|
/** Shared plain-object owners under WriteBarrier (no Proxy). */
|
|
2005
2002
|
const wbSharedOwners = new WeakMap();
|
|
2006
|
-
/** Objects explicitly marked OK to share across component instances (13
|
|
2003
|
+
/** Objects explicitly marked OK to share across component instances (13 ). */
|
|
2007
2004
|
const wbAllowShared = new WeakSet();
|
|
2008
2005
|
/** @type {Array<{ kind: string, message: string }>} */
|
|
2009
2006
|
const wbCrossComponentDiags = [];
|
|
2010
2007
|
/**
|
|
2011
2008
|
* Mark a plain object as intentionally shared across ownership boundaries.
|
|
2012
|
-
* Suppresses cross-component shared diagnostics (规划设计/vmz/13 §7.3).
|
|
2013
2009
|
* @param {any} value
|
|
2014
2010
|
*/
|
|
2015
2011
|
export function __vmzAllowShared(value) {
|
|
@@ -2056,11 +2052,10 @@ function registerWbOwner(value, report, baseSegs = [], inst = null) {
|
|
|
2056
2052
|
return;
|
|
2057
2053
|
}
|
|
2058
2054
|
entry.owners.push({ report, baseSegs: baseSegs.slice(), inst });
|
|
2059
|
-
// Cross-component share without explicit allow → diagnose (13
|
|
2055
|
+
// Cross-component share without explicit allow → diagnose (13 ).
|
|
2060
2056
|
if (!wbAllowShared.has(value) && inst) {
|
|
2061
2057
|
const other = entry.owners.find((o) => o.inst && o.inst !== inst);
|
|
2062
2058
|
if (other) {
|
|
2063
|
-
const msg = 'vmz: plain object shared across component instances without __vmzAllowShared (规划设计/vmz/13 §7.3)';
|
|
2064
2059
|
if (!wbCrossComponentDiags.some((d) => d.message === msg)) {
|
|
2065
2060
|
wbCrossComponentDiags.push({ kind: 'shared_cross_component', message: msg });
|
|
2066
2061
|
}
|
|
@@ -2134,7 +2129,6 @@ export function __vmzWritePathLogical(inst, root, segs, kind, rhs) {
|
|
|
2134
2129
|
return __vmzWritePath(inst, root, segs, rhs);
|
|
2135
2130
|
}
|
|
2136
2131
|
/**
|
|
2137
|
-
* Compiler-inserted path write barrier (规划设计/vmz/13 §7.3).
|
|
2138
2132
|
* Mutates a plain owned object/array and schedules the same path notice Proxy would.
|
|
2139
2133
|
*
|
|
2140
2134
|
* Root-array index assigns (`tags[0] = x`) notify as field replace (structural),
|
|
@@ -2273,7 +2267,7 @@ const reactiveProxies = new WeakMap();
|
|
|
2273
2267
|
/** Plain objects using defineProperty write barriers (not Proxy). */
|
|
2274
2268
|
const writeBarrierOwned = new WeakSet();
|
|
2275
2269
|
/**
|
|
2276
|
-
*
|
|
2270
|
+
* WriteBarrier: true when value is an owned plain object with path barriers (no Proxy).
|
|
2277
2271
|
* @param {any} value
|
|
2278
2272
|
*/
|
|
2279
2273
|
export function __vmzIsWriteBarrierOwned(value) {
|
|
@@ -2331,7 +2325,7 @@ function notifyOwners(owners, localSegs) {
|
|
|
2331
2325
|
}
|
|
2332
2326
|
/**
|
|
2333
2327
|
* Field-owned write traps for plain objects / arrays on state fields.
|
|
2334
|
-
* Plain objects: WriteBarrier via defineProperty (no Proxy) —
|
|
2328
|
+
* Plain objects: WriteBarrier via defineProperty (no Proxy) — .
|
|
2335
2329
|
* Arrays: transitional Proxy until keyed collection barriers land.
|
|
2336
2330
|
* Shared raw objects notify **all** current owners.
|
|
2337
2331
|
*
|
|
@@ -2532,11 +2526,10 @@ function wrapArray(arr, report, pathSegs) {
|
|
|
2532
2526
|
* Still precise deps — never a full-tree re-render. Flush runs as a microtask;
|
|
2533
2527
|
* call `await flushPending(inst)` to apply synchronously (tests / immediate UI).
|
|
2534
2528
|
*
|
|
2535
|
-
* Design: 规划设计/vmz/12 §6 — parent write covers children; siblings stay separate.
|
|
2536
2529
|
*
|
|
2537
2530
|
* @param {object} inst
|
|
2538
2531
|
* @param {{ type: 'replace', root: string } | { type: 'path', root: string, segs: string[] } | string} notice
|
|
2539
|
-
*
|
|
2532
|
+
* string form is transitional field-root alias for replace.
|
|
2540
2533
|
*/
|
|
2541
2534
|
function scheduleRefresh(inst, notice) {
|
|
2542
2535
|
if (!inst || inst.__vmzDestroyed)
|
|
@@ -2822,7 +2815,6 @@ function pathDirtyCovers(node, depSegs) {
|
|
|
2822
2815
|
}
|
|
2823
2816
|
/**
|
|
2824
2817
|
* Dual-track match retained for tests / tooling.
|
|
2825
|
-
* Design: 规划设计/vmz/11 §2.2 + 12 §6 parent-covers-children.
|
|
2826
2818
|
* @param {{ type: string, root: string, segs?: string[] }} notice
|
|
2827
2819
|
* @param {string} key
|
|
2828
2820
|
*/
|
|
@@ -2971,7 +2963,7 @@ function reindexBindingDeps(inst, bindingId, deps) {
|
|
|
2971
2963
|
/**
|
|
2972
2964
|
* @param {object} inst
|
|
2973
2965
|
* @param {string[]} deps
|
|
2974
|
-
* @param {
|
|
2966
|
+
* @param { => any} fn
|
|
2975
2967
|
* @param {number|string|null|undefined} [bindingId]
|
|
2976
2968
|
*/
|
|
2977
2969
|
function registerBind(inst, deps, fn, bindingId = null) {
|
|
@@ -2998,7 +2990,7 @@ function registerBind(inst, deps, fn, bindingId = null) {
|
|
|
2998
2990
|
/**
|
|
2999
2991
|
* @param {object} inst
|
|
3000
2992
|
* @param {string[]} deps
|
|
3001
|
-
* @param {
|
|
2993
|
+
* @param { => any} fn
|
|
3002
2994
|
* @param {number|string|null|undefined} [bindingId]
|
|
3003
2995
|
*/
|
|
3004
2996
|
function unregisterBind(inst, deps, fn, bindingId = null) {
|
package/dist/serve-host.mjs
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* `VMZ_DEV=1`: POST `/__vmz/reload` soft-reloads modules (cache-bust import);
|
|
11
11
|
* GET `/__vmz/events` SSE notifies the browser:
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* - island HMR → re-import `entry-client.js` (no full document reload)
|
|
13
|
+
* - otherwise → `location.reload`
|
|
14
14
|
*/
|
|
15
15
|
import http from 'node:http';
|
|
16
16
|
import path from 'node:path';
|
package/dist/server.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Minimal VMZ runtime — `#server` invoke + RPC/REST HTTP + optional static/SSR.
|
|
3
3
|
*
|
|
4
|
-
* Design: 规划设计/vmz/08-虚拟server.md, 07-REST接口.md
|
|
5
4
|
*
|
|
6
5
|
* Browser-safe: no static `node:*` imports. Node builtins are loaded only inside
|
|
7
6
|
* Node/SSR request handlers so client bundles can `import { callServer }`.
|
|
8
7
|
*/
|
|
9
8
|
/**
|
|
10
|
-
* Map `#server/foo` → filesystem / URL the host can `import
|
|
9
|
+
* Map `#server/foo` → filesystem / URL the host can `import`.
|
|
11
10
|
* Only set this in Node/SSR hosts — browser bundles must omit it so RPC goes HTTP.
|
|
12
11
|
*/
|
|
13
12
|
export declare function setServerModuleResolver(fn: any): void;
|
package/dist/server.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Minimal VMZ runtime — `#server` invoke + RPC/REST HTTP + optional static/SSR.
|
|
4
4
|
*
|
|
5
|
-
* Design: 规划设计/vmz/08-虚拟server.md, 07-REST接口.md
|
|
6
5
|
*
|
|
7
6
|
* Browser-safe: no static `node:*` imports. Node builtins are loaded only inside
|
|
8
7
|
* Node/SSR request handlers so client bundles can `import { callServer }`.
|
|
@@ -11,12 +10,12 @@
|
|
|
11
10
|
/** @typedef {{ verb: string, path: string, moduleId: string, method: string, className?: string }} Route */
|
|
12
11
|
/**
|
|
13
12
|
* @typedef {{
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* distDir?: string,
|
|
14
|
+
* renderIndex?: => Promise<string> | string,
|
|
15
|
+
* renderIndexStream?: (opts?: { signal?: AbortSignal }) => AsyncIterable<string>,
|
|
16
|
+
* renderPage?: (pathname: string) => Promise<string | null> | string | null,
|
|
17
|
+
* renderPageStream?: (pathname: string, opts?: { signal?: AbortSignal, searchParams?: URLSearchParams, cookieHeader?: string }) => Promise<AsyncIterable<string> | null> | AsyncIterable<string> | null,
|
|
18
|
+
* req?: import('node:http').IncomingMessage,
|
|
20
19
|
* }} NodeRequestOptions
|
|
21
20
|
*/
|
|
22
21
|
const DEFAULT_RPC_PATH = '/__vmz/rpc';
|
|
@@ -25,7 +24,7 @@ let resolveServerModule = null;
|
|
|
25
24
|
/** @type {Route[]} */
|
|
26
25
|
let routes = [];
|
|
27
26
|
/**
|
|
28
|
-
* Map `#server/foo` → filesystem / URL the host can `import
|
|
27
|
+
* Map `#server/foo` → filesystem / URL the host can `import`.
|
|
29
28
|
* Only set this in Node/SSR hosts — browser bundles must omit it so RPC goes HTTP.
|
|
30
29
|
*/
|
|
31
30
|
export function setServerModuleResolver(fn) {
|