@vmz/core 0.2.5 → 0.2.6
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/browser/client-nav.d.ts +5 -55
- package/dist/browser/client-nav.js +5 -769
- package/dist/browser/diagnostics/precision-trace.d.ts +47 -0
- package/dist/browser/diagnostics/precision-trace.js +86 -0
- package/dist/browser/dom/attributes.d.ts +5 -0
- package/dist/browser/dom/attributes.js +102 -0
- package/dist/browser/dom/component.d.ts +11 -0
- package/dist/browser/dom/component.js +255 -0
- package/dist/browser/dom/direct-api.d.ts +66 -0
- package/dist/browser/dom/direct-api.js +446 -0
- package/dist/browser/dom/lifecycle.d.ts +9 -0
- package/dist/browser/dom/lifecycle.js +161 -0
- package/dist/browser/dom/registry.d.ts +5 -0
- package/dist/browser/dom/registry.js +16 -0
- package/dist/browser/dom-core.d.ts +51 -249
- package/dist/browser/dom-core.js +51 -2521
- package/dist/browser/navigation/client-navigation.d.ts +44 -0
- package/dist/browser/navigation/client-navigation.js +277 -0
- package/dist/browser/navigation/extract-html.d.ts +1 -0
- package/dist/browser/navigation/extract-html.js +5 -0
- package/dist/browser/navigation/layout-retain.d.ts +3 -0
- package/dist/browser/navigation/layout-retain.js +34 -0
- package/dist/browser/navigation/locale-transition.d.ts +24 -0
- package/dist/browser/navigation/locale-transition.js +323 -0
- package/dist/browser/navigation/scroll-focus.d.ts +14 -0
- package/dist/browser/navigation/scroll-focus.js +87 -0
- package/dist/browser/reactivity/patch-scheduler.d.ts +12 -0
- package/dist/browser/reactivity/patch-scheduler.js +720 -0
- package/dist/browser/reactivity/write-barrier.d.ts +16 -0
- package/dist/browser/reactivity/write-barrier.js +546 -0
- package/dist/browser/resume/adopt.d.ts +1 -0
- package/dist/browser/resume/adopt.js +168 -0
- package/dist/browser/resume/hydrate.d.ts +4 -0
- package/dist/browser/resume/hydrate.js +107 -0
- package/dist/browser/resume/resume.d.ts +3 -0
- package/dist/browser/resume/resume.js +102 -0
- package/dist/faces/dom.browser.d.ts +2 -1
- package/dist/faces/dom.browser.js +2 -1
- package/dist/host/{deployment-registry.d.ts → deployment/deployment-registry.d.ts} +1 -1
- package/dist/host/{deployment-registry.js → deployment/deployment-registry.js} +1 -1
- package/dist/host/list-client-components.d.ts +2 -2
- package/dist/host/list-client-components.js +4 -4
- package/dist/host/{render-host.d.ts → render/render-host.d.ts} +2 -2
- package/dist/host/{render-host.js → render/render-host.js} +1 -1
- package/dist/host/{route-layout-chain.js → render/route-layout-chain.js} +1 -1
- package/dist/host/serve/bootstrap.d.ts +1 -0
- package/dist/host/serve/bootstrap.js +25 -0
- package/dist/host/serve/constants.d.ts +6 -0
- package/dist/host/serve/constants.js +6 -0
- package/dist/host/serve/hmr-dev.d.ts +34 -0
- package/dist/host/serve/hmr-dev.js +294 -0
- package/dist/host/serve/http.d.ts +1 -0
- package/dist/host/serve/http.js +124 -0
- package/dist/host/serve/locale-theme.d.ts +19 -0
- package/dist/host/serve/locale-theme.js +169 -0
- package/dist/host/serve/page-catalog.d.ts +14 -0
- package/dist/host/serve/page-catalog.js +105 -0
- package/dist/host/serve/page-render.d.ts +17 -0
- package/dist/host/serve/page-render.js +419 -0
- package/dist/host/serve/paths.d.ts +3 -0
- package/dist/host/serve/paths.js +74 -0
- package/dist/host/serve/route-catalog.d.ts +6 -0
- package/dist/host/serve/route-catalog.js +108 -0
- package/dist/host/serve/state.d.ts +30 -0
- package/dist/host/serve/state.js +36 -0
- package/dist/host/serve-host.d.ts +0 -17
- package/dist/host/serve-host.mjs +4 -1420
- package/dist/ssr/document.d.ts +2 -0
- package/dist/ssr/document.js +71 -0
- package/dist/ssr/dom-ssr.d.ts +10 -47
- package/dist/ssr/dom-ssr.js +10 -1284
- package/dist/ssr/render.d.ts +2 -0
- package/dist/ssr/render.js +78 -0
- package/dist/ssr/serialize.d.ts +3 -0
- package/dist/ssr/serialize.js +678 -0
- package/package.json +11 -11
- /package/dist/host/{route-layout-chain.d.ts → render/route-layout-chain.d.ts} +0 -0
package/dist/browser/dom-core.js
CHANGED
|
@@ -1,2524 +1,54 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VMZ DOM client runtime ??precise patches, no VDOM diff (SSR lives in dom-ssr).
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Direct components expose `__vmzCreate` / `__vmzSerialize` / `__vmzPlan`.
|
|
6
|
-
* Mount and client patches run that same schedule (SSR/hydrate/resume in dom-ssr).
|
|
7
|
-
* Field writes only run registered dep patches ??never re-create structure.
|
|
8
|
-
*/
|
|
9
|
-
import { mergeClassParts, mergeStyleParts } from '../shared/dom-attr-normalize.js';
|
|
10
|
-
import { applyDirectHostBox } from './direct-host-box.js';
|
|
11
|
-
import { createUnknownComponentElement } from './unknown-component.js';
|
|
1
|
+
/** Browser DOM runtime barrel. */
|
|
12
2
|
export { mergeClassParts, mergeStyleParts } from '../shared/dom-attr-normalize.js';
|
|
13
3
|
export { applyDirectHostBox, INLINE_HOST_CONTENTS, resolveDirectHostBox } from './direct-host-box.js';
|
|
14
4
|
export { createUnknownComponentElement, markUnknownComponentHost, serializeUnknownComponentNode, UNKNOWN_COMPONENT_ERROR, } from './unknown-component.js';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
export
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
export
|
|
65
|
-
precision.writes = 0;
|
|
66
|
-
precision.bindingEvals = 0;
|
|
67
|
-
precision.patchExecs = 0;
|
|
68
|
-
precision.domCreates = 0;
|
|
69
|
-
precision.domMoves = 0;
|
|
70
|
-
precision.domRemoves = 0;
|
|
71
|
-
precision.componentExecs = 0;
|
|
72
|
-
precision.writesByRoot = Object.create(null);
|
|
73
|
-
precision.bindingEvalsByDep = Object.create(null);
|
|
74
|
-
precision.patchesByDep = Object.create(null);
|
|
75
|
-
precision.bindingEvalsByBinding = Object.create(null);
|
|
76
|
-
precision.patchesByBinding = Object.create(null);
|
|
77
|
-
}
|
|
78
|
-
export function __vmzTraceReset() {
|
|
79
|
-
traceBuf.events = [];
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* StableId event snapshot (`vmz.dx.trace.v0` shape without schema stamp ?? * host may wrap via ingestRuntimeTrace).
|
|
83
|
-
*/
|
|
84
|
-
export function __vmzTraceSnapshot() {
|
|
85
|
-
const events = traceBuf.events.map((e) => ({ ...e, stableId: { ...e.stableId } }));
|
|
86
|
-
return {
|
|
87
|
-
schema: 'vmz.dx.trace.v0',
|
|
88
|
-
events,
|
|
89
|
-
status: events.length ? 'ready' : 'empty',
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
export function __vmzPrecisionSnapshot() {
|
|
93
|
-
return {
|
|
94
|
-
enabled: precision.enabled,
|
|
95
|
-
writes: precision.writes,
|
|
96
|
-
bindingEvals: precision.bindingEvals,
|
|
97
|
-
patchExecs: precision.patchExecs,
|
|
98
|
-
domCreates: precision.domCreates,
|
|
99
|
-
domMoves: precision.domMoves,
|
|
100
|
-
domRemoves: precision.domRemoves,
|
|
101
|
-
componentExecs: precision.componentExecs,
|
|
102
|
-
writesByRoot: { ...precision.writesByRoot },
|
|
103
|
-
bindingEvalsByDep: { ...precision.bindingEvalsByDep },
|
|
104
|
-
patchesByDep: { ...precision.patchesByDep },
|
|
105
|
-
bindingEvalsByBinding: { ...precision.bindingEvalsByBinding },
|
|
106
|
-
patchesByBinding: { ...precision.patchesByBinding },
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
function runPatch(inst, fn, depKey = null, bindingId = null) {
|
|
110
|
-
if (precision.enabled) {
|
|
111
|
-
// Specialized trackPatch runs are both patch execs and binding evals (0.2.0).
|
|
112
|
-
precision.patchExecs++;
|
|
113
|
-
precision.bindingEvals++;
|
|
114
|
-
if (depKey) {
|
|
115
|
-
bumpMap(precision.patchesByDep, depKey);
|
|
116
|
-
bumpMap(precision.bindingEvalsByDep, depKey);
|
|
117
|
-
}
|
|
118
|
-
if (bindingId != null) {
|
|
119
|
-
const id = String(bindingId);
|
|
120
|
-
bumpMap(precision.patchesByBinding, id);
|
|
121
|
-
bumpMap(precision.bindingEvalsByBinding, id);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (bindingId != null) {
|
|
125
|
-
pushTrace('patch', 'binding', bindingId, depKey);
|
|
126
|
-
}
|
|
127
|
-
return fn.call(inst);
|
|
128
|
-
}
|
|
129
|
-
export function noteDomCreate() {
|
|
130
|
-
if (precision.enabled)
|
|
131
|
-
precision.domCreates++;
|
|
132
|
-
}
|
|
133
|
-
function noteDomRemove() {
|
|
134
|
-
if (precision.enabled)
|
|
135
|
-
precision.domRemoves++;
|
|
136
|
-
}
|
|
137
|
-
function noteDomMove() {
|
|
138
|
-
if (precision.enabled)
|
|
139
|
-
precision.domMoves++;
|
|
140
|
-
}
|
|
141
|
-
export function registerComponents(map) {
|
|
142
|
-
Object.assign(components, map);
|
|
143
|
-
}
|
|
144
|
-
export function getRegisteredComponent(name) {
|
|
145
|
-
return components[name] || null;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Lazy component loader for EventEntry mixed packs (set by entry-client / entry-event).
|
|
149
|
-
*/
|
|
150
|
-
export async function resolveComponent(name) {
|
|
151
|
-
let Ctor = components[name];
|
|
152
|
-
if (!Ctor && typeof globalThis.__vmzLoadComponent === 'function') {
|
|
153
|
-
Ctor = (await globalThis.__vmzLoadComponent(name));
|
|
154
|
-
if (Ctor)
|
|
155
|
-
registerComponents({ [name]: Ctor });
|
|
156
|
-
}
|
|
157
|
-
return Ctor || null;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Mount once; later updates are dep patches only (never re-run structure).
|
|
161
|
-
* Requires compiler `__vmzCreate` (production Direct emit ??no blueprint fallback).
|
|
162
|
-
*/
|
|
163
|
-
export async function mount(Component, container, props = {}) {
|
|
164
|
-
if (container.__vmzInst) {
|
|
165
|
-
destroy(container.__vmzInst);
|
|
166
|
-
container.__vmzInst = null;
|
|
167
|
-
}
|
|
168
|
-
const inst = createInstance(Component, props);
|
|
169
|
-
inst.__vmzBinders = Object.create(null);
|
|
170
|
-
inst.__vmzBindings = Object.create(null);
|
|
171
|
-
inst.__vmzDepToBindings = Object.create(null);
|
|
172
|
-
container.replaceChildren();
|
|
173
|
-
const node = await createFromComponent(Component, inst);
|
|
174
|
-
if (node) {
|
|
175
|
-
inst.__vmzDomRoot = node;
|
|
176
|
-
container.appendChild(node);
|
|
177
|
-
}
|
|
178
|
-
if (typeof inst.onMount === 'function') {
|
|
179
|
-
await inst.onMount();
|
|
180
|
-
}
|
|
181
|
-
await settlePendingChildMounts(inst);
|
|
182
|
-
container.__vmzInst = inst;
|
|
183
|
-
return inst;
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Nested Direct `component` schedules child onMount asynchronously; drain before return
|
|
187
|
-
* so SSR/hydrate callers see post-mount DOM (e.g. UserCard Ada, not Loading??.
|
|
188
|
-
*/
|
|
189
|
-
export async function settlePendingChildMounts(inst) {
|
|
190
|
-
if (!inst || !Array.isArray(inst.__vmzPendingChildMounts) || !inst.__vmzPendingChildMounts.length)
|
|
191
|
-
return;
|
|
192
|
-
await Promise.all(inst.__vmzPendingChildMounts);
|
|
193
|
-
inst.__vmzPendingChildMounts = [];
|
|
194
|
-
const hosts = [];
|
|
195
|
-
const root = inst.__vmzDomRoot;
|
|
196
|
-
if (root && root.nodeType === 1) {
|
|
197
|
-
if (root.__vmzInst)
|
|
198
|
-
hosts.push(root.__vmzInst);
|
|
199
|
-
for (const el of root.querySelectorAll('[data-vmz]')) {
|
|
200
|
-
if (el.__vmzInst)
|
|
201
|
-
hosts.push(el.__vmzInst);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
for (const child of hosts) {
|
|
205
|
-
await flushPending(child);
|
|
206
|
-
await settlePendingChildMounts(child);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Direct create only (production Direct emit).
|
|
211
|
-
*/
|
|
212
|
-
async function createFromComponent(Component, inst) {
|
|
213
|
-
if (Component && Component.__vmzDirect && typeof Component.__vmzCreate === 'function') {
|
|
214
|
-
return runDirectCreate(Component, inst);
|
|
215
|
-
}
|
|
216
|
-
throw new Error(`vmz:dom mount requires __vmzCreate (Direct); blueprint render() removed (production Direct emit)`);
|
|
217
|
-
}
|
|
218
|
-
export function runDirectCreate(Component, inst) {
|
|
219
|
-
// Nested component creates (e.g. Button inside parent ifBlock branch) must not
|
|
220
|
-
// leak bindAttr/bindText into the parent's `_branchBinds` / `_itemPatches` sink ?? // that steals numeric BindingIds (0) and corrupts parent deps (density ??type).
|
|
221
|
-
const prevInst = directApi._inst;
|
|
222
|
-
const prevBranch = directApi._branchBinds;
|
|
223
|
-
const prevItems = directApi._itemPatches;
|
|
224
|
-
const prevEach = directApi._eachCtx;
|
|
225
|
-
directApi._inst = inst;
|
|
226
|
-
directApi._branchBinds = null;
|
|
227
|
-
directApi._itemPatches = null;
|
|
228
|
-
directApi._eachCtx = null;
|
|
229
|
-
try {
|
|
230
|
-
return Component.__vmzCreate.call(inst, directApi);
|
|
231
|
-
}
|
|
232
|
-
finally {
|
|
233
|
-
directApi._inst = prevInst;
|
|
234
|
-
directApi._branchBinds = prevBranch;
|
|
235
|
-
directApi._itemPatches = prevItems;
|
|
236
|
-
directApi._eachCtx = prevEach;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* First default `<slot>` owned by this component tree ??skip nested `[data-vmz]`
|
|
241
|
-
* hosts (Button/Link/Empty label slots). `querySelector('slot')` steals those and
|
|
242
|
-
* projects parent siblings into the wrong host (commercial Drawer into Create btn).
|
|
243
|
-
*/
|
|
244
|
-
export function findOwnedDefaultSlot(root) {
|
|
245
|
-
if (!root || root.nodeType !== 1)
|
|
246
|
-
return null;
|
|
247
|
-
const tag = String(root.tagName || '').toLowerCase();
|
|
248
|
-
if (tag === 'slot' && !root.getAttribute('name'))
|
|
249
|
-
return root;
|
|
250
|
-
const kids = root.children;
|
|
251
|
-
if (!kids || !kids.length)
|
|
252
|
-
return null;
|
|
253
|
-
for (let i = 0; i < kids.length; i++) {
|
|
254
|
-
const c = kids[i];
|
|
255
|
-
if (c.nodeType !== 1)
|
|
256
|
-
continue;
|
|
257
|
-
if (c.hasAttribute('data-vmz'))
|
|
258
|
-
continue;
|
|
259
|
-
const hit = findOwnedDefaultSlot(c);
|
|
260
|
-
if (hit)
|
|
261
|
-
return hit;
|
|
262
|
-
}
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
/** Button/Link shell when `<slot>` is absent (SSR-flattened or resume adopt). */
|
|
266
|
-
export function findOwnedDefaultSlotTarget(root) {
|
|
267
|
-
const slot = findOwnedDefaultSlot(root);
|
|
268
|
-
if (slot)
|
|
269
|
-
return slot;
|
|
270
|
-
if (!root || root.nodeType !== 1)
|
|
271
|
-
return null;
|
|
272
|
-
const walk = (el) => {
|
|
273
|
-
if (!el || el.nodeType !== 1)
|
|
274
|
-
return null;
|
|
275
|
-
const tag = String(el.tagName || '').toLowerCase();
|
|
276
|
-
if ((tag === 'button' || tag === 'a') && el.getAttribute('data-vmz-ui') === 'button')
|
|
277
|
-
return el;
|
|
278
|
-
if (el.hasAttribute('data-vmz'))
|
|
279
|
-
return null;
|
|
280
|
-
const kids = el.children;
|
|
281
|
-
if (!kids || !kids.length)
|
|
282
|
-
return null;
|
|
283
|
-
for (let i = 0; i < kids.length; i++) {
|
|
284
|
-
const hit = walk(kids[i]);
|
|
285
|
-
if (hit)
|
|
286
|
-
return hit;
|
|
287
|
-
}
|
|
288
|
-
return null;
|
|
289
|
-
};
|
|
290
|
-
return walk(root);
|
|
291
|
-
}
|
|
292
|
-
export const directApi = {
|
|
293
|
-
_inst: null,
|
|
294
|
-
_branchBinds: null,
|
|
295
|
-
_itemPatches: null,
|
|
296
|
-
_eachCtx: null,
|
|
297
|
-
_resumeAdopt: null,
|
|
298
|
-
el(tag) {
|
|
299
|
-
if (directApi._resumeAdopt)
|
|
300
|
-
return directApi._resumeAdopt.el(tag);
|
|
301
|
-
noteDomCreate();
|
|
302
|
-
return document.createElement(tag || 'div');
|
|
303
|
-
},
|
|
304
|
-
elNS(ns, tag) {
|
|
305
|
-
if (directApi._resumeAdopt && typeof directApi._resumeAdopt.elNS === 'function') {
|
|
306
|
-
return directApi._resumeAdopt.elNS(ns, tag);
|
|
307
|
-
}
|
|
308
|
-
noteDomCreate();
|
|
309
|
-
return document.createElementNS(ns || 'http://www.w3.org/2000/svg', tag || 'g');
|
|
310
|
-
},
|
|
311
|
-
text(value) {
|
|
312
|
-
if (directApi._resumeAdopt)
|
|
313
|
-
return directApi._resumeAdopt.text(value);
|
|
314
|
-
noteDomCreate();
|
|
315
|
-
return document.createTextNode(value == null ? '' : String(value));
|
|
316
|
-
},
|
|
317
|
-
adoptEnter(node) {
|
|
318
|
-
const adopt = directApi._resumeAdopt;
|
|
319
|
-
if (adopt && typeof adopt.enter === 'function') {
|
|
320
|
-
const ok = adopt.enter(node);
|
|
321
|
-
if (ok)
|
|
322
|
-
adopt._enterBalance = (adopt._enterBalance || 0) + 1;
|
|
323
|
-
return ok;
|
|
324
|
-
}
|
|
325
|
-
return false;
|
|
326
|
-
},
|
|
327
|
-
adoptLeave() {
|
|
328
|
-
const adopt = directApi._resumeAdopt;
|
|
329
|
-
// Emit always pairs leave after enter; if enter failed (fresh node, no
|
|
330
|
-
// pool), do not pop ??unbalanced leave previously stole parent scopes
|
|
331
|
-
// and projected siblings into nested Button slots.
|
|
332
|
-
if (!adopt || typeof adopt.leave !== 'function')
|
|
333
|
-
return;
|
|
334
|
-
if ((adopt._enterBalance || 0) <= 0)
|
|
335
|
-
return;
|
|
336
|
-
adopt._enterBalance -= 1;
|
|
337
|
-
adopt.leave();
|
|
338
|
-
},
|
|
339
|
-
frag() {
|
|
340
|
-
noteDomCreate();
|
|
341
|
-
return document.createDocumentFragment();
|
|
342
|
-
},
|
|
343
|
-
comment(value) {
|
|
344
|
-
noteDomCreate();
|
|
345
|
-
return document.createComment(value == null ? '' : String(value));
|
|
346
|
-
},
|
|
347
|
-
insertBefore(parent, node, ref) {
|
|
348
|
-
if (!parent || node == null)
|
|
349
|
-
return;
|
|
350
|
-
parent.insertBefore(node, ref);
|
|
351
|
-
},
|
|
352
|
-
removeNode(node) {
|
|
353
|
-
if (!node || !node.parentNode)
|
|
354
|
-
return;
|
|
355
|
-
noteDomRemove();
|
|
356
|
-
node.remove();
|
|
357
|
-
},
|
|
358
|
-
/**
|
|
359
|
-
* Register a generated per-binding patch (0.2.0). No generic get/cf interpreter.
|
|
360
|
-
*/
|
|
361
|
-
trackPatch(inst, deps, patch, bindingId = null) {
|
|
362
|
-
if (typeof patch !== 'function')
|
|
363
|
-
return;
|
|
364
|
-
if (directApi._branchBinds) {
|
|
365
|
-
directApi._branchBinds.push({ deps: deps || [], fn: patch, bindingId });
|
|
366
|
-
try {
|
|
367
|
-
patch.call(inst);
|
|
368
|
-
}
|
|
369
|
-
catch (err) {
|
|
370
|
-
console.error('vmz:dom trackPatch branch', err);
|
|
371
|
-
}
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
if (directApi._itemPatches) {
|
|
375
|
-
patch.__vmzItemLocal = true;
|
|
376
|
-
if (!directApi._itemPatches.includes(patch)) {
|
|
377
|
-
directApi._itemPatches.push(patch);
|
|
378
|
-
}
|
|
379
|
-
if (directApi._eachCtx) {
|
|
380
|
-
directApi._eachCtx.noteItemBind(bindingId, deps || [], patch);
|
|
381
|
-
}
|
|
382
|
-
// Also register on the instance so outer field writes (`selected` in
|
|
383
|
-
// `selected === item.id`) wake item patches — keyed-each only re-runs
|
|
384
|
-
// create/apply on list deps, not host fields referenced inside items.
|
|
385
|
-
registerBind(inst, deps || [], patch, bindingId);
|
|
386
|
-
try {
|
|
387
|
-
patch.call(inst);
|
|
388
|
-
}
|
|
389
|
-
catch (err) {
|
|
390
|
-
console.error('vmz:dom trackPatch item', err);
|
|
391
|
-
}
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
registerBind(inst, deps || [], patch, bindingId);
|
|
395
|
-
try {
|
|
396
|
-
runPatch(inst, patch, (deps && deps[0]) || null, bindingId ?? null);
|
|
397
|
-
}
|
|
398
|
-
catch (err) {
|
|
399
|
-
console.error('vmz:dom trackPatch', err);
|
|
400
|
-
}
|
|
401
|
-
},
|
|
402
|
-
untrackPatch(inst, deps, patch, bindingId = null) {
|
|
403
|
-
if (typeof patch !== 'function')
|
|
404
|
-
return;
|
|
405
|
-
unregisterBind(inst, deps || [], patch, bindingId);
|
|
406
|
-
},
|
|
407
|
-
disposeTree(root) {
|
|
408
|
-
disposeDomTree(root);
|
|
409
|
-
},
|
|
410
|
-
attr(el, name, value) {
|
|
411
|
-
applyDomAttr(el, name, value);
|
|
412
|
-
},
|
|
413
|
-
mergeClass(...parts) {
|
|
414
|
-
return mergeClassParts(...parts);
|
|
415
|
-
},
|
|
416
|
-
mergeStyle(...parts) {
|
|
417
|
-
return mergeStyleParts(...parts);
|
|
418
|
-
},
|
|
419
|
-
on(el, type, handler) {
|
|
420
|
-
const inst = directApi._inst;
|
|
421
|
-
if (directApi._eachCtx && typeof handler === 'function') {
|
|
422
|
-
const bag = el.__vmzEvt || (el.__vmzEvt = Object.create(null));
|
|
423
|
-
const methodHint = inferHandlerMethod(handler);
|
|
424
|
-
const listener = (ev) => {
|
|
425
|
-
if (type === 'submit' && ev && typeof ev.preventDefault === 'function') {
|
|
426
|
-
ev.preventDefault();
|
|
427
|
-
}
|
|
428
|
-
if (methodHint && methodAllowsSyncEventFlush(inst, methodHint)) {
|
|
429
|
-
runDomEventHandler(inst, methodHint, () => {
|
|
430
|
-
const m = inst[methodHint];
|
|
431
|
-
if (typeof m === 'function')
|
|
432
|
-
return m.call(inst, ev);
|
|
433
|
-
return handler.call(inst, ev);
|
|
434
|
-
});
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
runDomEventHandler(inst, methodHint, () => handler.call(inst, ev));
|
|
438
|
-
};
|
|
439
|
-
bag[type] = listener;
|
|
440
|
-
directApi._eachCtx.needDelegate(type);
|
|
441
|
-
el.addEventListener(type, listener);
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
// Infer once at bind time ??never Function#toString on the click hot path.
|
|
445
|
-
const methodHint = typeof handler === 'function' ? inferHandlerMethod(handler) : null;
|
|
446
|
-
if (methodHint && methodAllowsSyncEventFlush(inst, methodHint)) {
|
|
447
|
-
// Direct method bind: skip arrow wrapper + nested handler.call.
|
|
448
|
-
el.addEventListener(type, (ev) => {
|
|
449
|
-
if (type === 'submit' && ev && typeof ev.preventDefault === 'function') {
|
|
450
|
-
ev.preventDefault();
|
|
451
|
-
}
|
|
452
|
-
runDomEventHandler(inst, methodHint, () => {
|
|
453
|
-
const m = inst[methodHint];
|
|
454
|
-
if (typeof m === 'function')
|
|
455
|
-
return m.call(inst, ev);
|
|
456
|
-
return handler.call(inst, ev);
|
|
457
|
-
});
|
|
458
|
-
});
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
el.addEventListener(type, (ev) => {
|
|
462
|
-
// Belt-and-suspenders: form submit must not navigate before handler runs.
|
|
463
|
-
if (type === 'submit' && ev && typeof ev.preventDefault === 'function') {
|
|
464
|
-
ev.preventDefault();
|
|
465
|
-
}
|
|
466
|
-
if (typeof handler === 'function') {
|
|
467
|
-
runDomEventHandler(inst, methodHint, () => handler.call(inst, ev));
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
},
|
|
471
|
-
/**
|
|
472
|
-
* Bind a named instance method (no arrow / Function#toString).
|
|
473
|
-
* Learns `skipFlush` after a sync invocation that schedules no dirty work
|
|
474
|
-
* (stride / transpose self-apply DOM).
|
|
475
|
-
*/
|
|
476
|
-
onMethod(el, type, methodName, opts) {
|
|
477
|
-
// Prefer each-block owner: item create often runs during flush when `_inst` is unset.
|
|
478
|
-
const eachOwner = directApi._eachCtx && directApi._eachCtx.inst;
|
|
479
|
-
const inst = eachOwner || directApi._inst;
|
|
480
|
-
if (!inst)
|
|
481
|
-
return;
|
|
482
|
-
let skipFlush = !!(opts && opts.skipFlush);
|
|
483
|
-
const invoke = (ev) => {
|
|
484
|
-
if (type === 'submit' && ev && typeof ev.preventDefault === 'function') {
|
|
485
|
-
ev.preventDefault();
|
|
486
|
-
}
|
|
487
|
-
const m = inst[methodName];
|
|
488
|
-
if (typeof m !== 'function')
|
|
489
|
-
return;
|
|
490
|
-
if (skipFlush) {
|
|
491
|
-
m.call(inst, ev);
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
beginEventFlush(inst);
|
|
495
|
-
try {
|
|
496
|
-
m.call(inst, ev);
|
|
497
|
-
}
|
|
498
|
-
finally {
|
|
499
|
-
const scheduled = !!inst.__vmzFlushScheduled;
|
|
500
|
-
endEventFlush(inst);
|
|
501
|
-
// Barrier-owned methods (stride/transpose) never schedule ??skip frame next time.
|
|
502
|
-
if (!scheduled && methodAllowsSyncEventFlush(inst, methodName)) {
|
|
503
|
-
skipFlush = true;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
};
|
|
507
|
-
if (directApi._eachCtx) {
|
|
508
|
-
const bag = el.__vmzEvt || (el.__vmzEvt = Object.create(null));
|
|
509
|
-
const listener = (ev) => invoke(ev);
|
|
510
|
-
bag[type] = listener;
|
|
511
|
-
directApi._eachCtx.needDelegate(type);
|
|
512
|
-
el.addEventListener(type, listener);
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
el.addEventListener(type, (ev) => invoke(ev));
|
|
516
|
-
},
|
|
517
|
-
/**
|
|
518
|
-
* Specialized single-field attr bind (0.1.29): compile-time field name, no generic get closure in artifact.
|
|
519
|
-
*/
|
|
520
|
-
specFieldAttr(inst, bindingId, fieldName, el, name) {
|
|
521
|
-
directApi.trackPatch(inst, [fieldName], function specFieldAttrPatch() {
|
|
522
|
-
const raw = this[fieldName];
|
|
523
|
-
if (name === 'class' || name === 'className') {
|
|
524
|
-
const s = mergeClassParts(raw);
|
|
525
|
-
if (s)
|
|
526
|
-
el.setAttribute('class', s);
|
|
527
|
-
else if (el.hasAttribute('class'))
|
|
528
|
-
el.removeAttribute('class');
|
|
529
|
-
}
|
|
530
|
-
else if (name === 'style') {
|
|
531
|
-
const s = mergeStyleParts(raw);
|
|
532
|
-
if (s)
|
|
533
|
-
el.setAttribute('style', s);
|
|
534
|
-
else if (el.hasAttribute('style'))
|
|
535
|
-
el.removeAttribute('style');
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
applyDomAttr(el, name, raw);
|
|
539
|
-
}
|
|
540
|
-
}, bindingId);
|
|
541
|
-
},
|
|
542
|
-
/**
|
|
543
|
-
* Specialized single-field text bind (0.1.29): compile-time field name in generated artifact.
|
|
544
|
-
*/
|
|
545
|
-
specFieldText(inst, bindingId, fieldName, textNode) {
|
|
546
|
-
directApi.trackPatch(inst, [fieldName], function specFieldTextPatch() {
|
|
547
|
-
textNode.textContent = String(this[fieldName] ?? '');
|
|
548
|
-
}, bindingId);
|
|
549
|
-
},
|
|
550
|
-
setHtml(el, value) {
|
|
551
|
-
el.innerHTML = value == null ? '' : String(value);
|
|
552
|
-
},
|
|
553
|
-
bindHtml(inst, bindingId, deps, get, el) {
|
|
554
|
-
directApi.trackPatch(inst, deps || [], function bindHtmlPatch() {
|
|
555
|
-
let raw;
|
|
556
|
-
try {
|
|
557
|
-
raw = get.call(inst);
|
|
558
|
-
}
|
|
559
|
-
catch {
|
|
560
|
-
raw = '';
|
|
561
|
-
}
|
|
562
|
-
el.innerHTML = raw == null ? '' : String(raw);
|
|
563
|
-
}, bindingId);
|
|
564
|
-
},
|
|
565
|
-
/**
|
|
566
|
-
* Nested component (sync Direct child or island schedule).
|
|
567
|
-
*/
|
|
568
|
-
component(hostInst, nameOrCtor, props, client) {
|
|
569
|
-
const name = typeof nameOrCtor === 'function' ? nameOrCtor.__vmzTag || nameOrCtor.name || 'Component' : nameOrCtor;
|
|
570
|
-
let host = null;
|
|
571
|
-
if (directApi._resumeAdopt && typeof directApi._resumeAdopt.componentHost === 'function') {
|
|
572
|
-
host = directApi._resumeAdopt.componentHost(name);
|
|
573
|
-
}
|
|
574
|
-
if (!host) {
|
|
575
|
-
noteDomCreate();
|
|
576
|
-
host = document.createElement('div');
|
|
577
|
-
host.setAttribute('data-vmz', name);
|
|
578
|
-
}
|
|
579
|
-
const resolved = {};
|
|
580
|
-
for (const [k, v] of Object.entries(props || {})) {
|
|
581
|
-
// Function props that already look like `onXxx` stay as handlers.
|
|
582
|
-
// Component `@event` never arrives here (emit uses onComponentEvent).
|
|
583
|
-
const onKey = typeof v === 'function' ? eventPropHandlerName(k) : null;
|
|
584
|
-
if (onKey)
|
|
585
|
-
resolved[onKey] = v;
|
|
586
|
-
else if (typeof v === 'function')
|
|
587
|
-
resolved[k] = v.call(hostInst);
|
|
588
|
-
else
|
|
589
|
-
resolved[k] = v;
|
|
590
|
-
}
|
|
591
|
-
if (client) {
|
|
592
|
-
host.setAttribute('data-vmz-island', name);
|
|
593
|
-
host.setAttribute('data-vmz-client', String(client));
|
|
594
|
-
host.setAttribute('data-vmz-props', JSON.stringify(stripFns(resolved)));
|
|
595
|
-
if (isEventEntryStrategy(String(client))) {
|
|
596
|
-
host.setAttribute('data-vmz-entry', 'event');
|
|
597
|
-
}
|
|
598
|
-
// resume: resume on schedule; EventEntry may lazy-load chunk via __vmzLoadComponent.
|
|
599
|
-
scheduleClientOn(host, String(client), async () => {
|
|
600
|
-
const Ctor = typeof nameOrCtor === 'function' ? nameOrCtor : await resolveComponent(name);
|
|
601
|
-
if (!Ctor) {
|
|
602
|
-
// Replace placeholder island with leaf error node (do not throw page).
|
|
603
|
-
const err = createUnknownComponentElement(name, 'island');
|
|
604
|
-
host.replaceWith(err);
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
const { resume } = await import('../ssr/dom-ssr.js');
|
|
608
|
-
await resume(Ctor, host, { props: resolved, state: {} });
|
|
609
|
-
});
|
|
610
|
-
return host;
|
|
611
|
-
}
|
|
612
|
-
// Static Ctor import path: use Function directly (no registry lookup).
|
|
613
|
-
const Ctor = typeof nameOrCtor === 'function' ? nameOrCtor : components[name];
|
|
614
|
-
if (!Ctor) {
|
|
615
|
-
return createUnknownComponentElement(name, 'client');
|
|
616
|
-
}
|
|
617
|
-
// Inline chips ??`display: contents` (`ui-direct-host-box`). Block surfaces
|
|
618
|
-
// keep a real box (DataTable select timed out when defaulting everything).
|
|
619
|
-
applyDirectHostBox(host, name, Ctor);
|
|
620
|
-
const child = createInstance(Ctor, resolved);
|
|
621
|
-
if (!(Ctor.__vmzDirect && typeof Ctor.__vmzCreate === 'function')) {
|
|
622
|
-
throw new Error(`vmz:dom direct component <${name}> requires __vmzCreate (rebuild child with Direct)`);
|
|
623
|
-
}
|
|
624
|
-
// Nested resume: keep parent `_resumeAdopt` so child reclaim parked SSR nodes.
|
|
625
|
-
// Enter the host's private child pool so nested create cannot see uncle siblings.
|
|
626
|
-
const adopt = directApi._resumeAdopt;
|
|
627
|
-
const entered = adopt && typeof adopt.enter === 'function' ? adopt.enter(host) : false;
|
|
628
|
-
let node;
|
|
629
|
-
try {
|
|
630
|
-
node = runDirectCreate(Ctor, child);
|
|
631
|
-
}
|
|
632
|
-
finally {
|
|
633
|
-
if (entered && adopt && typeof adopt.leave === 'function')
|
|
634
|
-
adopt.leave();
|
|
635
|
-
}
|
|
636
|
-
if (node) {
|
|
637
|
-
// ifBlock/eachBlock roots are DocumentFragments: appendChild moves
|
|
638
|
-
// children into `host` and empties the fragment. Keep a live Element
|
|
639
|
-
// root so projectDefaultSlot can find `<slot>` (Button v-if/v-else).
|
|
640
|
-
child.__vmzDomRoot = node.nodeType === 11 ? host : node;
|
|
641
|
-
host.appendChild(node);
|
|
642
|
-
}
|
|
643
|
-
host.__vmzInst = child;
|
|
644
|
-
if (typeof child.onMount === 'function') {
|
|
645
|
-
const pending = Promise.resolve().then(() => {
|
|
646
|
-
if (!child.__vmzDestroyed)
|
|
647
|
-
return child.onMount();
|
|
648
|
-
});
|
|
649
|
-
const bag = hostInst.__vmzPendingChildMounts || (hostInst.__vmzPendingChildMounts = []);
|
|
650
|
-
bag.push(pending);
|
|
651
|
-
}
|
|
652
|
-
return host;
|
|
653
|
-
},
|
|
654
|
-
/**
|
|
655
|
-
* Subscribe to a child component event (`@submit` ??event name `submit`).
|
|
656
|
-
* Orthogonal to function props (`:on-submit` ??prop `onSubmit`).
|
|
657
|
-
*/
|
|
658
|
-
onComponentEvent(hostEl, eventName, handler) {
|
|
659
|
-
const child = hostEl && hostEl.__vmzInst;
|
|
660
|
-
if (!child || typeof eventName !== 'string' || !eventName)
|
|
661
|
-
return;
|
|
662
|
-
if (typeof handler !== 'function')
|
|
663
|
-
return;
|
|
664
|
-
const bag = child.__vmzComponentListeners || (child.__vmzComponentListeners = Object.create(null));
|
|
665
|
-
const list = bag[eventName] || (bag[eventName] = []);
|
|
666
|
-
list.push(handler);
|
|
667
|
-
},
|
|
668
|
-
/**
|
|
669
|
-
* Keep nested Direct child props live with parent field writes.
|
|
670
|
-
*/
|
|
671
|
-
bindComponentProp(hostInst, hostEl, propName, deps, get) {
|
|
672
|
-
if (hostEl && hostEl.__vmzPropBindSeq == null) {
|
|
673
|
-
hostEl.__vmzPropBindSeq = ++directPropBindSeq;
|
|
674
|
-
}
|
|
675
|
-
const seq = hostEl && hostEl.__vmzPropBindSeq != null ? hostEl.__vmzPropBindSeq : ++directPropBindSeq;
|
|
676
|
-
const bindingId = `pc:${seq}:${propName}`;
|
|
677
|
-
directApi.trackPatch(hostInst, deps || [], function bindComponentPropPatch() {
|
|
678
|
-
let raw;
|
|
679
|
-
try {
|
|
680
|
-
raw = get.call(hostInst);
|
|
681
|
-
}
|
|
682
|
-
catch {
|
|
683
|
-
raw = null;
|
|
684
|
-
}
|
|
685
|
-
const child = hostEl && hostEl.__vmzInst;
|
|
686
|
-
if (!child || child.__vmzDestroyed)
|
|
687
|
-
return;
|
|
688
|
-
if (typeof propName !== 'string' || !propName || propName.startsWith('#'))
|
|
689
|
-
return;
|
|
690
|
-
child[propName] = raw;
|
|
691
|
-
if (typeof child.__vmzOnParentProp === 'function') {
|
|
692
|
-
try {
|
|
693
|
-
child.__vmzOnParentProp(propName, raw);
|
|
694
|
-
}
|
|
695
|
-
catch (err) {
|
|
696
|
-
console.error('vmz:dom __vmzOnParentProp', err);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
scheduleRefresh(child, { type: 'replace', root: propName });
|
|
700
|
-
}, bindingId);
|
|
701
|
-
},
|
|
702
|
-
/**
|
|
703
|
-
* Project parent children into nested Direct component default `<slot>`.
|
|
704
|
-
* Uses {@link findOwnedDefaultSlot} so nested Button/Link slots are not stolen.
|
|
705
|
-
*/
|
|
706
|
-
projectDefaultSlot(hostEl, node) {
|
|
707
|
-
if (!hostEl || node == null)
|
|
708
|
-
return;
|
|
709
|
-
const child = hostEl.__vmzInst;
|
|
710
|
-
let root = (child && child.__vmzDomRoot) || hostEl;
|
|
711
|
-
// Emptied DocumentFragment after append must not receive slot kids.
|
|
712
|
-
if (!root || root.nodeType !== 1)
|
|
713
|
-
root = hostEl;
|
|
714
|
-
const target = root && root.nodeType === 1 ? findOwnedDefaultSlotTarget(root) : null;
|
|
715
|
-
if (target && target.parentNode) {
|
|
716
|
-
if (String(target.tagName || '').toLowerCase() === 'slot') {
|
|
717
|
-
target.replaceWith(node);
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
target.appendChild(node);
|
|
721
|
-
return;
|
|
722
|
-
}
|
|
723
|
-
if (root && root.nodeType === 1 && typeof root.appendChild === 'function')
|
|
724
|
-
root.appendChild(node);
|
|
725
|
-
else
|
|
726
|
-
hostEl.appendChild(node);
|
|
727
|
-
},
|
|
728
|
-
};
|
|
729
|
-
/**
|
|
730
|
-
* Function prop wire: only `onXxx` camelCase is a handler prop.
|
|
731
|
-
* Component `@event` must never normalize here ??that channel is `onComponentEvent`.
|
|
732
|
-
*/
|
|
733
|
-
export function eventPropHandlerName(name) {
|
|
734
|
-
if (typeof name !== 'string' || !name)
|
|
735
|
-
return null;
|
|
736
|
-
if (/^on[A-Z]/.test(name))
|
|
737
|
-
return name;
|
|
738
|
-
return null;
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Dispatch a component event to parent subscribers registered via `onComponentEvent`.
|
|
742
|
-
* Installed on every Direct instance as `inst.emit`.
|
|
743
|
-
*/
|
|
744
|
-
export function emitComponentEvent(inst, eventName, ...payload) {
|
|
745
|
-
if (!inst || typeof eventName !== 'string' || !eventName)
|
|
746
|
-
return;
|
|
747
|
-
const bag = inst.__vmzComponentListeners;
|
|
748
|
-
const list = bag && bag[eventName];
|
|
749
|
-
if (!Array.isArray(list) || list.length === 0)
|
|
750
|
-
return;
|
|
751
|
-
for (const fn of list) {
|
|
752
|
-
if (typeof fn === 'function')
|
|
753
|
-
fn(...payload);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
export function isEventPropName(name) {
|
|
757
|
-
return eventPropHandlerName(name) != null;
|
|
758
|
-
}
|
|
759
|
-
let directPropBindSeq = 0;
|
|
760
|
-
export const BOOLEAN_HTML_ATTRS = new Set([
|
|
761
|
-
'disabled',
|
|
762
|
-
'checked',
|
|
763
|
-
'selected',
|
|
764
|
-
'readonly',
|
|
765
|
-
'required',
|
|
766
|
-
'multiple',
|
|
767
|
-
'hidden',
|
|
768
|
-
'autofocus',
|
|
769
|
-
'autoplay',
|
|
770
|
-
'controls',
|
|
771
|
-
'loop',
|
|
772
|
-
'muted',
|
|
773
|
-
'open',
|
|
774
|
-
'novalidate',
|
|
775
|
-
'formnovalidate',
|
|
776
|
-
'defer',
|
|
777
|
-
'async',
|
|
778
|
-
'ismap',
|
|
779
|
-
'default',
|
|
780
|
-
'inert',
|
|
781
|
-
]);
|
|
782
|
-
export function applyDomAttr(el, name, value) {
|
|
783
|
-
const key = name === 'className' ? 'class' : name;
|
|
784
|
-
if (key === 'class') {
|
|
785
|
-
const s = mergeClassParts(value);
|
|
786
|
-
if (s)
|
|
787
|
-
el.setAttribute('class', s);
|
|
788
|
-
else
|
|
789
|
-
el.removeAttribute('class');
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
if (key === 'style') {
|
|
793
|
-
const s = mergeStyleParts(value);
|
|
794
|
-
if (s)
|
|
795
|
-
el.setAttribute('style', s);
|
|
796
|
-
else
|
|
797
|
-
el.removeAttribute('style');
|
|
798
|
-
return;
|
|
799
|
-
}
|
|
800
|
-
if (BOOLEAN_HTML_ATTRS.has(String(key).toLowerCase())) {
|
|
801
|
-
const on = value === true || value === '';
|
|
802
|
-
if (key === 'checked' && el && el.tagName === 'INPUT' && String(el.type || '').toLowerCase() === 'checkbox') {
|
|
803
|
-
if (el.checked !== on)
|
|
804
|
-
el.checked = on;
|
|
805
|
-
}
|
|
806
|
-
if (value === false || value == null || value === '') {
|
|
807
|
-
el.removeAttribute(key);
|
|
808
|
-
}
|
|
809
|
-
else {
|
|
810
|
-
el.setAttribute(key, value === true ? '' : String(value));
|
|
811
|
-
}
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
814
|
-
// <textarea value="??> as an attribute does not update visible text; INPUT/SELECT
|
|
815
|
-
// also need the IDL `.value` property so controlled updates stay in sync after switches.
|
|
816
|
-
// linkedom `<select>.value` is getter-only ??sync via `option.selected` instead of throwing.
|
|
817
|
-
if (key === 'value' && el && (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT' || el.tagName === 'SELECT')) {
|
|
818
|
-
const next = value == null || value === false ? '' : String(value);
|
|
819
|
-
if (el.tagName === 'SELECT') {
|
|
820
|
-
const opts = el.options || el.querySelectorAll?.('option') || [];
|
|
821
|
-
for (const opt of opts) {
|
|
822
|
-
opt.selected = String(opt.value ?? '') === next;
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
else if (el.value !== next) {
|
|
826
|
-
el.value = next;
|
|
827
|
-
}
|
|
828
|
-
if (value == null || value === false)
|
|
829
|
-
el.removeAttribute('value');
|
|
830
|
-
else
|
|
831
|
-
el.setAttribute('value', next);
|
|
832
|
-
return;
|
|
833
|
-
}
|
|
834
|
-
if (value == null || value === false)
|
|
835
|
-
el.removeAttribute(key);
|
|
836
|
-
else
|
|
837
|
-
el.setAttribute(key, value === true ? '' : String(value));
|
|
838
|
-
}
|
|
839
|
-
export function stripFns(obj) {
|
|
840
|
-
const out = {};
|
|
841
|
-
for (const [k, v] of Object.entries(obj || {})) {
|
|
842
|
-
if (typeof v === 'function')
|
|
843
|
-
continue;
|
|
844
|
-
out[k] = v;
|
|
845
|
-
}
|
|
846
|
-
return out;
|
|
847
|
-
}
|
|
848
|
-
/**
|
|
849
|
-
* Marker range host for Direct keyed-each IIFE (insert before end comment).
|
|
850
|
-
*/
|
|
851
|
-
function eachHostApi(start, end) {
|
|
852
|
-
return {
|
|
853
|
-
insert(dom) {
|
|
854
|
-
if (dom.parentNode)
|
|
855
|
-
noteDomMove();
|
|
856
|
-
end.parentNode.insertBefore(dom, end);
|
|
857
|
-
},
|
|
858
|
-
childrenBetween() {
|
|
859
|
-
const out = [];
|
|
860
|
-
let n = start.nextSibling;
|
|
861
|
-
while (n && n !== end) {
|
|
862
|
-
if (n.nodeType === 1)
|
|
863
|
-
out.push(n);
|
|
864
|
-
n = n.nextSibling;
|
|
865
|
-
}
|
|
866
|
-
return out;
|
|
867
|
-
},
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
/**
|
|
871
|
-
* Snapshot plain state/prop field values for Island HMR (session).
|
|
872
|
-
*/
|
|
873
|
-
export function snapshotInstanceState(inst) {
|
|
874
|
-
if (!inst || inst.__vmzDestroyed)
|
|
875
|
-
return null;
|
|
876
|
-
const Ctor = inst.constructor;
|
|
877
|
-
const keys = [...(Ctor.__vmzState || []), ...(Ctor.__vmzProps || [])];
|
|
878
|
-
const out = {};
|
|
879
|
-
for (const key of keys) {
|
|
880
|
-
if (!key || String(key).startsWith('__'))
|
|
881
|
-
continue;
|
|
882
|
-
try {
|
|
883
|
-
out[key] = inst[key];
|
|
884
|
-
}
|
|
885
|
-
catch {
|
|
886
|
-
/* ignore accessors that throw */
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
return out;
|
|
890
|
-
}
|
|
891
|
-
export function applyPreservedState(inst, state) {
|
|
892
|
-
if (!inst || !state)
|
|
893
|
-
return;
|
|
894
|
-
for (const [key, value] of Object.entries(state)) {
|
|
895
|
-
try {
|
|
896
|
-
inst[key] = value;
|
|
897
|
-
}
|
|
898
|
-
catch {
|
|
899
|
-
/* ignore */
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
/**
|
|
904
|
-
* Tear down binders and stop patches. Safe to call more than once.
|
|
905
|
-
* Field writes after destroy no longer update DOM (values may still change).
|
|
906
|
-
*: also dispose owned DOM trees (child __vmzInst / region __vmzDispose).
|
|
907
|
-
*/
|
|
908
|
-
export function destroy(inst) {
|
|
909
|
-
if (!inst || inst.__vmzDestroyed)
|
|
910
|
-
return;
|
|
911
|
-
inst.__vmzDestroyed = true;
|
|
912
|
-
inst.__vmzFlushScheduled = false;
|
|
913
|
-
// async cancel: abort in-flight tasks before tearing down DOM.
|
|
914
|
-
__vmzCancelTasks(inst);
|
|
915
|
-
if (inst.__vmzDomRoot) {
|
|
916
|
-
disposeDomTree(inst.__vmzDomRoot);
|
|
917
|
-
inst.__vmzDomRoot = null;
|
|
918
|
-
}
|
|
919
|
-
if (inst.__vmzDirtyNotices)
|
|
920
|
-
inst.__vmzDirtyNotices.length = 0;
|
|
921
|
-
if (inst.__vmzDirtyTrie)
|
|
922
|
-
inst.__vmzDirtyTrie = Object.create(null);
|
|
923
|
-
if (inst.__vmzDirty)
|
|
924
|
-
inst.__vmzDirty.clear();
|
|
925
|
-
inst.__vmzBinders = Object.create(null);
|
|
926
|
-
inst.__vmzBindings = Object.create(null);
|
|
927
|
-
inst.__vmzDepToBindings = Object.create(null);
|
|
928
|
-
if (typeof inst.onDestroy === 'function') {
|
|
929
|
-
try {
|
|
930
|
-
inst.onDestroy();
|
|
931
|
-
}
|
|
932
|
-
catch (err) {
|
|
933
|
-
console.error('vmz:dom onDestroy', err);
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
/**
|
|
938
|
-
*: walk a DOM subtree and run lifetime dispose hooks + nested instance destroy.
|
|
939
|
-
* Does not mark the *calling* parent destroyed; safe from destroy(inst).
|
|
940
|
-
*/
|
|
941
|
-
export function disposeDomTree(root) {
|
|
942
|
-
if (!root)
|
|
943
|
-
return;
|
|
944
|
-
const seen = new Set();
|
|
945
|
-
const visit = (node) => {
|
|
946
|
-
if (!node || seen.has(node))
|
|
947
|
-
return;
|
|
948
|
-
seen.add(node);
|
|
949
|
-
if (typeof node.__vmzDispose === 'function') {
|
|
950
|
-
try {
|
|
951
|
-
node.__vmzDispose();
|
|
952
|
-
}
|
|
953
|
-
catch (err) {
|
|
954
|
-
console.error('vmz:dom __vmzDispose', err);
|
|
955
|
-
}
|
|
956
|
-
node.__vmzDispose = null;
|
|
957
|
-
}
|
|
958
|
-
if (node.__vmzInst) {
|
|
959
|
-
const child = node.__vmzInst;
|
|
960
|
-
node.__vmzInst = null;
|
|
961
|
-
destroy(child);
|
|
962
|
-
}
|
|
963
|
-
let child = node.firstChild;
|
|
964
|
-
while (child) {
|
|
965
|
-
const next = child.nextSibling;
|
|
966
|
-
visit(child);
|
|
967
|
-
child = next;
|
|
968
|
-
}
|
|
969
|
-
};
|
|
970
|
-
visit(root);
|
|
971
|
-
}
|
|
972
|
-
export function scheduleClient(strategy, fn) {
|
|
973
|
-
scheduleClientOn(null, strategy, fn);
|
|
974
|
-
}
|
|
975
|
-
export function isEventEntryStrategy(strategy) {
|
|
976
|
-
const s = String(strategy || '');
|
|
977
|
-
return s === 'event' || s.startsWith('event:') || s === 'click';
|
|
978
|
-
}
|
|
979
|
-
function eventEntryType(strategy) {
|
|
980
|
-
const s = String(strategy || 'event');
|
|
981
|
-
if (s.startsWith('event:'))
|
|
982
|
-
return s.slice(6) || 'click';
|
|
983
|
-
if (s === 'click')
|
|
984
|
-
return 'click';
|
|
985
|
-
return 'click';
|
|
986
|
-
}
|
|
987
|
-
export function scheduleClientOn(el, strategy, fn) {
|
|
988
|
-
const run = () => {
|
|
989
|
-
Promise.resolve(fn()).catch((err) => console.error('vmz:dom island', err));
|
|
990
|
-
};
|
|
991
|
-
if (isEventEntryStrategy(strategy)) {
|
|
992
|
-
if (!el || typeof el.addEventListener !== 'function') {
|
|
993
|
-
run();
|
|
994
|
-
return;
|
|
995
|
-
}
|
|
996
|
-
const type = eventEntryType(strategy);
|
|
997
|
-
const once = () => {
|
|
998
|
-
el.removeEventListener(type, once);
|
|
999
|
-
run();
|
|
1000
|
-
};
|
|
1001
|
-
el.addEventListener(type, once);
|
|
1002
|
-
return;
|
|
1003
|
-
}
|
|
1004
|
-
if (strategy === 'idle') {
|
|
1005
|
-
if (typeof requestIdleCallback === 'function') {
|
|
1006
|
-
requestIdleCallback(() => run(), { timeout: 2000 });
|
|
1007
|
-
}
|
|
1008
|
-
else {
|
|
1009
|
-
setTimeout(run, 1);
|
|
1010
|
-
}
|
|
1011
|
-
return;
|
|
1012
|
-
}
|
|
1013
|
-
if (strategy === 'visible' && el && typeof IntersectionObserver === 'function') {
|
|
1014
|
-
const io = new IntersectionObserver((entries) => {
|
|
1015
|
-
if (entries.some((e) => e.isIntersecting)) {
|
|
1016
|
-
io.disconnect();
|
|
1017
|
-
run();
|
|
1018
|
-
}
|
|
1019
|
-
});
|
|
1020
|
-
io.observe(el);
|
|
1021
|
-
return;
|
|
1022
|
-
}
|
|
1023
|
-
run();
|
|
1024
|
-
}
|
|
1025
|
-
/**
|
|
1026
|
-
* AsyncTask cancel protocol (first slice): keyed generation + AbortSignal.
|
|
1027
|
-
* Superseded runs and `destroy(inst)` abort prior work; stale results must not apply.
|
|
1028
|
-
*/
|
|
1029
|
-
export function __vmzRunTask(inst, key, fn) {
|
|
1030
|
-
if (!inst)
|
|
1031
|
-
throw new Error('vmz:dom __vmzRunTask requires inst');
|
|
1032
|
-
const k = String(key || 'default');
|
|
1033
|
-
if (!inst.__vmzTasks)
|
|
1034
|
-
inst.__vmzTasks = Object.create(null);
|
|
1035
|
-
const prev = inst.__vmzTasks[k];
|
|
1036
|
-
if (prev) {
|
|
1037
|
-
prev.generation += 1;
|
|
1038
|
-
try {
|
|
1039
|
-
prev.controller.abort();
|
|
1040
|
-
}
|
|
1041
|
-
catch {
|
|
1042
|
-
/* ignore */
|
|
1043
|
-
}
|
|
1044
|
-
prev.status = 'cancelled';
|
|
1045
|
-
}
|
|
1046
|
-
const controller = typeof AbortController !== 'undefined'
|
|
1047
|
-
? new AbortController()
|
|
1048
|
-
: {
|
|
1049
|
-
signal: { aborted: false },
|
|
1050
|
-
abort() {
|
|
1051
|
-
this.signal.aborted = true;
|
|
1052
|
-
},
|
|
1053
|
-
};
|
|
1054
|
-
const generation = (prev?.generation || 0) + 1;
|
|
1055
|
-
const entry = {
|
|
1056
|
-
generation,
|
|
1057
|
-
controller,
|
|
1058
|
-
status: 'pending',
|
|
1059
|
-
result: undefined,
|
|
1060
|
-
error: undefined,
|
|
1061
|
-
promise: undefined,
|
|
1062
|
-
};
|
|
1063
|
-
inst.__vmzTasks[k] = entry;
|
|
1064
|
-
// Invoke synchronously so event handlers can call preventDefault before
|
|
1065
|
-
// the browser continues the default action (form submit ??native navigation).
|
|
1066
|
-
// Async work still continues via the returned Promise.
|
|
1067
|
-
let syncResult;
|
|
1068
|
-
let syncErr;
|
|
1069
|
-
let threw = false;
|
|
1070
|
-
try {
|
|
1071
|
-
syncResult = fn(controller.signal, { generation });
|
|
1072
|
-
}
|
|
1073
|
-
catch (err) {
|
|
1074
|
-
threw = true;
|
|
1075
|
-
syncErr = err;
|
|
1076
|
-
}
|
|
1077
|
-
const settleOk = (result) => {
|
|
1078
|
-
if (inst.__vmzDestroyed || controller.signal.aborted || inst.__vmzTasks[k] !== entry) {
|
|
1079
|
-
entry.status = 'cancelled';
|
|
1080
|
-
return undefined;
|
|
1081
|
-
}
|
|
1082
|
-
entry.status = 'success';
|
|
1083
|
-
entry.result = result;
|
|
1084
|
-
return result;
|
|
1085
|
-
};
|
|
1086
|
-
const settleErr = (err) => {
|
|
1087
|
-
if (inst.__vmzDestroyed || controller.signal.aborted || inst.__vmzTasks[k] !== entry) {
|
|
1088
|
-
entry.status = 'cancelled';
|
|
1089
|
-
return undefined;
|
|
1090
|
-
}
|
|
1091
|
-
entry.status = 'error';
|
|
1092
|
-
entry.error = err;
|
|
1093
|
-
throw err;
|
|
1094
|
-
};
|
|
1095
|
-
if (threw) {
|
|
1096
|
-
const promise = Promise.resolve().then(() => settleErr(syncErr));
|
|
1097
|
-
entry.promise = promise;
|
|
1098
|
-
return promise;
|
|
1099
|
-
}
|
|
1100
|
-
const promise = Promise.resolve(syncResult).then(settleOk, settleErr);
|
|
1101
|
-
entry.promise = promise;
|
|
1102
|
-
return promise;
|
|
1103
|
-
}
|
|
1104
|
-
export function __vmzCancelTasks(inst) {
|
|
1105
|
-
const tasks = inst?.__vmzTasks;
|
|
1106
|
-
if (!tasks)
|
|
1107
|
-
return;
|
|
1108
|
-
for (const key of Object.keys(tasks)) {
|
|
1109
|
-
const t = tasks[key];
|
|
1110
|
-
t.generation += 1;
|
|
1111
|
-
try {
|
|
1112
|
-
t.controller.abort();
|
|
1113
|
-
}
|
|
1114
|
-
catch {
|
|
1115
|
-
/* ignore */
|
|
1116
|
-
}
|
|
1117
|
-
t.status = 'cancelled';
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
export function __vmzTaskStatus(inst, key) {
|
|
1121
|
-
const t = inst?.__vmzTasks?.[String(key || 'default')];
|
|
1122
|
-
return t ? t.status : null;
|
|
1123
|
-
}
|
|
1124
|
-
export function createInstance(Component, props = {}) {
|
|
1125
|
-
if (precision.enabled)
|
|
1126
|
-
precision.componentExecs++;
|
|
1127
|
-
const inst = new Component(props || {});
|
|
1128
|
-
if (typeof inst.__vmzApplyProps === 'function' && !Component.__vmzCtorAppliesProps) {
|
|
1129
|
-
inst.__vmzApplyProps(props || {});
|
|
1130
|
-
}
|
|
1131
|
-
inst.__vmzBinders = Object.create(null);
|
|
1132
|
-
inst.__vmzBindings = Object.create(null);
|
|
1133
|
-
inst.__vmzDepToBindings = Object.create(null);
|
|
1134
|
-
inst.__vmzComponentListeners = Object.create(null);
|
|
1135
|
-
// Compiler intrinsic surface: `this.emit('submit', payload)` ??not a string bus.
|
|
1136
|
-
inst.emit = function emit(eventName, ...payload) {
|
|
1137
|
-
return emitComponentEvent(inst, eventName, ...payload);
|
|
1138
|
-
};
|
|
1139
|
-
makeReactive(inst, Component.__vmzState || []);
|
|
1140
|
-
makeReactive(inst, Component.__vmzProps || []);
|
|
1141
|
-
// WriteBarrier: install Component helpers once (no import needed in emitted code).
|
|
1142
|
-
if (!Component.__vmzWBInstalled) {
|
|
1143
|
-
Component.__vmzWBInstalled = true;
|
|
1144
|
-
Component.__vmzWritePath = __vmzWritePath;
|
|
1145
|
-
Component.__vmzWritePathItem = __vmzWritePathItem;
|
|
1146
|
-
Component.__vmzWritePathCompound = __vmzWritePathCompound;
|
|
1147
|
-
Component.__vmzWritePathCompoundItem = __vmzWritePathCompoundItem;
|
|
1148
|
-
Component.__vmzWritePathLogical = __vmzWritePathLogical;
|
|
1149
|
-
Component.__vmzReadPath = __vmzReadPath;
|
|
1150
|
-
Component.__vmzArrayMutate = __vmzArrayMutate;
|
|
1151
|
-
Component.__vmzArrayItemCompoundStride = __vmzArrayItemCompoundStride;
|
|
1152
|
-
Component.__vmzListTranspose = __vmzListTranspose;
|
|
1153
|
-
Component.__vmzAllowShared = __vmzAllowShared;
|
|
1154
|
-
Component.__vmzTakeShared = __vmzTakeShared;
|
|
1155
|
-
}
|
|
1156
|
-
return inst;
|
|
1157
|
-
}
|
|
1158
|
-
const wbSharedOwners = new WeakMap();
|
|
1159
|
-
const wbAllowShared = new WeakSet();
|
|
1160
|
-
const wbCrossComponentDiags = [];
|
|
1161
|
-
/**
|
|
1162
|
-
* Mark a plain object as intentionally shared across ownership boundaries.
|
|
1163
|
-
*/
|
|
1164
|
-
export function __vmzAllowShared(value) {
|
|
1165
|
-
if (value != null && typeof value === 'object')
|
|
1166
|
-
wbAllowShared.add(value);
|
|
1167
|
-
return value;
|
|
1168
|
-
}
|
|
1169
|
-
/**
|
|
1170
|
-
* Take exclusive ownership intent: clear multi-owner registry for this object.
|
|
1171
|
-
* Subsequent field assigns re-register from the assigning instance only.
|
|
1172
|
-
*/
|
|
1173
|
-
export function __vmzTakeShared(value) {
|
|
1174
|
-
if (value != null && typeof value === 'object') {
|
|
1175
|
-
wbSharedOwners.delete(value);
|
|
1176
|
-
wbAllowShared.delete(value);
|
|
1177
|
-
}
|
|
1178
|
-
return value;
|
|
1179
|
-
}
|
|
1180
|
-
export function __vmzSharedCrossComponentDiagnostics() {
|
|
1181
|
-
return wbCrossComponentDiags.slice();
|
|
1182
|
-
}
|
|
1183
|
-
export function __vmzSharedCrossComponentDiagnosticsReset() {
|
|
1184
|
-
wbCrossComponentDiags.length = 0;
|
|
1185
|
-
}
|
|
1186
|
-
function registerWbOwner(value, report, baseSegs = [], inst = null) {
|
|
1187
|
-
if (value == null || typeof value !== 'object')
|
|
1188
|
-
return;
|
|
1189
|
-
let entry = wbSharedOwners.get(value);
|
|
1190
|
-
if (!entry) {
|
|
1191
|
-
entry = { owners: [] };
|
|
1192
|
-
wbSharedOwners.set(value, entry);
|
|
1193
|
-
}
|
|
1194
|
-
if (entry.owners.some((o) => o.report === report && sameSegs(o.baseSegs, baseSegs))) {
|
|
1195
|
-
return;
|
|
1196
|
-
}
|
|
1197
|
-
entry.owners.push({ report, baseSegs: baseSegs.slice(), inst });
|
|
1198
|
-
// Cross-component share without explicit allow ??diagnose (13 ).
|
|
1199
|
-
if (!wbAllowShared.has(value) && inst) {
|
|
1200
|
-
const other = entry.owners.find((o) => o.inst && o.inst !== inst);
|
|
1201
|
-
if (other) {
|
|
1202
|
-
const msg = 'vmz: shared plain object written from multiple components without allowShared';
|
|
1203
|
-
if (!wbCrossComponentDiags.some((d) => d.message === msg)) {
|
|
1204
|
-
wbCrossComponentDiags.push({ kind: 'shared_cross_component', message: msg });
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
/**
|
|
1210
|
-
* Notify all registered owners of a shared plain object after a barrier write.
|
|
1211
|
-
*/
|
|
1212
|
-
function notifyWbShared(rootObj, localSegs) {
|
|
1213
|
-
const entry = rootObj && typeof rootObj === 'object' ? wbSharedOwners.get(rootObj) : null;
|
|
1214
|
-
if (!entry || !entry.owners.length)
|
|
1215
|
-
return false;
|
|
1216
|
-
for (const o of entry.owners) {
|
|
1217
|
-
if (localSegs == null) {
|
|
1218
|
-
o.report(o.baseSegs.length ? o.baseSegs.slice() : null);
|
|
1219
|
-
}
|
|
1220
|
-
else {
|
|
1221
|
-
o.report([...o.baseSegs, ...localSegs]);
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
return true;
|
|
1225
|
-
}
|
|
1226
|
-
/**
|
|
1227
|
-
* Read a nested path under a field root (for compound / update expansion).
|
|
1228
|
-
*/
|
|
1229
|
-
export function __vmzReadPath(inst, root, segs) {
|
|
1230
|
-
if (!inst || !root)
|
|
1231
|
-
return undefined;
|
|
1232
|
-
let obj = inst[root];
|
|
1233
|
-
if (!Array.isArray(segs) || segs.length === 0)
|
|
1234
|
-
return obj;
|
|
1235
|
-
for (let i = 0; i < segs.length; i++) {
|
|
1236
|
-
if (obj == null || typeof obj !== 'object')
|
|
1237
|
-
return undefined;
|
|
1238
|
-
obj = obj[segs[i]];
|
|
1239
|
-
}
|
|
1240
|
-
return obj;
|
|
1241
|
-
}
|
|
1242
|
-
/**
|
|
1243
|
-
* Short-circuit logical path assign (`||=` / `&&=` / `??=`).
|
|
1244
|
-
*/
|
|
1245
|
-
export function __vmzWritePathLogical(inst, root, segs, kind, rhs) {
|
|
1246
|
-
const cur = __vmzReadPath(inst, root, segs);
|
|
1247
|
-
if (kind === '||') {
|
|
1248
|
-
if (cur)
|
|
1249
|
-
return cur;
|
|
1250
|
-
}
|
|
1251
|
-
else if (kind === '&&') {
|
|
1252
|
-
if (!cur)
|
|
1253
|
-
return cur;
|
|
1254
|
-
}
|
|
1255
|
-
else if (kind === '??') {
|
|
1256
|
-
if (cur != null)
|
|
1257
|
-
return cur;
|
|
1258
|
-
}
|
|
1259
|
-
else {
|
|
1260
|
-
return cur;
|
|
1261
|
-
}
|
|
1262
|
-
return __vmzWritePath(inst, root, segs, rhs);
|
|
1263
|
-
}
|
|
1264
|
-
/**
|
|
1265
|
-
* Mutates a plain owned object/array and schedules the same path notice Proxy would.
|
|
1266
|
-
*
|
|
1267
|
-
* Root-array index assigns (`tags[0] = x`) notify as field replace (structural),
|
|
1268
|
-
* matching the transitional Proxy wrapArray behavior.
|
|
1269
|
-
* Shared multi-owner: writing through one field notifies all owners of the same raw object.
|
|
1270
|
-
*
|
|
1271
|
-
*/
|
|
1272
|
-
/**
|
|
1273
|
-
* Apply a binary compound/update op without a separate ReadPath call.
|
|
1274
|
-
*/
|
|
1275
|
-
function applyCompoundOp(op, cur, rhs) {
|
|
1276
|
-
switch (op) {
|
|
1277
|
-
case '+':
|
|
1278
|
-
return cur + rhs;
|
|
1279
|
-
case '-':
|
|
1280
|
-
return cur - rhs;
|
|
1281
|
-
case '*':
|
|
1282
|
-
return cur * rhs;
|
|
1283
|
-
case '/':
|
|
1284
|
-
return cur / rhs;
|
|
1285
|
-
case '%':
|
|
1286
|
-
return cur % rhs;
|
|
1287
|
-
case '**':
|
|
1288
|
-
return cur ** rhs;
|
|
1289
|
-
case '<<':
|
|
1290
|
-
return cur << rhs;
|
|
1291
|
-
case '>>':
|
|
1292
|
-
return cur >> rhs;
|
|
1293
|
-
case '>>>':
|
|
1294
|
-
return cur >>> rhs;
|
|
1295
|
-
case '|':
|
|
1296
|
-
return cur | rhs;
|
|
1297
|
-
case '^':
|
|
1298
|
-
return cur ^ rhs;
|
|
1299
|
-
case '&':
|
|
1300
|
-
return cur & rhs;
|
|
1301
|
-
default:
|
|
1302
|
-
return cur;
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
/**
|
|
1306
|
-
* Schedule leaf refresh after an array-item field mutate (event leaf-batch or trie).
|
|
1307
|
-
*/
|
|
1308
|
-
function notifyArrayItemLeaf(inst, root, idx, leaf) {
|
|
1309
|
-
if (typeof inst.__vmzDrainLeafDirty === 'function' && ((inst.__vmzEventDepth || 0) > 0 || inst.__vmzFlushSync)) {
|
|
1310
|
-
const i = +idx;
|
|
1311
|
-
const ld = inst.__vmzLeafDirty;
|
|
1312
|
-
if (!ld) {
|
|
1313
|
-
inst.__vmzLeafDirty = { root, field: leaf, idxs: [i] };
|
|
1314
|
-
}
|
|
1315
|
-
else if (ld.root === root && ld.field === leaf) {
|
|
1316
|
-
ld.idxs.push(i);
|
|
1317
|
-
}
|
|
1318
|
-
else {
|
|
1319
|
-
promoteLeafDirtyToTrie(inst);
|
|
1320
|
-
scheduleRefresh(inst, { type: 'path', root, segs: [String(idx), leaf] });
|
|
1321
|
-
return;
|
|
1322
|
-
}
|
|
1323
|
-
inst.__vmzFlushScheduled = true;
|
|
1324
|
-
return;
|
|
1325
|
-
}
|
|
1326
|
-
scheduleRefresh(inst, { type: 'path', root, segs: [String(idx), leaf] });
|
|
1327
|
-
}
|
|
1328
|
-
export function __vmzWritePath(inst, root, segs, value) {
|
|
1329
|
-
if (!inst || inst.__vmzDestroyed)
|
|
1330
|
-
return value;
|
|
1331
|
-
if (!root || !Array.isArray(segs) || segs.length === 0)
|
|
1332
|
-
return value;
|
|
1333
|
-
// Hot path: array item field write (`rows[i].label`) ??no map/slice, no shared-owner walk.
|
|
1334
|
-
if (segs.length === 2) {
|
|
1335
|
-
return __vmzWritePathItem(inst, root, segs[0], segs[1], value);
|
|
1336
|
-
}
|
|
1337
|
-
const normSegs = segs.map((s) => String(s));
|
|
1338
|
-
let obj = inst[root];
|
|
1339
|
-
if (obj == null || typeof obj !== 'object')
|
|
1340
|
-
return value;
|
|
1341
|
-
for (let i = 0; i < normSegs.length - 1; i++) {
|
|
1342
|
-
obj = obj[normSegs[i]];
|
|
1343
|
-
if (obj == null || typeof obj !== 'object')
|
|
1344
|
-
return value;
|
|
1345
|
-
}
|
|
1346
|
-
const leaf = normSegs[normSegs.length - 1];
|
|
1347
|
-
if (Object.is(obj[leaf], value))
|
|
1348
|
-
return value;
|
|
1349
|
-
obj[leaf] = value;
|
|
1350
|
-
// Register newly assigned nested objects under this field for future shared writes.
|
|
1351
|
-
if (value != null && typeof value === 'object') {
|
|
1352
|
-
const report = (local) => {
|
|
1353
|
-
if (!local || local.length === 0) {
|
|
1354
|
-
scheduleRefresh(inst, { type: 'replace', root });
|
|
1355
|
-
}
|
|
1356
|
-
else {
|
|
1357
|
-
scheduleRefresh(inst, { type: 'path', root, segs: local });
|
|
1358
|
-
}
|
|
1359
|
-
};
|
|
1360
|
-
registerWbOwner(value, report, normSegs.slice(), inst);
|
|
1361
|
-
}
|
|
1362
|
-
const rootObj = inst[root];
|
|
1363
|
-
const rootArr = rootObj;
|
|
1364
|
-
const isRootIndex = normSegs.length === 1 && Array.isArray(rootArr) && leaf !== 'length' && String(Number(leaf)) === leaf;
|
|
1365
|
-
if (isRootIndex) {
|
|
1366
|
-
if (!notifyWbShared(rootObj, null)) {
|
|
1367
|
-
scheduleRefresh(inst, { type: 'replace', root });
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
else if (!notifyWbShared(rootObj, normSegs)) {
|
|
1371
|
-
scheduleRefresh(inst, { type: 'path', root, segs: normSegs });
|
|
1372
|
-
}
|
|
1373
|
-
return value;
|
|
1374
|
-
}
|
|
1375
|
-
/**
|
|
1376
|
-
* Array-item leaf write without segs array alloc (`rows[i].label = v`).
|
|
1377
|
-
*/
|
|
1378
|
-
export function __vmzWritePathItem(inst, root, idx, leaf, value) {
|
|
1379
|
-
if (!inst || inst.__vmzDestroyed)
|
|
1380
|
-
return value;
|
|
1381
|
-
if (!root || leaf == null)
|
|
1382
|
-
return value;
|
|
1383
|
-
const arr = inst[root];
|
|
1384
|
-
if (!Array.isArray(arr))
|
|
1385
|
-
return value;
|
|
1386
|
-
const item = arr[idx];
|
|
1387
|
-
if (item == null || typeof item !== 'object')
|
|
1388
|
-
return value;
|
|
1389
|
-
if (Object.is(item[leaf], value))
|
|
1390
|
-
return value;
|
|
1391
|
-
item[leaf] = value;
|
|
1392
|
-
if (tryInlineLeafApply(inst, root, idx, leaf, item))
|
|
1393
|
-
return value;
|
|
1394
|
-
notifyArrayItemLeaf(inst, root, idx, leaf);
|
|
1395
|
-
return value;
|
|
1396
|
-
}
|
|
1397
|
-
/**
|
|
1398
|
-
* In-place two-index swap on an owned list field (WriteBarrier Slice 5).
|
|
1399
|
-
* Prefers eachBlock O(1) DOM transpose hook; otherwise schedules a list replace.
|
|
1400
|
-
*/
|
|
1401
|
-
export function __vmzListTranspose(inst, root, ia, ib) {
|
|
1402
|
-
if (!inst || inst.__vmzDestroyed || !root)
|
|
1403
|
-
return;
|
|
1404
|
-
const arr = inst[root];
|
|
1405
|
-
if (!Array.isArray(arr))
|
|
1406
|
-
return;
|
|
1407
|
-
const a = +ia;
|
|
1408
|
-
const b = +ib;
|
|
1409
|
-
if (a === b || a < 0 || b < 0 || a >= arr.length || b >= arr.length)
|
|
1410
|
-
return;
|
|
1411
|
-
const tmp = arr[a];
|
|
1412
|
-
arr[a] = arr[b];
|
|
1413
|
-
arr[b] = tmp;
|
|
1414
|
-
const hook = inst.__vmzEachTranspose && inst.__vmzEachTranspose[root];
|
|
1415
|
-
if (typeof hook === 'function' && hook(a, b) === true)
|
|
1416
|
-
return;
|
|
1417
|
-
scheduleRefresh(inst, { type: 'replace', root });
|
|
1418
|
-
}
|
|
1419
|
-
/**
|
|
1420
|
-
* Compound leaf write (`rows[i].label += x`) ??one item touch, no separate ReadPath.
|
|
1421
|
-
*/
|
|
1422
|
-
export function __vmzWritePathCompound(inst, root, segs, op, rhs) {
|
|
1423
|
-
if (!inst || inst.__vmzDestroyed)
|
|
1424
|
-
return undefined;
|
|
1425
|
-
if (!root || !Array.isArray(segs) || segs.length === 0)
|
|
1426
|
-
return undefined;
|
|
1427
|
-
if (segs.length === 2) {
|
|
1428
|
-
return __vmzWritePathCompoundItem(inst, root, segs[0], segs[1], op, rhs);
|
|
1429
|
-
}
|
|
1430
|
-
const cur = __vmzReadPath(inst, root, segs);
|
|
1431
|
-
const value = applyCompoundOp(op, cur, rhs);
|
|
1432
|
-
return __vmzWritePath(inst, root, segs, value);
|
|
1433
|
-
}
|
|
1434
|
-
/**
|
|
1435
|
-
* Array-item compound without segs array alloc (`rows[i].label += x`).
|
|
1436
|
-
*/
|
|
1437
|
-
export function __vmzWritePathCompoundItem(inst, root, idx, leaf, op, rhs) {
|
|
1438
|
-
if (!inst || inst.__vmzDestroyed)
|
|
1439
|
-
return undefined;
|
|
1440
|
-
if (!root || leaf == null)
|
|
1441
|
-
return undefined;
|
|
1442
|
-
const arr = inst[root];
|
|
1443
|
-
if (!Array.isArray(arr))
|
|
1444
|
-
return undefined;
|
|
1445
|
-
const item = arr[idx];
|
|
1446
|
-
if (item == null || typeof item !== 'object')
|
|
1447
|
-
return undefined;
|
|
1448
|
-
const cur = item[leaf];
|
|
1449
|
-
const value = applyCompoundOp(op, cur, rhs);
|
|
1450
|
-
if (Object.is(cur, value))
|
|
1451
|
-
return value;
|
|
1452
|
-
item[leaf] = value;
|
|
1453
|
-
if (tryInlineLeafApply(inst, root, idx, leaf, item))
|
|
1454
|
-
return value;
|
|
1455
|
-
notifyArrayItemLeaf(inst, root, idx, leaf);
|
|
1456
|
-
return value;
|
|
1457
|
-
}
|
|
1458
|
-
/**
|
|
1459
|
-
* Stride compound over an owned array (`for (i=start; i<n; i+=step) arr[i].leaf op= rhs`).
|
|
1460
|
-
* Mutates + applies DOM in one pass when eachBlock leaf hook is installed (update-every-Nth).
|
|
1461
|
-
*/
|
|
1462
|
-
export function __vmzArrayItemCompoundStride(inst, root, leaf, op, rhs, start, step) {
|
|
1463
|
-
if (!inst || inst.__vmzDestroyed || !root || leaf == null)
|
|
1464
|
-
return;
|
|
1465
|
-
// Prefer eachBlock-owned loop (hoisted applyByField, no per-index hook lookup).
|
|
1466
|
-
const owned = inst.__vmzEachCompoundStride && inst.__vmzEachCompoundStride[root];
|
|
1467
|
-
if (typeof owned === 'function' && owned(leaf, op, rhs, start, step) === true)
|
|
1468
|
-
return;
|
|
1469
|
-
const arr = inst[root];
|
|
1470
|
-
if (!Array.isArray(arr))
|
|
1471
|
-
return;
|
|
1472
|
-
const s = +start || 0;
|
|
1473
|
-
const st = +step || 0;
|
|
1474
|
-
if (st <= 0)
|
|
1475
|
-
return;
|
|
1476
|
-
const n = arr.length;
|
|
1477
|
-
const canInline = typeof inst.__vmzEachApplyLeaf === 'object' &&
|
|
1478
|
-
typeof inst.__vmzEachApplyLeaf[root] === 'function' &&
|
|
1479
|
-
((inst.__vmzEventDepth || 0) > 0 || inst.__vmzFlushSync);
|
|
1480
|
-
const applyLeaf = canInline ? inst.__vmzEachApplyLeaf[root] : null;
|
|
1481
|
-
// Hot path: string `+=` with inline DOM ??no switch / Object.is / idxs.
|
|
1482
|
-
if (op === '+' && applyLeaf) {
|
|
1483
|
-
for (let i = s; i < n; i += st) {
|
|
1484
|
-
const item = arr[i];
|
|
1485
|
-
if (item == null || typeof item !== 'object')
|
|
1486
|
-
continue;
|
|
1487
|
-
item[leaf] = item[leaf] + rhs;
|
|
1488
|
-
applyLeaf(i, leaf, item);
|
|
1489
|
-
}
|
|
1490
|
-
return;
|
|
1491
|
-
}
|
|
1492
|
-
const idxs = [];
|
|
1493
|
-
for (let i = s; i < n; i += st) {
|
|
1494
|
-
const item = arr[i];
|
|
1495
|
-
if (item == null || typeof item !== 'object')
|
|
1496
|
-
continue;
|
|
1497
|
-
const cur = item[leaf];
|
|
1498
|
-
const value = applyCompoundOp(op, cur, rhs);
|
|
1499
|
-
if (Object.is(cur, value))
|
|
1500
|
-
continue;
|
|
1501
|
-
item[leaf] = value;
|
|
1502
|
-
if (applyLeaf && applyLeaf(i, leaf, item) === true)
|
|
1503
|
-
continue;
|
|
1504
|
-
idxs.push(i);
|
|
1505
|
-
}
|
|
1506
|
-
if (!idxs.length)
|
|
1507
|
-
return;
|
|
1508
|
-
if (typeof inst.__vmzDrainLeafDirty === 'function' && ((inst.__vmzEventDepth || 0) > 0 || inst.__vmzFlushSync)) {
|
|
1509
|
-
inst.__vmzLeafDirty = { root, field: leaf, idxs };
|
|
1510
|
-
inst.__vmzFlushScheduled = true;
|
|
1511
|
-
return;
|
|
1512
|
-
}
|
|
1513
|
-
for (let k = 0; k < idxs.length; k++) {
|
|
1514
|
-
notifyArrayItemLeaf(inst, root, idxs[k], leaf);
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
/**
|
|
1518
|
-
* Apply a list-item leaf via eachBlock rowKernel hook when installed.
|
|
1519
|
-
*/
|
|
1520
|
-
function tryInlineLeafApply(inst, root, idx, leaf, item) {
|
|
1521
|
-
const hook = inst.__vmzEachApplyLeaf && inst.__vmzEachApplyLeaf[root];
|
|
1522
|
-
if (typeof hook !== 'function')
|
|
1523
|
-
return false;
|
|
1524
|
-
return hook(+idx, leaf, item) === true;
|
|
1525
|
-
}
|
|
1526
|
-
/**
|
|
1527
|
-
* Compiler-inserted array mutator barrier (push/pop/splice/??.
|
|
1528
|
-
* Applies the mutator on the plain array and schedules a structural notice
|
|
1529
|
-
* at `root` + `baseSegs` (empty baseSegs ??field replace).
|
|
1530
|
-
*
|
|
1531
|
-
*/
|
|
1532
|
-
export function __vmzArrayMutate(inst, root, baseSegs, method, args) {
|
|
1533
|
-
if (!inst || inst.__vmzDestroyed)
|
|
1534
|
-
return undefined;
|
|
1535
|
-
if (!root || typeof method !== 'string')
|
|
1536
|
-
return undefined;
|
|
1537
|
-
const segs = Array.isArray(baseSegs) ? baseSegs.map((s) => String(s)) : [];
|
|
1538
|
-
let arr = inst[root];
|
|
1539
|
-
if (arr == null || typeof arr !== 'object')
|
|
1540
|
-
return undefined;
|
|
1541
|
-
for (let i = 0; i < segs.length; i++) {
|
|
1542
|
-
arr = arr[segs[i]];
|
|
1543
|
-
if (arr == null || typeof arr !== 'object')
|
|
1544
|
-
return undefined;
|
|
1545
|
-
}
|
|
1546
|
-
if (!Array.isArray(arr) || typeof arr[method] !== 'function')
|
|
1547
|
-
return undefined;
|
|
1548
|
-
const list = Array.isArray(args) ? args : [];
|
|
1549
|
-
const ret = arr[method](...list);
|
|
1550
|
-
const rootObj = inst[root];
|
|
1551
|
-
if (segs.length === 0) {
|
|
1552
|
-
if (!notifyWbShared(rootObj, null)) {
|
|
1553
|
-
scheduleRefresh(inst, { type: 'replace', root });
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
else if (!notifyWbShared(rootObj, segs)) {
|
|
1557
|
-
scheduleRefresh(inst, { type: 'path', root, segs: segs.slice() });
|
|
1558
|
-
}
|
|
1559
|
-
return ret;
|
|
1560
|
-
}
|
|
1561
|
-
function makeReactive(inst, stateKeys) {
|
|
1562
|
-
const barrier = !!inst.constructor.__vmzWriteBarrier;
|
|
1563
|
-
for (const key of stateKeys) {
|
|
1564
|
-
if (!key || key.startsWith('#'))
|
|
1565
|
-
continue;
|
|
1566
|
-
const desc = Object.getOwnPropertyDescriptor(inst, key);
|
|
1567
|
-
if (desc && desc.set && desc.get && !desc.writable)
|
|
1568
|
-
continue;
|
|
1569
|
-
const report = (segs) => {
|
|
1570
|
-
if (!segs || segs.length === 0) {
|
|
1571
|
-
scheduleRefresh(inst, { type: 'replace', root: key });
|
|
1572
|
-
}
|
|
1573
|
-
else {
|
|
1574
|
-
scheduleRefresh(inst, { type: 'path', root: key, segs });
|
|
1575
|
-
}
|
|
1576
|
-
};
|
|
1577
|
-
// WriteBarrier components keep plain objects ??nested writes go through __vmzWritePath.
|
|
1578
|
-
let value = barrier ? inst[key] : wrapReactive(inst[key], report, []);
|
|
1579
|
-
if (barrier)
|
|
1580
|
-
registerWbOwner(value, report, [], inst);
|
|
1581
|
-
Object.defineProperty(inst, key, {
|
|
1582
|
-
configurable: true,
|
|
1583
|
-
enumerable: true,
|
|
1584
|
-
get() {
|
|
1585
|
-
return value;
|
|
1586
|
-
},
|
|
1587
|
-
set(next) {
|
|
1588
|
-
const wrapped = barrier ? next : wrapReactive(next, report, []);
|
|
1589
|
-
if (Object.is(value, wrapped))
|
|
1590
|
-
return;
|
|
1591
|
-
value = wrapped;
|
|
1592
|
-
if (barrier)
|
|
1593
|
-
registerWbOwner(value, report, [], inst);
|
|
1594
|
-
report(null);
|
|
1595
|
-
},
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
const reactiveProxies = new WeakMap();
|
|
1600
|
-
const writeBarrierOwned = new WeakSet();
|
|
1601
|
-
/**
|
|
1602
|
-
* WriteBarrier: true when value is an owned plain object with path barriers (no Proxy).
|
|
1603
|
-
*/
|
|
1604
|
-
export function __vmzIsWriteBarrierOwned(value) {
|
|
1605
|
-
return writeBarrierOwned.has(value);
|
|
1606
|
-
}
|
|
1607
|
-
/**
|
|
1608
|
-
* True when value is the Proxy wrapper from array (or residual) reactive wrap.
|
|
1609
|
-
*/
|
|
1610
|
-
export function __vmzIsReactiveProxy(value) {
|
|
1611
|
-
const e = reactiveProxies.get(value);
|
|
1612
|
-
return !!(e && e.kind === 'proxy' && e.proxy === value);
|
|
1613
|
-
}
|
|
1614
|
-
const ARRAY_MUTATORS = new Set(['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse', 'fill', 'copyWithin']);
|
|
1615
|
-
function sameSegs(a, b) {
|
|
1616
|
-
if (a.length !== b.length)
|
|
1617
|
-
return false;
|
|
1618
|
-
for (let i = 0; i < a.length; i++) {
|
|
1619
|
-
if (a[i] !== b[i])
|
|
1620
|
-
return false;
|
|
1621
|
-
}
|
|
1622
|
-
return true;
|
|
1623
|
-
}
|
|
1624
|
-
function addOwner(entry, report, baseSegs) {
|
|
1625
|
-
if (entry.owners.some((o) => o.report === report && sameSegs(o.baseSegs, baseSegs))) {
|
|
1626
|
-
return;
|
|
1627
|
-
}
|
|
1628
|
-
entry.owners.push({
|
|
1629
|
-
report,
|
|
1630
|
-
baseSegs: baseSegs.slice(),
|
|
1631
|
-
});
|
|
1632
|
-
}
|
|
1633
|
-
function notifyOwners(owners, localSegs) {
|
|
1634
|
-
for (const o of owners) {
|
|
1635
|
-
if (localSegs == null) {
|
|
1636
|
-
o.report(o.baseSegs.length ? o.baseSegs.slice() : null);
|
|
1637
|
-
}
|
|
1638
|
-
else {
|
|
1639
|
-
o.report([...o.baseSegs, ...localSegs]);
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
/**
|
|
1644
|
-
* Field-owned write traps for plain objects / arrays on state fields.
|
|
1645
|
-
* Plain objects: WriteBarrier via defineProperty (no Proxy).
|
|
1646
|
-
* Arrays: transitional Proxy tracks list identity/mutators only; elements stay plain
|
|
1647
|
-
* (no per-item wrap on large assign ??nested notifies via `__vmzWritePath`).
|
|
1648
|
-
* Shared raw objects notify **all** current owners.
|
|
1649
|
-
*
|
|
1650
|
-
*/
|
|
1651
|
-
function wrapReactive(value, report, pathSegs = []) {
|
|
1652
|
-
if (value == null || typeof value !== 'object')
|
|
1653
|
-
return value;
|
|
1654
|
-
const existing = reactiveProxies.get(value);
|
|
1655
|
-
if (existing) {
|
|
1656
|
-
addOwner(existing, report, pathSegs);
|
|
1657
|
-
return existing.proxy;
|
|
1658
|
-
}
|
|
1659
|
-
if (Array.isArray(value))
|
|
1660
|
-
return wrapArray(value, report, pathSegs);
|
|
1661
|
-
if (isPlainObject(value))
|
|
1662
|
-
return wrapOwnedObject(value, report, pathSegs);
|
|
1663
|
-
return value;
|
|
1664
|
-
}
|
|
1665
|
-
function isPlainObject(value) {
|
|
1666
|
-
const proto = Object.getPrototypeOf(value);
|
|
1667
|
-
return proto === Object.prototype || proto === null;
|
|
1668
|
-
}
|
|
1669
|
-
/**
|
|
1670
|
-
* Path-level write barrier for owned plain objects (no Proxy).
|
|
1671
|
-
*/
|
|
1672
|
-
function wrapOwnedObject(obj, report, pathSegs) {
|
|
1673
|
-
const existing = reactiveProxies.get(obj);
|
|
1674
|
-
if (existing) {
|
|
1675
|
-
addOwner(existing, report, pathSegs);
|
|
1676
|
-
return existing.proxy;
|
|
1677
|
-
}
|
|
1678
|
-
const entry = {
|
|
1679
|
-
proxy: obj,
|
|
1680
|
-
owners: [],
|
|
1681
|
-
kind: 'barrier',
|
|
1682
|
-
};
|
|
1683
|
-
addOwner(entry, report, pathSegs);
|
|
1684
|
-
writeBarrierOwned.add(obj);
|
|
1685
|
-
reactiveProxies.set(obj, entry);
|
|
1686
|
-
for (const prop of Object.keys(obj)) {
|
|
1687
|
-
installOwnedProp(obj, prop, entry);
|
|
1688
|
-
}
|
|
1689
|
-
return obj;
|
|
1690
|
-
}
|
|
1691
|
-
function installOwnedProp(obj, prop, entry) {
|
|
1692
|
-
const desc = Object.getOwnPropertyDescriptor(obj, prop);
|
|
1693
|
-
if (!desc || !desc.configurable)
|
|
1694
|
-
return;
|
|
1695
|
-
if (desc.get || desc.set)
|
|
1696
|
-
return;
|
|
1697
|
-
let current = obj[prop];
|
|
1698
|
-
for (const o of entry.owners) {
|
|
1699
|
-
current = wrapReactive(current, o.report, [...o.baseSegs, prop]);
|
|
1700
|
-
}
|
|
1701
|
-
Object.defineProperty(obj, prop, {
|
|
1702
|
-
configurable: true,
|
|
1703
|
-
enumerable: desc.enumerable !== false,
|
|
1704
|
-
get() {
|
|
1705
|
-
return current;
|
|
1706
|
-
},
|
|
1707
|
-
set(next) {
|
|
1708
|
-
const local = [prop];
|
|
1709
|
-
let wrapped = next;
|
|
1710
|
-
for (const o of entry.owners) {
|
|
1711
|
-
wrapped = wrapReactive(next, o.report, [...o.baseSegs, ...local]);
|
|
1712
|
-
}
|
|
1713
|
-
if (Object.is(current, wrapped))
|
|
1714
|
-
return;
|
|
1715
|
-
current = wrapped;
|
|
1716
|
-
notifyOwners(entry.owners, local);
|
|
1717
|
-
},
|
|
1718
|
-
});
|
|
1719
|
-
}
|
|
1720
|
-
/**
|
|
1721
|
-
* Transitional array Proxy: track list identity / mutators only.
|
|
1722
|
-
* Elements stay plain ??no per-item defineProperty on `this.rows = largeArray`
|
|
1723
|
-
* (design: WriteBarrier / list replace must not wrap 1k items). Nested field
|
|
1724
|
-
* notifies go through `__vmzWritePath` or whole-array replace.
|
|
1725
|
-
*/
|
|
1726
|
-
function wrapArray(arr, report, pathSegs) {
|
|
1727
|
-
const existing = reactiveProxies.get(arr);
|
|
1728
|
-
if (existing) {
|
|
1729
|
-
addOwner(existing, report, pathSegs);
|
|
1730
|
-
return existing.proxy;
|
|
1731
|
-
}
|
|
1732
|
-
const entry = {
|
|
1733
|
-
proxy: null,
|
|
1734
|
-
owners: [],
|
|
1735
|
-
kind: 'proxy',
|
|
1736
|
-
};
|
|
1737
|
-
addOwner(entry, report, pathSegs);
|
|
1738
|
-
const isArrayIndex = (prop) => typeof prop === 'string' && prop !== 'length' && String(Number(prop)) === prop;
|
|
1739
|
-
const proxy = new Proxy(arr, {
|
|
1740
|
-
get(target, prop, receiver) {
|
|
1741
|
-
if (typeof prop === 'string' && ARRAY_MUTATORS.has(prop)) {
|
|
1742
|
-
const fn = target[prop];
|
|
1743
|
-
return (...args) => {
|
|
1744
|
-
const ret = fn.apply(target, args);
|
|
1745
|
-
notifyOwners(entry.owners, null);
|
|
1746
|
-
return ret;
|
|
1747
|
-
};
|
|
1748
|
-
}
|
|
1749
|
-
// Indices / length / methods: return as-is (plain elements).
|
|
1750
|
-
return Reflect.get(target, prop, receiver);
|
|
1751
|
-
},
|
|
1752
|
-
set(target, prop, next, receiver) {
|
|
1753
|
-
const prev = target[prop];
|
|
1754
|
-
if (Object.is(prev, next))
|
|
1755
|
-
return true;
|
|
1756
|
-
const ok = Reflect.set(target, prop, next, receiver);
|
|
1757
|
-
if (ok) {
|
|
1758
|
-
if (prop === 'length' || isArrayIndex(prop))
|
|
1759
|
-
notifyOwners(entry.owners, null);
|
|
1760
|
-
else if (typeof prop === 'string')
|
|
1761
|
-
notifyOwners(entry.owners, [prop]);
|
|
1762
|
-
else
|
|
1763
|
-
notifyOwners(entry.owners, null);
|
|
1764
|
-
}
|
|
1765
|
-
return ok;
|
|
1766
|
-
},
|
|
1767
|
-
deleteProperty(target, prop) {
|
|
1768
|
-
if (!(prop in target))
|
|
1769
|
-
return true;
|
|
1770
|
-
const ok = Reflect.deleteProperty(target, prop);
|
|
1771
|
-
if (ok) {
|
|
1772
|
-
notifyOwners(entry.owners, typeof prop === 'string' ? [prop] : null);
|
|
1773
|
-
}
|
|
1774
|
-
return ok;
|
|
1775
|
-
},
|
|
1776
|
-
});
|
|
1777
|
-
entry.proxy = proxy;
|
|
1778
|
-
reactiveProxies.set(arr, entry);
|
|
1779
|
-
reactiveProxies.set(proxy, entry);
|
|
1780
|
-
return proxy;
|
|
1781
|
-
}
|
|
1782
|
-
/**
|
|
1783
|
-
* Coalesce field/path patches in the same turn via a dirty path trie.
|
|
1784
|
-
* Still precise deps ??never a full-tree re-render. Flush runs as a microtask by default;
|
|
1785
|
-
* DOM event handlers drain synchronously via beginEventFlush/endEventFlush when methodRw
|
|
1786
|
-
* proves the handler is sync (`async: false`, `opaque: false`).
|
|
1787
|
-
* Call `await flushPending(inst)` (may return void or a Promise) for tests / immediate UI.
|
|
1788
|
-
*
|
|
1789
|
-
*
|
|
1790
|
-
* string form is transitional field-root alias for replace.
|
|
1791
|
-
*/
|
|
1792
|
-
function scheduleRefresh(inst, notice) {
|
|
1793
|
-
if (!inst || inst.__vmzDestroyed || inst.__vmzQuiet)
|
|
1794
|
-
return;
|
|
1795
|
-
const n = typeof notice === 'string' ? { type: 'replace', root: notice } : notice;
|
|
1796
|
-
if (!n || !n.root)
|
|
1797
|
-
return;
|
|
1798
|
-
if (precision.enabled) {
|
|
1799
|
-
precision.writes++;
|
|
1800
|
-
bumpMap(precision.writesByRoot, n.root);
|
|
1801
|
-
}
|
|
1802
|
-
pushTrace('write', 'field', n.root, n.root);
|
|
1803
|
-
if (!inst.__vmzDirtyTrie)
|
|
1804
|
-
inst.__vmzDirtyTrie = Object.create(null);
|
|
1805
|
-
insertDirtyNotice(inst.__vmzDirtyTrie, n);
|
|
1806
|
-
// Transitional: keep notice list for flush loop emptiness check / compat.
|
|
1807
|
-
if (!inst.__vmzDirtyNotices)
|
|
1808
|
-
inst.__vmzDirtyNotices = [];
|
|
1809
|
-
inst.__vmzDirtyNotices.push(n);
|
|
1810
|
-
// Inside a UI event (or its sync flush): coalesce; endEventFlush drains without microtask hop.
|
|
1811
|
-
if ((inst.__vmzEventDepth || 0) > 0 || inst.__vmzFlushSync) {
|
|
1812
|
-
inst.__vmzFlushScheduled = true;
|
|
1813
|
-
return;
|
|
1814
|
-
}
|
|
1815
|
-
if (inst.__vmzFlushScheduled)
|
|
1816
|
-
return;
|
|
1817
|
-
inst.__vmzFlushScheduled = true;
|
|
1818
|
-
queueMicrotask(() => {
|
|
1819
|
-
inst.__vmzFlushScheduled = false;
|
|
1820
|
-
const p = flushPending(inst);
|
|
1821
|
-
if (p && typeof p.then === 'function') {
|
|
1822
|
-
p.catch((err) => console.error('vmz:dom flush', err));
|
|
1823
|
-
}
|
|
1824
|
-
});
|
|
1825
|
-
}
|
|
1826
|
-
/**
|
|
1827
|
-
* Infer `this.<method>(...)` from a compiled click arrow / bag handler.
|
|
1828
|
-
*/
|
|
1829
|
-
function inferHandlerMethod(handler) {
|
|
1830
|
-
if (typeof handler !== 'function')
|
|
1831
|
-
return null;
|
|
1832
|
-
if (Object.hasOwn(handler, '__vmzMethod')) {
|
|
1833
|
-
return handler.__vmzMethod;
|
|
1834
|
-
}
|
|
1835
|
-
let name = null;
|
|
1836
|
-
try {
|
|
1837
|
-
const src = Function.prototype.toString.call(handler);
|
|
1838
|
-
const m = src.match(/this\.([A-Za-z_$][\w$]*)\s*\(/);
|
|
1839
|
-
name = m ? m[1] : null;
|
|
1840
|
-
}
|
|
1841
|
-
catch {
|
|
1842
|
-
name = null;
|
|
1843
|
-
}
|
|
1844
|
-
try {
|
|
1845
|
-
handler.__vmzMethod = name;
|
|
1846
|
-
}
|
|
1847
|
-
catch {
|
|
1848
|
-
/* non-extensible function */
|
|
1849
|
-
}
|
|
1850
|
-
return name;
|
|
1851
|
-
}
|
|
1852
|
-
/**
|
|
1853
|
-
* Sync event flush only when `__vmzMethodRw` proves the method is non-async / non-opaque.
|
|
1854
|
-
* Missing summary ??sync (Direct UI default). Async/opaque ??microtask coalesce.
|
|
1855
|
-
*/
|
|
1856
|
-
function methodAllowsSyncEventFlush(inst, methodName) {
|
|
1857
|
-
if (!inst)
|
|
1858
|
-
return false;
|
|
1859
|
-
if (!methodName)
|
|
1860
|
-
return true;
|
|
1861
|
-
const table = inst.constructor && inst.constructor.__vmzMethodRw;
|
|
1862
|
-
const rw = table && table[methodName];
|
|
1863
|
-
if (!rw)
|
|
1864
|
-
return true;
|
|
1865
|
-
if (rw.async || rw.opaque)
|
|
1866
|
-
return false;
|
|
1867
|
-
return true;
|
|
1868
|
-
}
|
|
1869
|
-
function runDomEventHandler(inst, methodHint, fn) {
|
|
1870
|
-
const sync = methodAllowsSyncEventFlush(inst, methodHint);
|
|
1871
|
-
if (sync)
|
|
1872
|
-
beginEventFlush(inst);
|
|
1873
|
-
try {
|
|
1874
|
-
return fn();
|
|
1875
|
-
}
|
|
1876
|
-
finally {
|
|
1877
|
-
if (sync)
|
|
1878
|
-
endEventFlush(inst);
|
|
1879
|
-
}
|
|
1880
|
-
}
|
|
1881
|
-
function beginEventFlush(inst) {
|
|
1882
|
-
if (!inst)
|
|
1883
|
-
return;
|
|
1884
|
-
inst.__vmzEventDepth = (inst.__vmzEventDepth || 0) + 1;
|
|
1885
|
-
}
|
|
1886
|
-
function endEventFlush(inst) {
|
|
1887
|
-
if (!inst)
|
|
1888
|
-
return;
|
|
1889
|
-
inst.__vmzEventDepth = Math.max(0, (inst.__vmzEventDepth || 0) - 1);
|
|
1890
|
-
if (inst.__vmzEventDepth !== 0 || !inst.__vmzFlushScheduled)
|
|
1891
|
-
return;
|
|
1892
|
-
// Keep __vmzFlushSync so nested writes during flush stay off the microtask path.
|
|
1893
|
-
inst.__vmzFlushSync = true;
|
|
1894
|
-
try {
|
|
1895
|
-
inst.__vmzFlushScheduled = false;
|
|
1896
|
-
const ret = flushPending(inst);
|
|
1897
|
-
if (ret && typeof ret.then === 'function') {
|
|
1898
|
-
ret.catch((err) => console.error('vmz:dom flush', err));
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
finally {
|
|
1902
|
-
inst.__vmzFlushSync = false;
|
|
1903
|
-
// Async binder left dirties: fall back to microtask coalesce.
|
|
1904
|
-
if (inst.__vmzFlushScheduled) {
|
|
1905
|
-
const again = inst.__vmzFlushScheduled;
|
|
1906
|
-
inst.__vmzFlushScheduled = false;
|
|
1907
|
-
if (again) {
|
|
1908
|
-
inst.__vmzFlushScheduled = true;
|
|
1909
|
-
queueMicrotask(() => {
|
|
1910
|
-
inst.__vmzFlushScheduled = false;
|
|
1911
|
-
const p = flushPending(inst);
|
|
1912
|
-
if (p && typeof p.then === 'function') {
|
|
1913
|
-
p.catch((err) => console.error('vmz:dom flush', err));
|
|
1914
|
-
}
|
|
1915
|
-
});
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
/**
|
|
1921
|
-
* Promote an abandoned leaf batch into the dirty trie so mixed writes stay correct.
|
|
1922
|
-
*/
|
|
1923
|
-
function promoteLeafDirtyToTrie(inst) {
|
|
1924
|
-
const ld = inst.__vmzLeafDirty;
|
|
1925
|
-
if (!ld || !ld.idxs || !ld.idxs.length) {
|
|
1926
|
-
inst.__vmzLeafDirty = null;
|
|
1927
|
-
return;
|
|
1928
|
-
}
|
|
1929
|
-
inst.__vmzLeafDirty = null;
|
|
1930
|
-
if (!inst.__vmzDirtyTrie)
|
|
1931
|
-
inst.__vmzDirtyTrie = Object.create(null);
|
|
1932
|
-
if (!inst.__vmzDirtyNotices)
|
|
1933
|
-
inst.__vmzDirtyNotices = [];
|
|
1934
|
-
const field = ld.field;
|
|
1935
|
-
const root = ld.root;
|
|
1936
|
-
for (let k = 0; k < ld.idxs.length; k++) {
|
|
1937
|
-
const segs = [String(ld.idxs[k]), field];
|
|
1938
|
-
insertDirtyNotice(inst.__vmzDirtyTrie, { type: 'path', root, segs });
|
|
1939
|
-
inst.__vmzDirtyNotices.push({ type: 'path', root, segs });
|
|
1940
|
-
}
|
|
1941
|
-
inst.__vmzFlushScheduled = true;
|
|
1942
|
-
}
|
|
1943
|
-
function insertDirtyNotice(trie, notice) {
|
|
1944
|
-
if (notice.type === 'replace') {
|
|
1945
|
-
trie[notice.root] = { replace: true };
|
|
1946
|
-
return;
|
|
1947
|
-
}
|
|
1948
|
-
const segs = notice.segs || [];
|
|
1949
|
-
let node = trie[notice.root];
|
|
1950
|
-
if (node && node.replace)
|
|
1951
|
-
return;
|
|
1952
|
-
if (!node) {
|
|
1953
|
-
node = { children: Object.create(null) };
|
|
1954
|
-
trie[notice.root] = node;
|
|
1955
|
-
}
|
|
1956
|
-
if (!segs.length) {
|
|
1957
|
-
trie[notice.root] = { replace: true };
|
|
1958
|
-
return;
|
|
1959
|
-
}
|
|
1960
|
-
if (!node.children)
|
|
1961
|
-
node.children = Object.create(null);
|
|
1962
|
-
let cur = node;
|
|
1963
|
-
for (let i = 0; i < segs.length; i++) {
|
|
1964
|
-
const seg = segs[i];
|
|
1965
|
-
if (cur.dirty)
|
|
1966
|
-
return; // ancestor already dirty
|
|
1967
|
-
if (!cur.children)
|
|
1968
|
-
cur.children = Object.create(null);
|
|
1969
|
-
if (i === segs.length - 1) {
|
|
1970
|
-
cur.children[seg] = { dirty: true };
|
|
1971
|
-
return;
|
|
1972
|
-
}
|
|
1973
|
-
let next = cur.children[seg];
|
|
1974
|
-
if (!next) {
|
|
1975
|
-
next = { children: Object.create(null) };
|
|
1976
|
-
cur.children[seg] = next;
|
|
1977
|
-
}
|
|
1978
|
-
else if (next.dirty) {
|
|
1979
|
-
return;
|
|
1980
|
-
}
|
|
1981
|
-
else if (!next.children) {
|
|
1982
|
-
next.children = Object.create(null);
|
|
1983
|
-
}
|
|
1984
|
-
cur = next;
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
export function flushPending(inst) {
|
|
1988
|
-
if (!inst || inst.__vmzDestroyed)
|
|
1989
|
-
return undefined;
|
|
1990
|
-
inst.__vmzFlushScheduled = false;
|
|
1991
|
-
// rowKernel leaf batch (event update): apply before trie emptiness check.
|
|
1992
|
-
if (typeof inst.__vmzDrainLeafDirty === 'function') {
|
|
1993
|
-
try {
|
|
1994
|
-
inst.__vmzDrainLeafDirty();
|
|
1995
|
-
}
|
|
1996
|
-
catch (err) {
|
|
1997
|
-
console.error('vmz:dom leafDirty', err);
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
let guard = 0;
|
|
2001
|
-
while (!inst.__vmzDestroyed &&
|
|
2002
|
-
(dirtyTrieHasEntries(inst.__vmzDirtyTrie) || (inst.__vmzDirtyNotices && inst.__vmzDirtyNotices.length > 0)) &&
|
|
2003
|
-
guard++ < 64) {
|
|
2004
|
-
const trie = inst.__vmzDirtyTrie || Object.create(null);
|
|
2005
|
-
inst.__vmzDirtyTrie = Object.create(null);
|
|
2006
|
-
if (inst.__vmzDirtyNotices)
|
|
2007
|
-
inst.__vmzDirtyNotices.length = 0;
|
|
2008
|
-
inst.__vmzFlushTrie = trie;
|
|
2009
|
-
const jobs = [];
|
|
2010
|
-
// Prefer BindingId scheduling (IR). String `__vmzBinders` is adapter-only.
|
|
2011
|
-
// Pass `trie` into refresh ??dirty map is cleared above before patches run.
|
|
2012
|
-
try {
|
|
2013
|
-
const bindingIds = bindingIdsMatchingTrie(inst, trie);
|
|
2014
|
-
const coveredDeps = Object.create(null);
|
|
2015
|
-
for (const id of bindingIds) {
|
|
2016
|
-
const entry = inst.__vmzBindings && inst.__vmzBindings[id];
|
|
2017
|
-
if (entry) {
|
|
2018
|
-
for (const d of entry.deps || [])
|
|
2019
|
-
coveredDeps[d] = true;
|
|
2020
|
-
}
|
|
2021
|
-
jobs.push(...refreshBinding(inst, id, trie));
|
|
2022
|
-
}
|
|
2023
|
-
for (const key of binderKeysMatchingTrie(inst, trie)) {
|
|
2024
|
-
if (coveredDeps[key] || (inst.__vmzDepToBindings && inst.__vmzDepToBindings[key]?.length)) {
|
|
2025
|
-
// BindingId path already flushed IR patches for this dep.
|
|
2026
|
-
// Still run binder-only patches (bindComponentProp uses bindingId null).
|
|
2027
|
-
jobs.push(...refreshFieldBinderOnly(inst, key));
|
|
2028
|
-
continue;
|
|
2029
|
-
}
|
|
2030
|
-
jobs.push(...refreshField(inst, key));
|
|
2031
|
-
}
|
|
2032
|
-
const pending = jobs.filter((j) => j && typeof j.then === 'function');
|
|
2033
|
-
if (pending.length) {
|
|
2034
|
-
// Async binders: resume after they settle (default microtask path after).
|
|
2035
|
-
return Promise.all(pending).then(() => flushPending(inst));
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
finally {
|
|
2039
|
-
inst.__vmzFlushTrie = null;
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
return undefined;
|
|
2043
|
-
}
|
|
2044
|
-
function dirtyTrieHasEntries(trie) {
|
|
2045
|
-
if (!trie)
|
|
2046
|
-
return false;
|
|
2047
|
-
for (const _ in trie)
|
|
2048
|
-
return true;
|
|
2049
|
-
return false;
|
|
2050
|
-
}
|
|
2051
|
-
function bindingIdsMatchingTrie(inst, trie) {
|
|
2052
|
-
const index = inst.__vmzDepToBindings;
|
|
2053
|
-
if (!index)
|
|
2054
|
-
return [];
|
|
2055
|
-
const out = [];
|
|
2056
|
-
const seen = Object.create(null);
|
|
2057
|
-
for (const key of Object.keys(index)) {
|
|
2058
|
-
if (!depMatchesTrie(trie, key))
|
|
2059
|
-
continue;
|
|
2060
|
-
for (const id of index[key]) {
|
|
2061
|
-
const k = String(id);
|
|
2062
|
-
if (seen[k])
|
|
2063
|
-
continue;
|
|
2064
|
-
seen[k] = true;
|
|
2065
|
-
out.push(id);
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
return out;
|
|
2069
|
-
}
|
|
2070
|
-
function binderKeysMatchingTrie(inst, trie) {
|
|
2071
|
-
const binders = inst.__vmzBinders;
|
|
2072
|
-
if (!binders)
|
|
2073
|
-
return [];
|
|
2074
|
-
const out = [];
|
|
2075
|
-
for (const key of Object.keys(binders)) {
|
|
2076
|
-
if (depMatchesTrie(trie, key))
|
|
2077
|
-
out.push(key);
|
|
2078
|
-
}
|
|
2079
|
-
return out;
|
|
2080
|
-
}
|
|
2081
|
-
function depMatchesTrie(trie, key) {
|
|
2082
|
-
const root = depRootField(key);
|
|
2083
|
-
const node = trie[root];
|
|
2084
|
-
if (!node)
|
|
2085
|
-
return false;
|
|
2086
|
-
if (node.replace) {
|
|
2087
|
-
return key === root || key === `${root}.*` || key.startsWith(`${root}.`) || key.startsWith(`${root}[`);
|
|
2088
|
-
}
|
|
2089
|
-
if (key === `${root}.*`) {
|
|
2090
|
-
// Bare `field.*` soft/structure channel: item replace / array structure only ?? // NOT deep leaf writes (`tags.0.label`); those use `tags.*.label` BindingId.
|
|
2091
|
-
return structureStarMatches(node);
|
|
2092
|
-
}
|
|
2093
|
-
// Bare field: replace-only.
|
|
2094
|
-
if (key === root)
|
|
2095
|
-
return false;
|
|
2096
|
-
// Path channel: `tags.*.label` ??wildcard index under list root.
|
|
2097
|
-
const starPrefix = `${root}.*`;
|
|
2098
|
-
if (key === starPrefix || key.startsWith(`${starPrefix}.`)) {
|
|
2099
|
-
const rest = key === starPrefix
|
|
2100
|
-
? []
|
|
2101
|
-
: key
|
|
2102
|
-
.slice(starPrefix.length + 1)
|
|
2103
|
-
.split('.')
|
|
2104
|
-
.filter(Boolean);
|
|
2105
|
-
return wildcardIndexDirtyCovers(node, rest);
|
|
2106
|
-
}
|
|
2107
|
-
// Stable ListItem form `tags[key=?].label` ??treat `[key=?]` as wildcard index.
|
|
2108
|
-
if (key.startsWith(`${root}[`)) {
|
|
2109
|
-
const afterBracket = key.indexOf(']');
|
|
2110
|
-
if (afterBracket > root.length) {
|
|
2111
|
-
const rest = key.length > afterBracket + 1 && key[afterBracket + 1] === '.'
|
|
2112
|
-
? key
|
|
2113
|
-
.slice(afterBracket + 2)
|
|
2114
|
-
.split('.')
|
|
2115
|
-
.filter(Boolean)
|
|
2116
|
-
: [];
|
|
2117
|
-
return wildcardIndexDirtyCovers(node, rest);
|
|
2118
|
-
}
|
|
2119
|
-
}
|
|
2120
|
-
const segs = key
|
|
2121
|
-
.slice(root.length + 1)
|
|
2122
|
-
.split('.')
|
|
2123
|
-
.filter(Boolean);
|
|
2124
|
-
return pathDirtyCovers(node, segs);
|
|
2125
|
-
}
|
|
2126
|
-
function structureStarMatches(node) {
|
|
2127
|
-
if (!node)
|
|
2128
|
-
return false;
|
|
2129
|
-
if (node.replace || node.dirty)
|
|
2130
|
-
return true;
|
|
2131
|
-
if (!node.children)
|
|
2132
|
-
return false;
|
|
2133
|
-
for (const idx of Object.keys(node.children)) {
|
|
2134
|
-
const child = node.children[idx];
|
|
2135
|
-
// Index node dirty/replace ??item identity changed.
|
|
2136
|
-
if (child && (child.replace || child.dirty))
|
|
2137
|
-
return true;
|
|
2138
|
-
}
|
|
2139
|
-
return false;
|
|
2140
|
-
}
|
|
2141
|
-
function wildcardIndexDirtyCovers(node, restSegs) {
|
|
2142
|
-
if (!node || node.replace)
|
|
2143
|
-
return !!node?.replace;
|
|
2144
|
-
if (node.dirty)
|
|
2145
|
-
return true;
|
|
2146
|
-
if (!node.children)
|
|
2147
|
-
return false;
|
|
2148
|
-
for (const idx of Object.keys(node.children)) {
|
|
2149
|
-
const child = node.children[idx];
|
|
2150
|
-
if (restSegs.length === 0) {
|
|
2151
|
-
if (trieHasAnyDirty(child))
|
|
2152
|
-
return true;
|
|
2153
|
-
}
|
|
2154
|
-
else if (pathDirtyCovers(child, restSegs)) {
|
|
2155
|
-
return true;
|
|
2156
|
-
}
|
|
2157
|
-
}
|
|
2158
|
-
return false;
|
|
2159
|
-
}
|
|
2160
|
-
function trieHasAnyDirty(node) {
|
|
2161
|
-
if (!node || node.replace)
|
|
2162
|
-
return !!node;
|
|
2163
|
-
if (node.dirty)
|
|
2164
|
-
return true;
|
|
2165
|
-
if (!node.children)
|
|
2166
|
-
return false;
|
|
2167
|
-
for (const k of Object.keys(node.children)) {
|
|
2168
|
-
if (trieHasAnyDirty(node.children[k]))
|
|
2169
|
-
return true;
|
|
2170
|
-
}
|
|
2171
|
-
return false;
|
|
2172
|
-
}
|
|
2173
|
-
/**
|
|
2174
|
-
* Wake if write is at/under dep, or dep is under write (parent covers children).
|
|
2175
|
-
*/
|
|
2176
|
-
function pathDirtyCovers(node, depSegs) {
|
|
2177
|
-
let cur = node;
|
|
2178
|
-
for (let i = 0; i < depSegs.length; i++) {
|
|
2179
|
-
if (!cur || cur.replace)
|
|
2180
|
-
return !!cur?.replace;
|
|
2181
|
-
if (cur.dirty)
|
|
2182
|
-
return true; // write parent covers this dep
|
|
2183
|
-
if (!cur.children)
|
|
2184
|
-
return false;
|
|
2185
|
-
const next = cur.children[depSegs[i]];
|
|
2186
|
-
if (!next) {
|
|
2187
|
-
// No write along this dep path ??but a write under a prefix?
|
|
2188
|
-
return false;
|
|
2189
|
-
}
|
|
2190
|
-
cur = next;
|
|
2191
|
-
}
|
|
2192
|
-
// Reached dep node: wake if dirty here or any dirty descendant (write under dep).
|
|
2193
|
-
return trieHasAnyDirty(cur);
|
|
2194
|
-
}
|
|
2195
|
-
/**
|
|
2196
|
-
* Dual-track match retained for tests / tooling.
|
|
2197
|
-
*/
|
|
2198
|
-
function noticeMatchesDepKey(notice, key) {
|
|
2199
|
-
const trie = Object.create(null);
|
|
2200
|
-
insertDirtyNotice(trie, notice);
|
|
2201
|
-
return depMatchesTrie(trie, key);
|
|
2202
|
-
}
|
|
2203
|
-
function depRootField(dep) {
|
|
2204
|
-
if (!dep)
|
|
2205
|
-
return '';
|
|
2206
|
-
const star = dep.indexOf('.*');
|
|
2207
|
-
if (star >= 0)
|
|
2208
|
-
return dep.slice(0, star);
|
|
2209
|
-
const dot = dep.indexOf('.');
|
|
2210
|
-
if (dot >= 0)
|
|
2211
|
-
return dep.slice(0, dot);
|
|
2212
|
-
const bracket = dep.indexOf('[');
|
|
2213
|
-
if (bracket >= 0)
|
|
2214
|
-
return dep.slice(0, bracket);
|
|
2215
|
-
return dep;
|
|
2216
|
-
}
|
|
2217
|
-
function refreshBinding(inst, bindingId, dirtyTrie = null) {
|
|
2218
|
-
const entry = inst.__vmzBindings && inst.__vmzBindings[bindingId];
|
|
2219
|
-
const jobs = [];
|
|
2220
|
-
if (!inst || inst.__vmzDestroyed || bindingId == null || !entry) {
|
|
2221
|
-
return jobs;
|
|
2222
|
-
}
|
|
2223
|
-
const depKey = (entry.deps && entry.deps[0]) || null;
|
|
2224
|
-
const trie = dirtyTrie || inst.__vmzDirtyTrie;
|
|
2225
|
-
const allowIdx = itemIndicesAllowedForDeps(trie, entry.deps);
|
|
2226
|
-
// Snapshot: keyed-each items share IR BindingIds; cf patches may untrack/retrack
|
|
2227
|
-
// mid-flush and mutate `entry.patches` (would skip sibling item attrs like aria-selected).
|
|
2228
|
-
const patches = entry.patches.slice();
|
|
2229
|
-
for (const fn of patches) {
|
|
2230
|
-
if (allowIdx && !patchMatchesDirtyIndex(fn, allowIdx))
|
|
2231
|
-
continue;
|
|
2232
|
-
try {
|
|
2233
|
-
const ret = runPatch(inst, fn, depKey, bindingId);
|
|
2234
|
-
if (ret && typeof ret.then === 'function')
|
|
2235
|
-
jobs.push(ret);
|
|
2236
|
-
}
|
|
2237
|
-
catch (err) {
|
|
2238
|
-
console.error('vmz:dom patch', err);
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
return jobs;
|
|
2242
|
-
}
|
|
2243
|
-
/**
|
|
2244
|
-
* For ListItem path-channel deps (`tags.*.label`), restrict to dirty indices.
|
|
2245
|
-
*/
|
|
2246
|
-
function itemIndicesAllowedForDeps(trie, deps) {
|
|
2247
|
-
if (!trie || !deps || !deps.length)
|
|
2248
|
-
return null;
|
|
2249
|
-
let sawListChannel = false;
|
|
2250
|
-
let allow = null;
|
|
2251
|
-
for (const dep of deps) {
|
|
2252
|
-
const root = depRootField(dep);
|
|
2253
|
-
if (!root)
|
|
2254
|
-
continue;
|
|
2255
|
-
const starPrefix = `${root}.*`;
|
|
2256
|
-
const isListChannel = dep === starPrefix || dep.startsWith(`${starPrefix}.`) || (dep.startsWith(`${root}[`) && dep.includes(']'));
|
|
2257
|
-
if (!isListChannel)
|
|
2258
|
-
return null;
|
|
2259
|
-
sawListChannel = true;
|
|
2260
|
-
const node = trie[root];
|
|
2261
|
-
if (!node)
|
|
2262
|
-
continue;
|
|
2263
|
-
if (node.replace || node.dirty)
|
|
2264
|
-
return null; // whole list
|
|
2265
|
-
if (!node.children)
|
|
2266
|
-
continue;
|
|
2267
|
-
if (!allow)
|
|
2268
|
-
allow = new Set();
|
|
2269
|
-
for (const idx of Object.keys(node.children)) {
|
|
2270
|
-
const child = node.children[idx];
|
|
2271
|
-
if (!child)
|
|
2272
|
-
continue;
|
|
2273
|
-
if (child.replace || child.dirty || trieHasAnyDirty(child)) {
|
|
2274
|
-
allow.add(String(idx));
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
}
|
|
2278
|
-
if (!sawListChannel)
|
|
2279
|
-
return null;
|
|
2280
|
-
return allow && allow.size ? allow : null;
|
|
2281
|
-
}
|
|
2282
|
-
/**
|
|
2283
|
-
* If every allowed list index is dirty on exactly the same single item field, return that field.
|
|
2284
|
-
* Used to hoist one monomorphic `applyByField[field]` across the update batch.
|
|
2285
|
-
*/
|
|
2286
|
-
function soleDirtyItemField(trie, listRoot, allowIdx) {
|
|
2287
|
-
if (!trie || !listRoot || !allowIdx || !allowIdx.size)
|
|
2288
|
-
return null;
|
|
2289
|
-
const node = trie[listRoot];
|
|
2290
|
-
if (!node || node.replace || node.dirty || !node.children)
|
|
2291
|
-
return null;
|
|
2292
|
-
// Peek first index for the sole dirty field, then verify the rest match.
|
|
2293
|
-
// Fast path: index child has a single key (typical WritePath leaf) ??no sibling scan.
|
|
2294
|
-
let field = null;
|
|
2295
|
-
for (const idx of allowIdx) {
|
|
2296
|
-
const child = node.children[String(idx)];
|
|
2297
|
-
if (!child || child.replace || child.dirty || !child.children)
|
|
2298
|
-
return null;
|
|
2299
|
-
const kids = child.children;
|
|
2300
|
-
const keys = Object.keys(kids);
|
|
2301
|
-
if (keys.length === 1) {
|
|
2302
|
-
const f = keys[0];
|
|
2303
|
-
const n = kids[f];
|
|
2304
|
-
if (!n || !(n.dirty || n.replace || trieHasAnyDirty(n)))
|
|
2305
|
-
return null;
|
|
2306
|
-
if (field == null)
|
|
2307
|
-
field = f;
|
|
2308
|
-
else if (field !== f)
|
|
2309
|
-
return null;
|
|
2310
|
-
continue;
|
|
2311
|
-
}
|
|
2312
|
-
if (field == null) {
|
|
2313
|
-
for (let k = 0; k < keys.length; k++) {
|
|
2314
|
-
const f = keys[k];
|
|
2315
|
-
const n = kids[f];
|
|
2316
|
-
if (n && (n.dirty || n.replace || trieHasAnyDirty(n))) {
|
|
2317
|
-
if (field != null)
|
|
2318
|
-
return null;
|
|
2319
|
-
field = f;
|
|
2320
|
-
}
|
|
2321
|
-
}
|
|
2322
|
-
if (field == null)
|
|
2323
|
-
return null;
|
|
2324
|
-
continue;
|
|
2325
|
-
}
|
|
2326
|
-
const n = kids[field];
|
|
2327
|
-
if (!n || !(n.dirty || n.replace || trieHasAnyDirty(n)))
|
|
2328
|
-
return null;
|
|
2329
|
-
for (let k = 0; k < keys.length; k++) {
|
|
2330
|
-
const f = keys[k];
|
|
2331
|
-
if (f === field)
|
|
2332
|
-
continue;
|
|
2333
|
-
const o = kids[f];
|
|
2334
|
-
if (o && (o.dirty || o.replace || trieHasAnyDirty(o)))
|
|
2335
|
-
return null;
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
return field;
|
|
2339
|
-
}
|
|
2340
|
-
/**
|
|
2341
|
-
* Dirty item field names at list index (`rows.3.label` ??`["label"]`).
|
|
2342
|
-
*/
|
|
2343
|
-
function dirtyItemFieldsAt(trie, listRoot, idx) {
|
|
2344
|
-
if (!trie || !listRoot)
|
|
2345
|
-
return null;
|
|
2346
|
-
const node = trie[listRoot];
|
|
2347
|
-
if (!node)
|
|
2348
|
-
return [];
|
|
2349
|
-
if (node.replace || node.dirty)
|
|
2350
|
-
return null;
|
|
2351
|
-
const child = node.children && node.children[String(idx)];
|
|
2352
|
-
if (!child)
|
|
2353
|
-
return [];
|
|
2354
|
-
if (child.replace || child.dirty)
|
|
2355
|
-
return null;
|
|
2356
|
-
if (!child.children)
|
|
2357
|
-
return null;
|
|
2358
|
-
const out = [];
|
|
2359
|
-
for (const field of Object.keys(child.children)) {
|
|
2360
|
-
const n = child.children[field];
|
|
2361
|
-
if (n && (n.dirty || n.replace || trieHasAnyDirty(n)))
|
|
2362
|
-
out.push(field);
|
|
2363
|
-
}
|
|
2364
|
-
return out;
|
|
2365
|
-
}
|
|
2366
|
-
function patchMatchesDirtyIndex(fn, allowIdx) {
|
|
2367
|
-
const idx = fn && fn.__vmzItemIndex;
|
|
2368
|
-
if (idx == null || idx === '')
|
|
2369
|
-
return true;
|
|
2370
|
-
return allowIdx.has(String(idx));
|
|
2371
|
-
}
|
|
2372
|
-
function refreshField(inst, field) {
|
|
2373
|
-
const binders = inst.__vmzBinders;
|
|
2374
|
-
const jobs = [];
|
|
2375
|
-
if (!inst || inst.__vmzDestroyed || !field || !binders || !binders[field]) {
|
|
2376
|
-
return jobs;
|
|
2377
|
-
}
|
|
2378
|
-
// Snapshot: patches may untrack/retrack and mutate the binder list mid-loop.
|
|
2379
|
-
const list = binders[field].slice();
|
|
2380
|
-
for (const fn of list) {
|
|
2381
|
-
try {
|
|
2382
|
-
const ret = runPatch(inst, fn, field, null);
|
|
2383
|
-
if (ret && typeof ret.then === 'function')
|
|
2384
|
-
jobs.push(ret);
|
|
2385
|
-
}
|
|
2386
|
-
catch (err) {
|
|
2387
|
-
console.error('vmz:dom patch', err);
|
|
2388
|
-
}
|
|
2389
|
-
}
|
|
2390
|
-
return jobs;
|
|
2391
|
-
}
|
|
2392
|
-
/**
|
|
2393
|
-
* Run `__vmzBinders` patches that are not owned by a BindingId entry.
|
|
2394
|
-
* Needed so `bindComponentProp` (bindingId null) still flushes when the same
|
|
2395
|
-
* dep also has IR bindText/bindAttr BindingIds.
|
|
2396
|
-
*/
|
|
2397
|
-
function refreshFieldBinderOnly(inst, field) {
|
|
2398
|
-
const binders = inst.__vmzBinders;
|
|
2399
|
-
const jobs = [];
|
|
2400
|
-
if (!inst || inst.__vmzDestroyed || !field || !binders || !binders[field]) {
|
|
2401
|
-
return jobs;
|
|
2402
|
-
}
|
|
2403
|
-
const list = binders[field].slice();
|
|
2404
|
-
for (const fn of list) {
|
|
2405
|
-
if (patchHasBindingId(inst, fn))
|
|
2406
|
-
continue;
|
|
2407
|
-
try {
|
|
2408
|
-
const ret = runPatch(inst, fn, field, null);
|
|
2409
|
-
if (ret && typeof ret.then === 'function')
|
|
2410
|
-
jobs.push(ret);
|
|
2411
|
-
}
|
|
2412
|
-
catch (err) {
|
|
2413
|
-
console.error('vmz:dom patch', err);
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
return jobs;
|
|
2417
|
-
}
|
|
2418
|
-
function reindexBindingDeps(inst, bindingId, deps) {
|
|
2419
|
-
if (!inst.__vmzDepToBindings)
|
|
2420
|
-
inst.__vmzDepToBindings = Object.create(null);
|
|
2421
|
-
const entry = inst.__vmzBindings[bindingId];
|
|
2422
|
-
if (!entry)
|
|
2423
|
-
return;
|
|
2424
|
-
for (const dep of entry.deps || []) {
|
|
2425
|
-
const list = inst.__vmzDepToBindings[dep];
|
|
2426
|
-
if (!list)
|
|
2427
|
-
continue;
|
|
2428
|
-
const j = list.indexOf(bindingId);
|
|
2429
|
-
if (j >= 0)
|
|
2430
|
-
list.splice(j, 1);
|
|
2431
|
-
if (list.length === 0)
|
|
2432
|
-
delete inst.__vmzDepToBindings[dep];
|
|
2433
|
-
}
|
|
2434
|
-
entry.deps = [...(deps || [])];
|
|
2435
|
-
for (const dep of entry.deps) {
|
|
2436
|
-
if (!inst.__vmzDepToBindings[dep])
|
|
2437
|
-
inst.__vmzDepToBindings[dep] = [];
|
|
2438
|
-
if (!inst.__vmzDepToBindings[dep].includes(bindingId)) {
|
|
2439
|
-
inst.__vmzDepToBindings[dep].push(bindingId);
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
}
|
|
2443
|
-
function registerBind(inst, deps, fn, bindingId = null) {
|
|
2444
|
-
if (!inst.__vmzBinders)
|
|
2445
|
-
inst.__vmzBinders = Object.create(null);
|
|
2446
|
-
for (const dep of deps || []) {
|
|
2447
|
-
if (!inst.__vmzBinders[dep])
|
|
2448
|
-
inst.__vmzBinders[dep] = [];
|
|
2449
|
-
// Dedup: cf untrack/retrack with identical deps must not grow the binder list.
|
|
2450
|
-
if (!inst.__vmzBinders[dep].includes(fn))
|
|
2451
|
-
inst.__vmzBinders[dep].push(fn);
|
|
2452
|
-
}
|
|
2453
|
-
if (bindingId == null)
|
|
2454
|
-
return;
|
|
2455
|
-
if (!inst.__vmzBindings)
|
|
2456
|
-
inst.__vmzBindings = Object.create(null);
|
|
2457
|
-
let entry = inst.__vmzBindings[bindingId];
|
|
2458
|
-
if (!entry) {
|
|
2459
|
-
entry = { id: bindingId, deps: [], patches: [] };
|
|
2460
|
-
inst.__vmzBindings[bindingId] = entry;
|
|
2461
|
-
}
|
|
2462
|
-
if (!entry.patches.includes(fn))
|
|
2463
|
-
entry.patches.push(fn);
|
|
2464
|
-
reindexBindingDeps(inst, bindingId, deps || []);
|
|
2465
|
-
}
|
|
2466
|
-
function unregisterBind(inst, deps, fn, bindingId = null) {
|
|
2467
|
-
const binders = inst.__vmzBinders;
|
|
2468
|
-
if (binders) {
|
|
2469
|
-
for (const dep of deps || []) {
|
|
2470
|
-
const list = binders[dep];
|
|
2471
|
-
if (!list)
|
|
2472
|
-
continue;
|
|
2473
|
-
const i = list.indexOf(fn);
|
|
2474
|
-
if (i >= 0)
|
|
2475
|
-
list.splice(i, 1);
|
|
2476
|
-
if (list.length === 0)
|
|
2477
|
-
delete binders[dep];
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
if (bindingId == null || !inst.__vmzBindings)
|
|
2481
|
-
return;
|
|
2482
|
-
const entry = inst.__vmzBindings[bindingId];
|
|
2483
|
-
if (!entry)
|
|
2484
|
-
return;
|
|
2485
|
-
const i = entry.patches.indexOf(fn);
|
|
2486
|
-
if (i >= 0)
|
|
2487
|
-
entry.patches.splice(i, 1);
|
|
2488
|
-
if (entry.patches.length === 0) {
|
|
2489
|
-
reindexBindingDeps(inst, bindingId, []);
|
|
2490
|
-
delete inst.__vmzBindings[bindingId];
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
|
-
/**
|
|
2494
|
-
* True when the container already has meaningful DOM (SSR / resume shell).
|
|
2495
|
-
*/
|
|
2496
|
-
export function hasMeaningfulChild(el) {
|
|
2497
|
-
for (const n of el.childNodes) {
|
|
2498
|
-
if (n.nodeType === 1)
|
|
2499
|
-
return true;
|
|
2500
|
-
if (n.nodeType === 3 && String(n.textContent).trim() !== '')
|
|
2501
|
-
return true;
|
|
2502
|
-
}
|
|
2503
|
-
return false;
|
|
2504
|
-
}
|
|
2505
|
-
function patchHasBindingId(inst, fn) {
|
|
2506
|
-
const bindings = inst && inst.__vmzBindings;
|
|
2507
|
-
if (!bindings)
|
|
2508
|
-
return false;
|
|
2509
|
-
for (const id of Object.keys(bindings)) {
|
|
2510
|
-
const patches = bindings[id].patches;
|
|
2511
|
-
if (patches && patches.includes(fn))
|
|
2512
|
-
return true;
|
|
2513
|
-
}
|
|
2514
|
-
return false;
|
|
2515
|
-
}
|
|
2516
|
-
function tagItemPatches(patches, index) {
|
|
2517
|
-
if (!patches)
|
|
2518
|
-
return;
|
|
2519
|
-
const idx = String(index);
|
|
2520
|
-
for (const p of patches) {
|
|
2521
|
-
if (typeof p === 'function')
|
|
2522
|
-
p.__vmzItemIndex = idx;
|
|
2523
|
-
}
|
|
2524
|
-
}
|
|
5
|
+
export { __vmzPrecisionEnable } from './diagnostics/precision-trace.js';
|
|
6
|
+
export { __vmzTraceEnable } from './diagnostics/precision-trace.js';
|
|
7
|
+
export { __vmzPrecisionReset } from './diagnostics/precision-trace.js';
|
|
8
|
+
export { __vmzTraceReset } from './diagnostics/precision-trace.js';
|
|
9
|
+
export { __vmzTraceSnapshot } from './diagnostics/precision-trace.js';
|
|
10
|
+
export { __vmzPrecisionSnapshot } from './diagnostics/precision-trace.js';
|
|
11
|
+
export { registerComponents } from './dom/registry.js';
|
|
12
|
+
export { getRegisteredComponent } from './dom/registry.js';
|
|
13
|
+
export { resolveComponent } from './dom/registry.js';
|
|
14
|
+
export { mount } from './dom/component.js';
|
|
15
|
+
export { settlePendingChildMounts } from './dom/component.js';
|
|
16
|
+
export { runDirectCreate } from './dom/component.js';
|
|
17
|
+
export { findOwnedDefaultSlot } from './dom/component.js';
|
|
18
|
+
export { findOwnedDefaultSlotTarget } from './dom/component.js';
|
|
19
|
+
export { createInstance } from './dom/component.js';
|
|
20
|
+
export { destroy } from './dom/component.js';
|
|
21
|
+
export { disposeDomTree } from './dom/component.js';
|
|
22
|
+
export { snapshotInstanceState } from './dom/component.js';
|
|
23
|
+
export { applyPreservedState } from './dom/component.js';
|
|
24
|
+
export { stripFns } from './dom/component.js';
|
|
25
|
+
export { BOOLEAN_HTML_ATTRS } from './dom/attributes.js';
|
|
26
|
+
export { applyDomAttr } from './dom/attributes.js';
|
|
27
|
+
export { eventPropHandlerName } from './dom/attributes.js';
|
|
28
|
+
export { emitComponentEvent } from './dom/attributes.js';
|
|
29
|
+
export { isEventPropName } from './dom/attributes.js';
|
|
30
|
+
export { noteDomCreate } from './dom/lifecycle.js';
|
|
31
|
+
export { scheduleClient } from './dom/lifecycle.js';
|
|
32
|
+
export { isEventEntryStrategy } from './dom/lifecycle.js';
|
|
33
|
+
export { scheduleClientOn } from './dom/lifecycle.js';
|
|
34
|
+
export { __vmzRunTask } from './dom/lifecycle.js';
|
|
35
|
+
export { __vmzCancelTasks } from './dom/lifecycle.js';
|
|
36
|
+
export { __vmzTaskStatus } from './dom/lifecycle.js';
|
|
37
|
+
export { directApi } from './dom/direct-api.js';
|
|
38
|
+
export { __vmzAllowShared } from './reactivity/write-barrier.js';
|
|
39
|
+
export { __vmzTakeShared } from './reactivity/write-barrier.js';
|
|
40
|
+
export { __vmzSharedCrossComponentDiagnostics } from './reactivity/write-barrier.js';
|
|
41
|
+
export { __vmzSharedCrossComponentDiagnosticsReset } from './reactivity/write-barrier.js';
|
|
42
|
+
export { __vmzReadPath } from './reactivity/write-barrier.js';
|
|
43
|
+
export { __vmzWritePathLogical } from './reactivity/write-barrier.js';
|
|
44
|
+
export { __vmzWritePath } from './reactivity/write-barrier.js';
|
|
45
|
+
export { __vmzWritePathItem } from './reactivity/write-barrier.js';
|
|
46
|
+
export { __vmzListTranspose } from './reactivity/write-barrier.js';
|
|
47
|
+
export { __vmzWritePathCompound } from './reactivity/write-barrier.js';
|
|
48
|
+
export { __vmzWritePathCompoundItem } from './reactivity/write-barrier.js';
|
|
49
|
+
export { __vmzArrayItemCompoundStride } from './reactivity/write-barrier.js';
|
|
50
|
+
export { __vmzArrayMutate } from './reactivity/write-barrier.js';
|
|
51
|
+
export { __vmzIsWriteBarrierOwned } from './reactivity/write-barrier.js';
|
|
52
|
+
export { __vmzIsReactiveProxy } from './reactivity/write-barrier.js';
|
|
53
|
+
export { flushPending } from './reactivity/patch-scheduler.js';
|
|
54
|
+
export { hasMeaningfulChild } from './reactivity/patch-scheduler.js';
|