@vmz/core 0.2.4 → 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.
Files changed (77) hide show
  1. package/dist/browser/client-nav.d.ts +5 -55
  2. package/dist/browser/client-nav.js +5 -769
  3. package/dist/browser/diagnostics/precision-trace.d.ts +47 -0
  4. package/dist/browser/diagnostics/precision-trace.js +86 -0
  5. package/dist/browser/dom/attributes.d.ts +5 -0
  6. package/dist/browser/dom/attributes.js +102 -0
  7. package/dist/browser/dom/component.d.ts +11 -0
  8. package/dist/browser/dom/component.js +255 -0
  9. package/dist/browser/dom/direct-api.d.ts +66 -0
  10. package/dist/browser/dom/direct-api.js +446 -0
  11. package/dist/browser/dom/lifecycle.d.ts +9 -0
  12. package/dist/browser/dom/lifecycle.js +161 -0
  13. package/dist/browser/dom/registry.d.ts +5 -0
  14. package/dist/browser/dom/registry.js +16 -0
  15. package/dist/browser/dom-core.d.ts +51 -249
  16. package/dist/browser/dom-core.js +51 -2521
  17. package/dist/browser/navigation/client-navigation.d.ts +44 -0
  18. package/dist/browser/navigation/client-navigation.js +277 -0
  19. package/dist/browser/navigation/extract-html.d.ts +1 -0
  20. package/dist/browser/navigation/extract-html.js +5 -0
  21. package/dist/browser/navigation/layout-retain.d.ts +3 -0
  22. package/dist/browser/navigation/layout-retain.js +34 -0
  23. package/dist/browser/navigation/locale-transition.d.ts +24 -0
  24. package/dist/browser/navigation/locale-transition.js +323 -0
  25. package/dist/browser/navigation/scroll-focus.d.ts +14 -0
  26. package/dist/browser/navigation/scroll-focus.js +87 -0
  27. package/dist/browser/reactivity/patch-scheduler.d.ts +12 -0
  28. package/dist/browser/reactivity/patch-scheduler.js +720 -0
  29. package/dist/browser/reactivity/write-barrier.d.ts +16 -0
  30. package/dist/browser/reactivity/write-barrier.js +546 -0
  31. package/dist/browser/resume/adopt.d.ts +1 -0
  32. package/dist/browser/resume/adopt.js +168 -0
  33. package/dist/browser/resume/hydrate.d.ts +4 -0
  34. package/dist/browser/resume/hydrate.js +107 -0
  35. package/dist/browser/resume/resume.d.ts +3 -0
  36. package/dist/browser/resume/resume.js +102 -0
  37. package/dist/faces/dom.browser.d.ts +2 -1
  38. package/dist/faces/dom.browser.js +2 -1
  39. package/dist/host/{deployment-registry.d.ts → deployment/deployment-registry.d.ts} +1 -1
  40. package/dist/host/{deployment-registry.js → deployment/deployment-registry.js} +1 -1
  41. package/dist/host/list-client-components.d.ts +2 -2
  42. package/dist/host/list-client-components.js +4 -4
  43. package/dist/host/{render-host.d.ts → render/render-host.d.ts} +2 -2
  44. package/dist/host/{render-host.js → render/render-host.js} +1 -1
  45. package/dist/host/{route-layout-chain.js → render/route-layout-chain.js} +1 -1
  46. package/dist/host/serve/bootstrap.d.ts +1 -0
  47. package/dist/host/serve/bootstrap.js +25 -0
  48. package/dist/host/serve/constants.d.ts +6 -0
  49. package/dist/host/serve/constants.js +6 -0
  50. package/dist/host/serve/hmr-dev.d.ts +34 -0
  51. package/dist/host/serve/hmr-dev.js +294 -0
  52. package/dist/host/serve/http.d.ts +1 -0
  53. package/dist/host/serve/http.js +124 -0
  54. package/dist/host/serve/locale-theme.d.ts +19 -0
  55. package/dist/host/serve/locale-theme.js +169 -0
  56. package/dist/host/serve/page-catalog.d.ts +14 -0
  57. package/dist/host/serve/page-catalog.js +105 -0
  58. package/dist/host/serve/page-render.d.ts +17 -0
  59. package/dist/host/serve/page-render.js +419 -0
  60. package/dist/host/serve/paths.d.ts +3 -0
  61. package/dist/host/serve/paths.js +74 -0
  62. package/dist/host/serve/route-catalog.d.ts +6 -0
  63. package/dist/host/serve/route-catalog.js +108 -0
  64. package/dist/host/serve/state.d.ts +30 -0
  65. package/dist/host/serve/state.js +36 -0
  66. package/dist/host/serve-host.d.ts +0 -17
  67. package/dist/host/serve-host.mjs +4 -1420
  68. package/dist/ssr/document.d.ts +2 -0
  69. package/dist/ssr/document.js +71 -0
  70. package/dist/ssr/dom-ssr.d.ts +10 -47
  71. package/dist/ssr/dom-ssr.js +10 -1284
  72. package/dist/ssr/render.d.ts +2 -0
  73. package/dist/ssr/render.js +78 -0
  74. package/dist/ssr/serialize.d.ts +3 -0
  75. package/dist/ssr/serialize.js +678 -0
  76. package/package.json +11 -11
  77. /package/dist/host/{route-layout-chain.d.ts → render/route-layout-chain.d.ts} +0 -0
@@ -0,0 +1,446 @@
1
+ import { mergeClassParts, mergeStyleParts } from '../../shared/dom-attr-normalize.js';
2
+ import { applyDirectHostBox } from '../direct-host-box.js';
3
+ import { createUnknownComponentElement } from '../unknown-component.js';
4
+ import { applyDomAttr, eventPropHandlerName } from './attributes.js';
5
+ import { createInstance, disposeDomTree, findOwnedDefaultSlotTarget, runDirectCreate, stripFns } from './component.js';
6
+ import { isEventEntryStrategy, noteDomCreate, noteDomRemove, scheduleClientOn } from './lifecycle.js';
7
+ import { components, resolveComponent } from './registry.js';
8
+ import { beginEventFlush, endEventFlush, inferHandlerMethod, methodAllowsSyncEventFlush, registerBind, runDomEventHandler, runPatch, scheduleRefresh, unregisterBind, } from '../reactivity/patch-scheduler.js';
9
+ let directPropBindSeq = 0;
10
+ export const directApi = {
11
+ _inst: null,
12
+ _branchBinds: null,
13
+ _itemPatches: null,
14
+ _eachCtx: null,
15
+ _resumeAdopt: null,
16
+ el(tag) {
17
+ if (directApi._resumeAdopt)
18
+ return directApi._resumeAdopt.el(tag);
19
+ noteDomCreate();
20
+ return document.createElement(tag || 'div');
21
+ },
22
+ elNS(ns, tag) {
23
+ if (directApi._resumeAdopt && typeof directApi._resumeAdopt.elNS === 'function') {
24
+ return directApi._resumeAdopt.elNS(ns, tag);
25
+ }
26
+ noteDomCreate();
27
+ return document.createElementNS(ns || 'http://www.w3.org/2000/svg', tag || 'g');
28
+ },
29
+ text(value) {
30
+ if (directApi._resumeAdopt)
31
+ return directApi._resumeAdopt.text(value);
32
+ noteDomCreate();
33
+ return document.createTextNode(value == null ? '' : String(value));
34
+ },
35
+ adoptEnter(node) {
36
+ const adopt = directApi._resumeAdopt;
37
+ if (adopt && typeof adopt.enter === 'function') {
38
+ const ok = adopt.enter(node);
39
+ if (ok)
40
+ adopt._enterBalance = (adopt._enterBalance || 0) + 1;
41
+ return ok;
42
+ }
43
+ return false;
44
+ },
45
+ adoptLeave() {
46
+ const adopt = directApi._resumeAdopt;
47
+ // Emit always pairs leave after enter; if enter failed (fresh node, no
48
+ // pool), do not pop ??unbalanced leave previously stole parent scopes
49
+ // and projected siblings into nested Button slots.
50
+ if (!adopt || typeof adopt.leave !== 'function')
51
+ return;
52
+ if ((adopt._enterBalance || 0) <= 0)
53
+ return;
54
+ adopt._enterBalance -= 1;
55
+ adopt.leave();
56
+ },
57
+ frag() {
58
+ noteDomCreate();
59
+ return document.createDocumentFragment();
60
+ },
61
+ comment(value) {
62
+ noteDomCreate();
63
+ return document.createComment(value == null ? '' : String(value));
64
+ },
65
+ insertBefore(parent, node, ref) {
66
+ if (!parent || node == null)
67
+ return;
68
+ parent.insertBefore(node, ref);
69
+ },
70
+ removeNode(node) {
71
+ if (!node || !node.parentNode)
72
+ return;
73
+ noteDomRemove();
74
+ node.remove();
75
+ },
76
+ /**
77
+ * Register a generated per-binding patch (0.2.0). No generic get/cf interpreter.
78
+ */
79
+ trackPatch(inst, deps, patch, bindingId = null) {
80
+ if (typeof patch !== 'function')
81
+ return;
82
+ if (directApi._branchBinds) {
83
+ directApi._branchBinds.push({ deps: deps || [], fn: patch, bindingId });
84
+ try {
85
+ patch.call(inst);
86
+ }
87
+ catch (err) {
88
+ console.error('vmz:dom trackPatch branch', err);
89
+ }
90
+ return;
91
+ }
92
+ if (directApi._itemPatches) {
93
+ patch.__vmzItemLocal = true;
94
+ if (!directApi._itemPatches.includes(patch)) {
95
+ directApi._itemPatches.push(patch);
96
+ }
97
+ if (directApi._eachCtx) {
98
+ directApi._eachCtx.noteItemBind(bindingId, deps || [], patch);
99
+ }
100
+ // Also register on the instance so outer field writes (`selected` in
101
+ // `selected === item.id`) wake item patches — keyed-each only re-runs
102
+ // create/apply on list deps, not host fields referenced inside items.
103
+ registerBind(inst, deps || [], patch, bindingId);
104
+ try {
105
+ patch.call(inst);
106
+ }
107
+ catch (err) {
108
+ console.error('vmz:dom trackPatch item', err);
109
+ }
110
+ return;
111
+ }
112
+ registerBind(inst, deps || [], patch, bindingId);
113
+ try {
114
+ runPatch(inst, patch, (deps && deps[0]) || null, bindingId ?? null);
115
+ }
116
+ catch (err) {
117
+ console.error('vmz:dom trackPatch', err);
118
+ }
119
+ },
120
+ untrackPatch(inst, deps, patch, bindingId = null) {
121
+ if (typeof patch !== 'function')
122
+ return;
123
+ unregisterBind(inst, deps || [], patch, bindingId);
124
+ },
125
+ disposeTree(root) {
126
+ disposeDomTree(root);
127
+ },
128
+ attr(el, name, value) {
129
+ applyDomAttr(el, name, value);
130
+ },
131
+ mergeClass(...parts) {
132
+ return mergeClassParts(...parts);
133
+ },
134
+ mergeStyle(...parts) {
135
+ return mergeStyleParts(...parts);
136
+ },
137
+ on(el, type, handler) {
138
+ const inst = directApi._inst;
139
+ if (directApi._eachCtx && typeof handler === 'function') {
140
+ const bag = el.__vmzEvt || (el.__vmzEvt = Object.create(null));
141
+ const methodHint = inferHandlerMethod(handler);
142
+ const listener = (ev) => {
143
+ if (type === 'submit' && ev && typeof ev.preventDefault === 'function') {
144
+ ev.preventDefault();
145
+ }
146
+ if (methodHint && methodAllowsSyncEventFlush(inst, methodHint)) {
147
+ runDomEventHandler(inst, methodHint, () => {
148
+ const m = inst[methodHint];
149
+ if (typeof m === 'function')
150
+ return m.call(inst, ev);
151
+ return handler.call(inst, ev);
152
+ });
153
+ return;
154
+ }
155
+ runDomEventHandler(inst, methodHint, () => handler.call(inst, ev));
156
+ };
157
+ bag[type] = listener;
158
+ directApi._eachCtx.needDelegate(type);
159
+ el.addEventListener(type, listener);
160
+ return;
161
+ }
162
+ // Infer once at bind time ??never Function#toString on the click hot path.
163
+ const methodHint = typeof handler === 'function' ? inferHandlerMethod(handler) : null;
164
+ if (methodHint && methodAllowsSyncEventFlush(inst, methodHint)) {
165
+ // Direct method bind: skip arrow wrapper + nested handler.call.
166
+ el.addEventListener(type, (ev) => {
167
+ if (type === 'submit' && ev && typeof ev.preventDefault === 'function') {
168
+ ev.preventDefault();
169
+ }
170
+ runDomEventHandler(inst, methodHint, () => {
171
+ const m = inst[methodHint];
172
+ if (typeof m === 'function')
173
+ return m.call(inst, ev);
174
+ return handler.call(inst, ev);
175
+ });
176
+ });
177
+ return;
178
+ }
179
+ el.addEventListener(type, (ev) => {
180
+ // Belt-and-suspenders: form submit must not navigate before handler runs.
181
+ if (type === 'submit' && ev && typeof ev.preventDefault === 'function') {
182
+ ev.preventDefault();
183
+ }
184
+ if (typeof handler === 'function') {
185
+ runDomEventHandler(inst, methodHint, () => handler.call(inst, ev));
186
+ }
187
+ });
188
+ },
189
+ /**
190
+ * Bind a named instance method (no arrow / Function#toString).
191
+ * Learns `skipFlush` after a sync invocation that schedules no dirty work
192
+ * (stride / transpose self-apply DOM).
193
+ */
194
+ onMethod(el, type, methodName, opts) {
195
+ // Prefer each-block owner: item create often runs during flush when `_inst` is unset.
196
+ const eachOwner = directApi._eachCtx && directApi._eachCtx.inst;
197
+ const inst = eachOwner || directApi._inst;
198
+ if (!inst)
199
+ return;
200
+ let skipFlush = !!(opts && opts.skipFlush);
201
+ const invoke = (ev) => {
202
+ if (type === 'submit' && ev && typeof ev.preventDefault === 'function') {
203
+ ev.preventDefault();
204
+ }
205
+ const m = inst[methodName];
206
+ if (typeof m !== 'function')
207
+ return;
208
+ if (skipFlush) {
209
+ m.call(inst, ev);
210
+ return;
211
+ }
212
+ beginEventFlush(inst);
213
+ try {
214
+ m.call(inst, ev);
215
+ }
216
+ finally {
217
+ const scheduled = !!inst.__vmzFlushScheduled;
218
+ endEventFlush(inst);
219
+ // Barrier-owned methods (stride/transpose) never schedule ??skip frame next time.
220
+ if (!scheduled && methodAllowsSyncEventFlush(inst, methodName)) {
221
+ skipFlush = true;
222
+ }
223
+ }
224
+ };
225
+ if (directApi._eachCtx) {
226
+ const bag = el.__vmzEvt || (el.__vmzEvt = Object.create(null));
227
+ const listener = (ev) => invoke(ev);
228
+ bag[type] = listener;
229
+ directApi._eachCtx.needDelegate(type);
230
+ el.addEventListener(type, listener);
231
+ return;
232
+ }
233
+ el.addEventListener(type, (ev) => invoke(ev));
234
+ },
235
+ /**
236
+ * Specialized single-field attr bind (0.1.29): compile-time field name, no generic get closure in artifact.
237
+ */
238
+ specFieldAttr(inst, bindingId, fieldName, el, name) {
239
+ directApi.trackPatch(inst, [fieldName], function specFieldAttrPatch() {
240
+ const raw = this[fieldName];
241
+ if (name === 'class' || name === 'className') {
242
+ const s = mergeClassParts(raw);
243
+ if (s)
244
+ el.setAttribute('class', s);
245
+ else if (el.hasAttribute('class'))
246
+ el.removeAttribute('class');
247
+ }
248
+ else if (name === 'style') {
249
+ const s = mergeStyleParts(raw);
250
+ if (s)
251
+ el.setAttribute('style', s);
252
+ else if (el.hasAttribute('style'))
253
+ el.removeAttribute('style');
254
+ }
255
+ else {
256
+ applyDomAttr(el, name, raw);
257
+ }
258
+ }, bindingId);
259
+ },
260
+ /**
261
+ * Specialized single-field text bind (0.1.29): compile-time field name in generated artifact.
262
+ */
263
+ specFieldText(inst, bindingId, fieldName, textNode) {
264
+ directApi.trackPatch(inst, [fieldName], function specFieldTextPatch() {
265
+ textNode.textContent = String(this[fieldName] ?? '');
266
+ }, bindingId);
267
+ },
268
+ setHtml(el, value) {
269
+ el.innerHTML = value == null ? '' : String(value);
270
+ },
271
+ bindHtml(inst, bindingId, deps, get, el) {
272
+ directApi.trackPatch(inst, deps || [], function bindHtmlPatch() {
273
+ let raw;
274
+ try {
275
+ raw = get.call(inst);
276
+ }
277
+ catch {
278
+ raw = '';
279
+ }
280
+ el.innerHTML = raw == null ? '' : String(raw);
281
+ }, bindingId);
282
+ },
283
+ /**
284
+ * Nested component (sync Direct child or island schedule).
285
+ */
286
+ component(hostInst, nameOrCtor, props, client) {
287
+ const name = typeof nameOrCtor === 'function' ? nameOrCtor.__vmzTag || nameOrCtor.name || 'Component' : nameOrCtor;
288
+ let host = null;
289
+ if (directApi._resumeAdopt && typeof directApi._resumeAdopt.componentHost === 'function') {
290
+ host = directApi._resumeAdopt.componentHost(name);
291
+ }
292
+ if (!host) {
293
+ noteDomCreate();
294
+ host = document.createElement('div');
295
+ host.setAttribute('data-vmz', name);
296
+ }
297
+ const resolved = {};
298
+ for (const [k, v] of Object.entries(props || {})) {
299
+ // Function props that already look like `onXxx` stay as handlers.
300
+ // Component `@event` never arrives here (emit uses onComponentEvent).
301
+ const onKey = typeof v === 'function' ? eventPropHandlerName(k) : null;
302
+ if (onKey)
303
+ resolved[onKey] = v;
304
+ else if (typeof v === 'function')
305
+ resolved[k] = v.call(hostInst);
306
+ else
307
+ resolved[k] = v;
308
+ }
309
+ if (client) {
310
+ host.setAttribute('data-vmz-island', name);
311
+ host.setAttribute('data-vmz-client', String(client));
312
+ host.setAttribute('data-vmz-props', JSON.stringify(stripFns(resolved)));
313
+ if (isEventEntryStrategy(String(client))) {
314
+ host.setAttribute('data-vmz-entry', 'event');
315
+ }
316
+ // resume: resume on schedule; EventEntry may lazy-load chunk via __vmzLoadComponent.
317
+ scheduleClientOn(host, String(client), async () => {
318
+ const Ctor = typeof nameOrCtor === 'function' ? nameOrCtor : await resolveComponent(name);
319
+ if (!Ctor) {
320
+ // Replace placeholder island with leaf error node (do not throw page).
321
+ const err = createUnknownComponentElement(name, 'island');
322
+ host.replaceWith(err);
323
+ return;
324
+ }
325
+ const { resume } = await import('../../ssr/dom-ssr.js');
326
+ await resume(Ctor, host, { props: resolved, state: {} });
327
+ });
328
+ return host;
329
+ }
330
+ // Static Ctor import path: use Function directly (no registry lookup).
331
+ const Ctor = typeof nameOrCtor === 'function' ? nameOrCtor : components[name];
332
+ if (!Ctor) {
333
+ return createUnknownComponentElement(name, 'client');
334
+ }
335
+ // Inline chips ??`display: contents` (`ui-direct-host-box`). Block surfaces
336
+ // keep a real box (DataTable select timed out when defaulting everything).
337
+ applyDirectHostBox(host, name, Ctor);
338
+ const child = createInstance(Ctor, resolved);
339
+ if (!(Ctor.__vmzDirect && typeof Ctor.__vmzCreate === 'function')) {
340
+ throw new Error(`vmz:dom direct component <${name}> requires __vmzCreate (rebuild child with Direct)`);
341
+ }
342
+ // Nested resume: keep parent `_resumeAdopt` so child reclaim parked SSR nodes.
343
+ // Enter the host's private child pool so nested create cannot see uncle siblings.
344
+ const adopt = directApi._resumeAdopt;
345
+ const entered = adopt && typeof adopt.enter === 'function' ? adopt.enter(host) : false;
346
+ let node;
347
+ try {
348
+ node = runDirectCreate(Ctor, child);
349
+ }
350
+ finally {
351
+ if (entered && adopt && typeof adopt.leave === 'function')
352
+ adopt.leave();
353
+ }
354
+ if (node) {
355
+ // ifBlock/eachBlock roots are DocumentFragments: appendChild moves
356
+ // children into `host` and empties the fragment. Keep a live Element
357
+ // root so projectDefaultSlot can find `<slot>` (Button v-if/v-else).
358
+ child.__vmzDomRoot = node.nodeType === 11 ? host : node;
359
+ host.appendChild(node);
360
+ }
361
+ host.__vmzInst = child;
362
+ if (typeof child.onMount === 'function') {
363
+ const pending = Promise.resolve().then(() => {
364
+ if (!child.__vmzDestroyed)
365
+ return child.onMount();
366
+ });
367
+ const bag = hostInst.__vmzPendingChildMounts || (hostInst.__vmzPendingChildMounts = []);
368
+ bag.push(pending);
369
+ }
370
+ return host;
371
+ },
372
+ /**
373
+ * Subscribe to a child component event (`@submit` ??event name `submit`).
374
+ * Orthogonal to function props (`:on-submit` ??prop `onSubmit`).
375
+ */
376
+ onComponentEvent(hostEl, eventName, handler) {
377
+ const child = hostEl && hostEl.__vmzInst;
378
+ if (!child || typeof eventName !== 'string' || !eventName)
379
+ return;
380
+ if (typeof handler !== 'function')
381
+ return;
382
+ const bag = child.__vmzComponentListeners || (child.__vmzComponentListeners = Object.create(null));
383
+ const list = bag[eventName] || (bag[eventName] = []);
384
+ list.push(handler);
385
+ },
386
+ /**
387
+ * Keep nested Direct child props live with parent field writes.
388
+ */
389
+ bindComponentProp(hostInst, hostEl, propName, deps, get) {
390
+ if (hostEl && hostEl.__vmzPropBindSeq == null) {
391
+ hostEl.__vmzPropBindSeq = ++directPropBindSeq;
392
+ }
393
+ const seq = hostEl && hostEl.__vmzPropBindSeq != null ? hostEl.__vmzPropBindSeq : ++directPropBindSeq;
394
+ const bindingId = `pc:${seq}:${propName}`;
395
+ directApi.trackPatch(hostInst, deps || [], function bindComponentPropPatch() {
396
+ let raw;
397
+ try {
398
+ raw = get.call(hostInst);
399
+ }
400
+ catch {
401
+ raw = null;
402
+ }
403
+ const child = hostEl && hostEl.__vmzInst;
404
+ if (!child || child.__vmzDestroyed)
405
+ return;
406
+ if (typeof propName !== 'string' || !propName || propName.startsWith('#'))
407
+ return;
408
+ child[propName] = raw;
409
+ if (typeof child.__vmzOnParentProp === 'function') {
410
+ try {
411
+ child.__vmzOnParentProp(propName, raw);
412
+ }
413
+ catch (err) {
414
+ console.error('vmz:dom __vmzOnParentProp', err);
415
+ }
416
+ }
417
+ scheduleRefresh(child, { type: 'replace', root: propName });
418
+ }, bindingId);
419
+ },
420
+ /**
421
+ * Project parent children into nested Direct component default `<slot>`.
422
+ * Uses {@link findOwnedDefaultSlot} so nested Button/Link slots are not stolen.
423
+ */
424
+ projectDefaultSlot(hostEl, node) {
425
+ if (!hostEl || node == null)
426
+ return;
427
+ const child = hostEl.__vmzInst;
428
+ let root = (child && child.__vmzDomRoot) || hostEl;
429
+ // Emptied DocumentFragment after append must not receive slot kids.
430
+ if (!root || root.nodeType !== 1)
431
+ root = hostEl;
432
+ const target = root && root.nodeType === 1 ? findOwnedDefaultSlotTarget(root) : null;
433
+ if (target && target.parentNode) {
434
+ if (String(target.tagName || '').toLowerCase() === 'slot') {
435
+ target.replaceWith(node);
436
+ return;
437
+ }
438
+ target.appendChild(node);
439
+ return;
440
+ }
441
+ if (root && root.nodeType === 1 && typeof root.appendChild === 'function')
442
+ root.appendChild(node);
443
+ else
444
+ hostEl.appendChild(node);
445
+ },
446
+ };
@@ -0,0 +1,9 @@
1
+ export declare function noteDomCreate(): void;
2
+ export declare function noteDomRemove(): void;
3
+ export declare function noteDomMove(): void;
4
+ export declare function scheduleClient(strategy: any, fn: any): void;
5
+ export declare function isEventEntryStrategy(strategy: any): boolean;
6
+ export declare function scheduleClientOn(el: any, strategy: any, fn: any): void;
7
+ export declare function __vmzRunTask(inst: any, key: any, fn: any): Promise<any>;
8
+ export declare function __vmzCancelTasks(inst: any): void;
9
+ export declare function __vmzTaskStatus(inst: any, key: any): any;
@@ -0,0 +1,161 @@
1
+ import { precision } from '../diagnostics/precision-trace.js';
2
+ export function noteDomCreate() {
3
+ if (precision.enabled)
4
+ precision.domCreates++;
5
+ }
6
+ export function noteDomRemove() {
7
+ if (precision.enabled)
8
+ precision.domRemoves++;
9
+ }
10
+ export function noteDomMove() {
11
+ if (precision.enabled)
12
+ precision.domMoves++;
13
+ }
14
+ export function scheduleClient(strategy, fn) {
15
+ scheduleClientOn(null, strategy, fn);
16
+ }
17
+ export function isEventEntryStrategy(strategy) {
18
+ const s = String(strategy || '');
19
+ return s === 'event' || s.startsWith('event:') || s === 'click';
20
+ }
21
+ function eventEntryType(strategy) {
22
+ const s = String(strategy || 'event');
23
+ if (s.startsWith('event:'))
24
+ return s.slice(6) || 'click';
25
+ if (s === 'click')
26
+ return 'click';
27
+ return 'click';
28
+ }
29
+ export function scheduleClientOn(el, strategy, fn) {
30
+ const run = () => {
31
+ Promise.resolve(fn()).catch((err) => console.error('vmz:dom island', err));
32
+ };
33
+ if (isEventEntryStrategy(strategy)) {
34
+ if (!el || typeof el.addEventListener !== 'function') {
35
+ run();
36
+ return;
37
+ }
38
+ const type = eventEntryType(strategy);
39
+ const once = () => {
40
+ el.removeEventListener(type, once);
41
+ run();
42
+ };
43
+ el.addEventListener(type, once);
44
+ return;
45
+ }
46
+ if (strategy === 'idle') {
47
+ if (typeof requestIdleCallback === 'function') {
48
+ requestIdleCallback(() => run(), { timeout: 2000 });
49
+ }
50
+ else {
51
+ setTimeout(run, 1);
52
+ }
53
+ return;
54
+ }
55
+ if (strategy === 'visible' && el && typeof IntersectionObserver === 'function') {
56
+ const io = new IntersectionObserver((entries) => {
57
+ if (entries.some((e) => e.isIntersecting)) {
58
+ io.disconnect();
59
+ run();
60
+ }
61
+ });
62
+ io.observe(el);
63
+ return;
64
+ }
65
+ run();
66
+ }
67
+ export function __vmzRunTask(inst, key, fn) {
68
+ if (!inst)
69
+ throw new Error('vmz:dom __vmzRunTask requires inst');
70
+ const k = String(key || 'default');
71
+ if (!inst.__vmzTasks)
72
+ inst.__vmzTasks = Object.create(null);
73
+ const prev = inst.__vmzTasks[k];
74
+ if (prev) {
75
+ prev.generation += 1;
76
+ try {
77
+ prev.controller.abort();
78
+ }
79
+ catch {
80
+ /* ignore */
81
+ }
82
+ prev.status = 'cancelled';
83
+ }
84
+ const controller = typeof AbortController !== 'undefined'
85
+ ? new AbortController()
86
+ : {
87
+ signal: { aborted: false },
88
+ abort() {
89
+ this.signal.aborted = true;
90
+ },
91
+ };
92
+ const generation = (prev?.generation || 0) + 1;
93
+ const entry = {
94
+ generation,
95
+ controller,
96
+ status: 'pending',
97
+ result: undefined,
98
+ error: undefined,
99
+ promise: undefined,
100
+ };
101
+ inst.__vmzTasks[k] = entry;
102
+ // Invoke synchronously so event handlers can call preventDefault before
103
+ // the browser continues the default action (form submit ??native navigation).
104
+ // Async work still continues via the returned Promise.
105
+ let syncResult;
106
+ let syncErr;
107
+ let threw = false;
108
+ try {
109
+ syncResult = fn(controller.signal, { generation });
110
+ }
111
+ catch (err) {
112
+ threw = true;
113
+ syncErr = err;
114
+ }
115
+ const settleOk = (result) => {
116
+ if (inst.__vmzDestroyed || controller.signal.aborted || inst.__vmzTasks[k] !== entry) {
117
+ entry.status = 'cancelled';
118
+ return undefined;
119
+ }
120
+ entry.status = 'success';
121
+ entry.result = result;
122
+ return result;
123
+ };
124
+ const settleErr = (err) => {
125
+ if (inst.__vmzDestroyed || controller.signal.aborted || inst.__vmzTasks[k] !== entry) {
126
+ entry.status = 'cancelled';
127
+ return undefined;
128
+ }
129
+ entry.status = 'error';
130
+ entry.error = err;
131
+ throw err;
132
+ };
133
+ if (threw) {
134
+ const promise = Promise.resolve().then(() => settleErr(syncErr));
135
+ entry.promise = promise;
136
+ return promise;
137
+ }
138
+ const promise = Promise.resolve(syncResult).then(settleOk, settleErr);
139
+ entry.promise = promise;
140
+ return promise;
141
+ }
142
+ export function __vmzCancelTasks(inst) {
143
+ const tasks = inst?.__vmzTasks;
144
+ if (!tasks)
145
+ return;
146
+ for (const key of Object.keys(tasks)) {
147
+ const t = tasks[key];
148
+ t.generation += 1;
149
+ try {
150
+ t.controller.abort();
151
+ }
152
+ catch {
153
+ /* ignore */
154
+ }
155
+ t.status = 'cancelled';
156
+ }
157
+ }
158
+ export function __vmzTaskStatus(inst, key) {
159
+ const t = inst?.__vmzTasks?.[String(key || 'default')];
160
+ return t ? t.status : null;
161
+ }
@@ -0,0 +1,5 @@
1
+ import type { ComponentCtor } from '../dom-core.types.js';
2
+ export declare const components: Record<string, ComponentCtor>;
3
+ export declare function registerComponents(map: any): void;
4
+ export declare function getRegisteredComponent(name: any): ComponentCtor;
5
+ export declare function resolveComponent(name: any): Promise<ComponentCtor>;
@@ -0,0 +1,16 @@
1
+ export const components = Object.create(null);
2
+ export function registerComponents(map) {
3
+ Object.assign(components, map);
4
+ }
5
+ export function getRegisteredComponent(name) {
6
+ return components[name] || null;
7
+ }
8
+ export async function resolveComponent(name) {
9
+ let Ctor = components[name];
10
+ if (!Ctor && typeof globalThis.__vmzLoadComponent === 'function') {
11
+ Ctor = (await globalThis.__vmzLoadComponent(name));
12
+ if (Ctor)
13
+ registerComponents({ [name]: Ctor });
14
+ }
15
+ return Ctor || null;
16
+ }