@vmz/core 0.1.32 → 0.2.1
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/{client-nav.d.ts → browser/client-nav.d.ts} +4 -13
- package/dist/{client-nav.js → browser/client-nav.js} +16 -74
- package/dist/browser/direct-api.types.d.ts +141 -0
- package/dist/browser/direct-api.types.js +4 -0
- package/dist/browser/direct-host-box.d.ts +11 -0
- package/dist/{direct-host-box.js → browser/direct-host-box.js} +2 -18
- package/dist/{dom-core.d.ts → browser/dom-core.d.ts} +67 -220
- package/dist/browser/dom-core.js +2484 -0
- package/dist/browser/dom-core.types.d.ts +127 -0
- package/dist/browser/dom-core.types.js +4 -0
- package/dist/{position-context.js → browser/position-context.js} +1 -1
- package/dist/browser/unknown-component.d.ts +14 -0
- package/dist/{unknown-component.js → browser/unknown-component.js} +5 -30
- package/dist/{dom.browser.d.ts → faces/dom.browser.d.ts} +2 -2
- package/dist/{dom.browser.js → faces/dom.browser.js} +2 -3
- package/dist/faces/dom.client.d.ts +4 -0
- package/dist/faces/dom.client.js +4 -0
- package/dist/faces/dom.d.ts +6 -0
- package/dist/faces/dom.js +6 -0
- package/dist/faces/http.d.ts +6 -0
- package/dist/{http.js → faces/http.js} +0 -1
- package/dist/faces/server.d.ts +30 -0
- package/dist/{server.js → faces/server.js} +27 -149
- package/dist/{vmz-dom.d.ts → faces/vmz-dom.d.ts} +1 -1
- package/dist/{vmz-dom.js → faces/vmz-dom.js} +1 -2
- package/dist/{vmz-runtime.d.ts → faces/vmz-runtime.d.ts} +1 -1
- package/dist/{vmz-runtime.js → faces/vmz-runtime.js} +1 -2
- package/dist/host/deployment-registry.d.ts +16 -0
- package/dist/{deployment-registry.js → host/deployment-registry.js} +1 -68
- package/dist/host/list-client-components.d.ts +14 -0
- package/dist/{list-client-components.js → host/list-client-components.js} +0 -37
- package/dist/{localize-body-links.js → host/localize-body-links.js} +0 -1
- package/dist/host/native-addon.d.ts +6 -0
- package/dist/{native-addon.js → host/native-addon.js} +0 -20
- package/dist/host/render-host.d.ts +20 -0
- package/dist/{render-host.js → host/render-host.js} +1 -17
- package/dist/{serve-host.d.ts → host/serve-host.d.ts} +3 -3
- package/dist/{serve-host.mjs → host/serve-host.mjs} +45 -168
- package/dist/shared/dom-attr-normalize.d.ts +8 -0
- package/dist/shared/dom-attr-normalize.js +71 -0
- package/dist/shared/host.types.d.ts +113 -0
- package/dist/shared/host.types.js +4 -0
- package/dist/shared/server.types.d.ts +67 -0
- package/dist/shared/server.types.js +4 -0
- package/dist/{dom-ssr.d.ts → ssr/dom-ssr.d.ts} +8 -37
- package/dist/{dom-ssr.js → ssr/dom-ssr.js} +189 -217
- package/package.json +34 -34
- package/dist/deployment-registry.d.ts +0 -70
- package/dist/direct-host-box.d.ts +0 -26
- package/dist/dom-core.js +0 -4815
- package/dist/dom.client.d.ts +0 -4
- package/dist/dom.client.js +0 -5
- package/dist/dom.d.ts +0 -6
- package/dist/dom.js +0 -7
- package/dist/http.d.ts +0 -6
- package/dist/list-client-components.d.ts +0 -62
- package/dist/native-addon.d.ts +0 -12
- package/dist/render-host.d.ts +0 -32
- package/dist/server.d.ts +0 -46
- package/dist/unknown-component.d.ts +0 -40
- /package/dist/{position-context.d.ts → browser/position-context.d.ts} +0 -0
- /package/dist/{index.d.ts → faces/index.d.ts} +0 -0
- /package/dist/{index.js → faces/index.js} +0 -0
- /package/dist/{localize-body-links.d.ts → host/localize-body-links.d.ts} +0 -0
- /package/dist/{route-layout-chain.d.ts → host/route-layout-chain.d.ts} +0 -0
- /package/dist/{route-layout-chain.js → host/route-layout-chain.js} +0 -0
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Browser client navigation
|
|
2
|
+
* Browser client navigation �?same-app `<Link>` SPA takeover.
|
|
3
3
|
* Zero-JS still works via real `<a href>`; with JS, intercept same-origin
|
|
4
4
|
* `a[data-vmz-route]` clicks, pushState, fetch SSR HTML, swap page (retain layout
|
|
5
5
|
* when `data-vmz-layout` is unchanged) or full `#app`, then hydrate.
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
|
-
* @param {{
|
|
9
8
|
* fetchImpl?: typeof fetch,
|
|
10
9
|
* document?: Document,
|
|
11
10
|
* history?: History,
|
|
@@ -17,7 +16,7 @@
|
|
|
17
16
|
* importPage?: (chunkId: string) => Promise<any>,
|
|
18
17
|
* }} [opts]
|
|
19
18
|
*/
|
|
20
|
-
export declare function installClientNavigation(opts?:
|
|
19
|
+
export declare function installClientNavigation(opts?: Record<string, any>): {
|
|
21
20
|
ok: boolean;
|
|
22
21
|
reason: string;
|
|
23
22
|
transitionTo?: undefined;
|
|
@@ -25,11 +24,7 @@ export declare function installClientNavigation(opts?: {}): {
|
|
|
25
24
|
dispose?: undefined;
|
|
26
25
|
} | {
|
|
27
26
|
ok: boolean;
|
|
28
|
-
transitionTo: (url: any, { replace, fromPop, softFail }?: {
|
|
29
|
-
replace?: boolean;
|
|
30
|
-
fromPop?: boolean;
|
|
31
|
-
softFail?: boolean;
|
|
32
|
-
}) => Promise<{
|
|
27
|
+
transitionTo: (url: any, { replace, fromPop, softFail }?: any) => Promise<{
|
|
33
28
|
ok: boolean;
|
|
34
29
|
reason: string;
|
|
35
30
|
href?: undefined;
|
|
@@ -48,7 +43,7 @@ export declare function installClientNavigation(opts?: {}): {
|
|
|
48
43
|
localeId: any;
|
|
49
44
|
reason?: undefined;
|
|
50
45
|
}>;
|
|
51
|
-
transitionLocale: (toLocale: any, opts?:
|
|
46
|
+
transitionLocale: (toLocale: any, opts?: any) => Promise<{
|
|
52
47
|
status: string;
|
|
53
48
|
fromLocale: any;
|
|
54
49
|
toLocale: any;
|
|
@@ -57,8 +52,4 @@ export declare function installClientNavigation(opts?: {}): {
|
|
|
57
52
|
dispose(): void;
|
|
58
53
|
reason?: undefined;
|
|
59
54
|
};
|
|
60
|
-
/**
|
|
61
|
-
* @param {string} html
|
|
62
|
-
* @param {Document} doc
|
|
63
|
-
*/
|
|
64
55
|
export declare function extractAppHtml(html: any, doc: any): any;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Browser client navigation
|
|
2
|
+
* Browser client navigation �?same-app `<Link>` SPA takeover.
|
|
3
3
|
* Zero-JS still works via real `<a href>`; with JS, intercept same-origin
|
|
4
4
|
* `a[data-vmz-route]` clicks, pushState, fetch SSR HTML, swap page (retain layout
|
|
5
5
|
* when `data-vmz-layout` is unchanged) or full `#app`, then hydrate.
|
|
6
6
|
*/
|
|
7
|
-
// @ts-nocheck
|
|
8
7
|
/**
|
|
9
|
-
* @param {{
|
|
10
8
|
* fetchImpl?: typeof fetch,
|
|
11
9
|
* document?: Document,
|
|
12
10
|
* history?: History,
|
|
@@ -24,7 +22,6 @@ export function installClientNavigation(opts = {}) {
|
|
|
24
22
|
const hist = opts.history || win?.history;
|
|
25
23
|
const loc = opts.location || win?.location;
|
|
26
24
|
const fetchImplDefault = opts.fetchImpl || (typeof fetch === 'function' ? fetch.bind(globalThis) : null);
|
|
27
|
-
/** @type {typeof fetch | null} */
|
|
28
25
|
let fetchImpl = fetchImplDefault;
|
|
29
26
|
if (!doc || !hist || !loc || !fetchImpl) {
|
|
30
27
|
return { ok: false, reason: 'missing document/history/fetch' };
|
|
@@ -42,10 +39,8 @@ export function installClientNavigation(opts = {}) {
|
|
|
42
39
|
catch {
|
|
43
40
|
/* ignore */
|
|
44
41
|
}
|
|
45
|
-
/** @type {AbortController | null} */
|
|
46
42
|
let inflight = null;
|
|
47
43
|
let navigating = false;
|
|
48
|
-
/** @type {Map<string, { x: number, y: number }>} */
|
|
49
44
|
const scrollPositions = new Map();
|
|
50
45
|
function destroyInst(inst) {
|
|
51
46
|
if (!inst)
|
|
@@ -70,10 +65,8 @@ export function installClientNavigation(opts = {}) {
|
|
|
70
65
|
});
|
|
71
66
|
}
|
|
72
67
|
/**
|
|
73
|
-
* Route Transition Plan: restore scroll on popstate; forward nav
|
|
68
|
+
* Route Transition Plan: restore scroll on popstate; forward nav �?hash or top.
|
|
74
69
|
* Re-applies across frames until scrollHeight can hold the saved Y (hydrate settle).
|
|
75
|
-
* @param {URL} target
|
|
76
|
-
* @param {boolean} fromPop
|
|
77
70
|
*/
|
|
78
71
|
async function restoreScroll(target, fromPop) {
|
|
79
72
|
if (!win)
|
|
@@ -103,7 +96,7 @@ export function installClientNavigation(opts = {}) {
|
|
|
103
96
|
break;
|
|
104
97
|
const docEl = doc.documentElement || doc.body;
|
|
105
98
|
const maxY = Math.max(0, (docEl?.scrollHeight || 0) - (win.innerHeight || 0));
|
|
106
|
-
// Document still short
|
|
99
|
+
// Document still short �?wait for hydrate/layout to grow scrollHeight.
|
|
107
100
|
if (maxY + 2 < saved.y) {
|
|
108
101
|
await frame();
|
|
109
102
|
continue;
|
|
@@ -128,8 +121,6 @@ export function installClientNavigation(opts = {}) {
|
|
|
128
121
|
}
|
|
129
122
|
/**
|
|
130
123
|
* Focus the primary page landmark after SPA swap (not a scattered runtime hook).
|
|
131
|
-
* @param {Element | null} root
|
|
132
|
-
* @param {URL} target
|
|
133
124
|
*/
|
|
134
125
|
function restoreFocus(root, target) {
|
|
135
126
|
if (!root || !doc)
|
|
@@ -147,7 +138,7 @@ export function installClientNavigation(opts = {}) {
|
|
|
147
138
|
el = root;
|
|
148
139
|
if (el === doc.body)
|
|
149
140
|
return null;
|
|
150
|
-
const focusable =
|
|
141
|
+
const focusable = el;
|
|
151
142
|
if (!focusable.hasAttribute('tabindex') && focusable.tabIndex < 0) {
|
|
152
143
|
focusable.setAttribute('tabindex', '-1');
|
|
153
144
|
}
|
|
@@ -155,13 +146,12 @@ export function installClientNavigation(opts = {}) {
|
|
|
155
146
|
focusable.focus({ preventScroll: true });
|
|
156
147
|
}
|
|
157
148
|
catch {
|
|
158
|
-
/* ignore
|
|
149
|
+
/* ignore �?never focus() without preventScroll (would steal restored scrollY) */
|
|
159
150
|
}
|
|
160
151
|
return focusable.getAttribute('data-vmz-focus') || focusable.tagName?.toLowerCase() || null;
|
|
161
152
|
}
|
|
162
153
|
/**
|
|
163
154
|
* Apply locale realization attributes from SSR `#app` onto `<html>`.
|
|
164
|
-
* @param {Element | null} root
|
|
165
155
|
*/
|
|
166
156
|
function applyLocaleRealization(root) {
|
|
167
157
|
if (!root || !doc?.documentElement)
|
|
@@ -196,7 +186,7 @@ export function installClientNavigation(opts = {}) {
|
|
|
196
186
|
signal,
|
|
197
187
|
});
|
|
198
188
|
if (!res.ok) {
|
|
199
|
-
// LocaleTransition uses softFail
|
|
189
|
+
// LocaleTransition uses softFail �?keep current surface (no full assign).
|
|
200
190
|
if (!fromPop && !softFail)
|
|
201
191
|
loc.assign(target.href);
|
|
202
192
|
return { ok: false, reason: `http ${res.status}` };
|
|
@@ -234,7 +224,6 @@ export function installClientNavigation(opts = {}) {
|
|
|
234
224
|
hist.pushState({ vmzClientNav: true }, '', target.href);
|
|
235
225
|
}
|
|
236
226
|
const importChunk = opts.importPage || (async (id) => (await import(/* @vite-ignore */ `/${id}.client.js`)).default);
|
|
237
|
-
/** @type {Element | null} */
|
|
238
227
|
let liveRoot = root;
|
|
239
228
|
let retainedLayout = false;
|
|
240
229
|
// Apply target LocaleId before hydrate/onMount so `#locales/*` and
|
|
@@ -283,7 +272,6 @@ export function installClientNavigation(opts = {}) {
|
|
|
283
272
|
liveRoot = fresh;
|
|
284
273
|
if (chunkId) {
|
|
285
274
|
const Page = await importChunk(chunkId);
|
|
286
|
-
/** @type {any[]} */
|
|
287
275
|
const layoutCtors = [];
|
|
288
276
|
for (const id of nextLayout) {
|
|
289
277
|
layoutCtors.push(await importChunk(id));
|
|
@@ -318,12 +306,12 @@ export function installClientNavigation(opts = {}) {
|
|
|
318
306
|
retainedLayout,
|
|
319
307
|
focusTarget,
|
|
320
308
|
localeId,
|
|
321
|
-
// Pending until restoreScroll finishes
|
|
309
|
+
// Pending until restoreScroll finishes �?never lie as "restored" early (gate race).
|
|
322
310
|
scrollMode: 'pending',
|
|
323
311
|
scrollY: null,
|
|
324
312
|
};
|
|
325
313
|
}
|
|
326
|
-
// Wait for hydrate/layout paint before applying scroll
|
|
314
|
+
// Wait for hydrate/layout paint before applying scroll �?otherwise scrollTo is clamped/reset.
|
|
327
315
|
if (win && typeof win.requestAnimationFrame === 'function') {
|
|
328
316
|
await new Promise((resolve) => {
|
|
329
317
|
win.requestAnimationFrame(() => win.requestAnimationFrame(resolve));
|
|
@@ -370,16 +358,12 @@ export function installClientNavigation(opts = {}) {
|
|
|
370
358
|
if (a.hasAttribute('download') || a.getAttribute('target') === '_blank')
|
|
371
359
|
return;
|
|
372
360
|
ev.preventDefault();
|
|
373
|
-
// Prefer frozen
|
|
361
|
+
// Prefer frozen RouteId�LocaleId href table (0.1.30); fall back only when table missing.
|
|
374
362
|
const realized = localizeClickHref(u.pathname + u.search + u.hash, a.getAttribute('data-vmz-route'));
|
|
375
363
|
void transitionTo(realized, {
|
|
376
364
|
replace: a.getAttribute('data-vmz-replace') === 'true',
|
|
377
365
|
});
|
|
378
366
|
}
|
|
379
|
-
/**
|
|
380
|
-
* @param {string} href
|
|
381
|
-
* @param {string | null} [routeId]
|
|
382
|
-
*/
|
|
383
367
|
function localizeClickHref(href, routeId) {
|
|
384
368
|
if (!doc?.documentElement)
|
|
385
369
|
return href;
|
|
@@ -430,11 +414,11 @@ export function installClientNavigation(opts = {}) {
|
|
|
430
414
|
if (!pathname)
|
|
431
415
|
pathname = '/';
|
|
432
416
|
const parts = pathname.split('/').filter(Boolean);
|
|
433
|
-
// Explicit locale prefix in href = intentional locale (switch/deep-link)
|
|
417
|
+
// Explicit locale prefix in href = intentional locale (switch/deep-link) �?do not rewrite.
|
|
434
418
|
if (parts.length && supported.includes(parts[0])) {
|
|
435
419
|
return `${pathname}${search}${hash}`;
|
|
436
420
|
}
|
|
437
|
-
// Unprefixed same-app Link
|
|
421
|
+
// Unprefixed same-app Link �?realize with current LocaleId.
|
|
438
422
|
let rest = pathname;
|
|
439
423
|
if (rest.length > 1 && rest.endsWith('/'))
|
|
440
424
|
rest = rest.slice(0, -1);
|
|
@@ -452,15 +436,12 @@ export function installClientNavigation(opts = {}) {
|
|
|
452
436
|
function onPopState() {
|
|
453
437
|
void transitionTo(loc.pathname + loc.search + loc.hash, { fromPop: true });
|
|
454
438
|
}
|
|
455
|
-
/** @type {number} */
|
|
456
439
|
let localeTransitionGeneration = 0;
|
|
457
440
|
/**
|
|
458
441
|
* Atomic LocaleTransition (browser host slice):
|
|
459
|
-
* - prefix: validate
|
|
460
|
-
* - none: validate
|
|
442
|
+
* - prefix: validate �?realize path �?navigate/fetch �?commit locale attrs from SSR HTML
|
|
443
|
+
* - none: validate �?Host persist (localStorage+cookie) �?commit attrs �?reload (v1; no URL rewrite)
|
|
461
444
|
* Failure keeps the previous locale surface (no half-page commit).
|
|
462
|
-
* @param {string} toLocale
|
|
463
|
-
* @param {{ replace?: boolean, reload?: boolean }} [opts]
|
|
464
445
|
*/
|
|
465
446
|
async function transitionLocale(toLocale, opts = {}) {
|
|
466
447
|
const fromLocale = doc.documentElement?.getAttribute('data-locale') || null;
|
|
@@ -521,7 +502,7 @@ export function installClientNavigation(opts = {}) {
|
|
|
521
502
|
return out;
|
|
522
503
|
}
|
|
523
504
|
if (!result?.ok) {
|
|
524
|
-
// transitionTo does not mutate html locale attrs before success
|
|
505
|
+
// transitionTo does not mutate html locale attrs before success �?surface stays fromLocale.
|
|
525
506
|
const still = doc.documentElement?.getAttribute('data-locale');
|
|
526
507
|
const out = {
|
|
527
508
|
status: 'rolled_back',
|
|
@@ -564,11 +545,8 @@ export function installClientNavigation(opts = {}) {
|
|
|
564
545
|
return out;
|
|
565
546
|
}
|
|
566
547
|
/**
|
|
567
|
-
* `routing.strategy: 'none'`
|
|
568
|
-
* Persist
|
|
569
|
-
* @param {string} toLocale
|
|
570
|
-
* @param {string | null} fromLocale
|
|
571
|
-
* @param {{ reload?: boolean }} [opts]
|
|
548
|
+
* `routing.strategy: 'none'` �?LocaleId is Host preference, not URL.
|
|
549
|
+
* Persist �?commit document attrs + hint �?full reload so `#locales/*` re-resolve (I2 v1).
|
|
572
550
|
*/
|
|
573
551
|
function transitionLocaleNone(toLocale, fromLocale, opts = {}) {
|
|
574
552
|
const STORE_KEY = 'vmz.locale';
|
|
@@ -621,9 +599,6 @@ export function installClientNavigation(opts = {}) {
|
|
|
621
599
|
}
|
|
622
600
|
return out;
|
|
623
601
|
}
|
|
624
|
-
/**
|
|
625
|
-
* @returns {{ strategy?: string, defaultPrefix?: string, defaultLocale?: string, locales?: string[] } | null}
|
|
626
|
-
*/
|
|
627
602
|
function readLocaleRouting() {
|
|
628
603
|
const raw = doc.documentElement?.getAttribute('data-vmz-locale-routing');
|
|
629
604
|
if (!raw)
|
|
@@ -637,9 +612,6 @@ export function installClientNavigation(opts = {}) {
|
|
|
637
612
|
}
|
|
638
613
|
/**
|
|
639
614
|
* Re-realize current URL under target LocaleId via frozen href table when present.
|
|
640
|
-
* @param {string} href
|
|
641
|
-
* @param {string} localeId
|
|
642
|
-
* @param {{ strategy?: string, defaultPrefix?: string, defaultLocale?: string, locales?: string[] }} routing
|
|
643
615
|
*/
|
|
644
616
|
function realizePathForLocale(href, localeId, routing) {
|
|
645
617
|
let pathname = href;
|
|
@@ -685,9 +657,6 @@ export function installClientNavigation(opts = {}) {
|
|
|
685
657
|
const pathOut = rest === '/' ? `/${localeId}` : `/${localeId}${rest}`;
|
|
686
658
|
return `${pathOut}${search}${hash}`;
|
|
687
659
|
}
|
|
688
|
-
/**
|
|
689
|
-
* @returns {Record<string, Record<string, string>> | null}
|
|
690
|
-
*/
|
|
691
660
|
function readLocaleHrefTable() {
|
|
692
661
|
const raw = doc.documentElement?.getAttribute('data-vmz-locale-hrefs');
|
|
693
662
|
if (!raw)
|
|
@@ -700,10 +669,6 @@ export function installClientNavigation(opts = {}) {
|
|
|
700
669
|
return null;
|
|
701
670
|
}
|
|
702
671
|
}
|
|
703
|
-
/**
|
|
704
|
-
* @param {string | null | undefined} routeId
|
|
705
|
-
* @param {string} localeId
|
|
706
|
-
*/
|
|
707
672
|
function lookupFrozenLocaleHref(routeId, localeId) {
|
|
708
673
|
if (!routeId || !localeId)
|
|
709
674
|
return null;
|
|
@@ -713,8 +678,6 @@ export function installClientNavigation(opts = {}) {
|
|
|
713
678
|
}
|
|
714
679
|
/**
|
|
715
680
|
* Reverse-lookup RouteId from frozen table when html lacks data-vmz-route.
|
|
716
|
-
* @param {string} pathname
|
|
717
|
-
* @param {string | null} localeId
|
|
718
681
|
*/
|
|
719
682
|
function resolveRouteIdFromHrefTable(pathname, localeId) {
|
|
720
683
|
const table = readLocaleHrefTable();
|
|
@@ -765,20 +728,12 @@ export function installClientNavigation(opts = {}) {
|
|
|
765
728
|
},
|
|
766
729
|
};
|
|
767
730
|
}
|
|
768
|
-
/**
|
|
769
|
-
* @param {string | null} raw
|
|
770
|
-
* @returns {string[]}
|
|
771
|
-
*/
|
|
772
731
|
function parseLayoutChain(raw) {
|
|
773
732
|
return String(raw || '')
|
|
774
733
|
.split(',')
|
|
775
734
|
.map((s) => s.trim())
|
|
776
735
|
.filter(Boolean);
|
|
777
736
|
}
|
|
778
|
-
/**
|
|
779
|
-
* @param {string[]} a
|
|
780
|
-
* @param {string[]} b
|
|
781
|
-
*/
|
|
782
737
|
function layoutChainsEqual(a, b) {
|
|
783
738
|
if (a.length !== b.length)
|
|
784
739
|
return false;
|
|
@@ -788,11 +743,6 @@ function layoutChainsEqual(a, b) {
|
|
|
788
743
|
}
|
|
789
744
|
return true;
|
|
790
745
|
}
|
|
791
|
-
/**
|
|
792
|
-
* @param {Element} root
|
|
793
|
-
* @param {string[]} prevLayout
|
|
794
|
-
* @param {string[]} nextLayout
|
|
795
|
-
*/
|
|
796
746
|
function canRetainLayouts(root, prevLayout, nextLayout) {
|
|
797
747
|
if (!layoutChainsEqual(prevLayout, nextLayout))
|
|
798
748
|
return false;
|
|
@@ -803,10 +753,6 @@ function canRetainLayouts(root, prevLayout, nextLayout) {
|
|
|
803
753
|
const insts = root.__vmzLayoutInsts;
|
|
804
754
|
return Array.isArray(insts) && insts.length === nextLayout.length;
|
|
805
755
|
}
|
|
806
|
-
/**
|
|
807
|
-
* @param {Element} root
|
|
808
|
-
* @param {Element} nextApp
|
|
809
|
-
*/
|
|
810
756
|
function applyAppAttrs(root, nextApp) {
|
|
811
757
|
for (const name of ['data-vmz-page', 'data-vmz-props', 'data-vmz-layout', 'data-vmz-route', 'data-vmz-locale', 'data-vmz-dir']) {
|
|
812
758
|
const v = nextApp.getAttribute(name);
|
|
@@ -816,10 +762,6 @@ function applyAppAttrs(root, nextApp) {
|
|
|
816
762
|
root.setAttribute(name, v);
|
|
817
763
|
}
|
|
818
764
|
}
|
|
819
|
-
/**
|
|
820
|
-
* @param {string} html
|
|
821
|
-
* @param {Document} doc
|
|
822
|
-
*/
|
|
823
765
|
export function extractAppHtml(html, doc) {
|
|
824
766
|
const parser = new (doc.defaultView?.DOMParser || globalThis.DOMParser)();
|
|
825
767
|
const parsed = parser.parseFromString(html, 'text/html');
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct DOM API types for `@vmz/core` browser runtime.
|
|
3
|
+
*/
|
|
4
|
+
export type BindingId = number | string | null;
|
|
5
|
+
export type PatchFn = (this: DirectInstance) => void;
|
|
6
|
+
export type DirectBranchCreate = (api: DirectApi) => Node;
|
|
7
|
+
export type DirectIfBranch = {
|
|
8
|
+
cond?: PatchFn;
|
|
9
|
+
create: DirectBranchCreate;
|
|
10
|
+
};
|
|
11
|
+
export type DirectEachSpec = {
|
|
12
|
+
as?: string;
|
|
13
|
+
list: PatchFn;
|
|
14
|
+
key?: PatchFn;
|
|
15
|
+
createItem: DirectBranchCreate;
|
|
16
|
+
serializeItem?: DirectBranchCreate;
|
|
17
|
+
};
|
|
18
|
+
export type BinderEntry = {
|
|
19
|
+
deps: string[];
|
|
20
|
+
fn: PatchFn;
|
|
21
|
+
bindingId?: BindingId;
|
|
22
|
+
};
|
|
23
|
+
export type DirtyTrieNode = {
|
|
24
|
+
[seg: string]: DirtyTrieNode | true | undefined;
|
|
25
|
+
};
|
|
26
|
+
export type ComponentListenerBag = Record<string, Array<(detail?: unknown) => void>>;
|
|
27
|
+
export type VmzPlan = {
|
|
28
|
+
schema?: string;
|
|
29
|
+
root_ids?: Array<string | number>;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
};
|
|
32
|
+
export type TaskStatus = 'pending' | 'success' | 'error' | 'cancelled';
|
|
33
|
+
export type TaskEntry = {
|
|
34
|
+
generation: number;
|
|
35
|
+
controller: AbortController | {
|
|
36
|
+
signal: {
|
|
37
|
+
aborted: boolean;
|
|
38
|
+
};
|
|
39
|
+
abort: () => void;
|
|
40
|
+
};
|
|
41
|
+
status: TaskStatus;
|
|
42
|
+
result?: unknown;
|
|
43
|
+
error?: unknown;
|
|
44
|
+
promise?: Promise<unknown>;
|
|
45
|
+
};
|
|
46
|
+
export type DirtyNotice = {
|
|
47
|
+
type: 'replace';
|
|
48
|
+
root: string;
|
|
49
|
+
} | {
|
|
50
|
+
type: 'path';
|
|
51
|
+
root: string;
|
|
52
|
+
segs: string[];
|
|
53
|
+
};
|
|
54
|
+
/** Runtime instance shape mounted by Direct create / hydrate / resume. */
|
|
55
|
+
export type DirectInstance = {
|
|
56
|
+
[key: string]: unknown;
|
|
57
|
+
__vmzDestroyed?: boolean;
|
|
58
|
+
__vmzDomRoot?: (Node & {
|
|
59
|
+
__vmzInst?: DirectInstance | null;
|
|
60
|
+
}) | null;
|
|
61
|
+
__vmzFlushTrie?: DirtyTrieNode | null;
|
|
62
|
+
__vmzFlushScheduled?: boolean;
|
|
63
|
+
__vmzFlushSync?: boolean;
|
|
64
|
+
__vmzDirtyTrie?: DirtyTrieNode | null;
|
|
65
|
+
__vmzDirty?: Set<string>;
|
|
66
|
+
__vmzDirtyNotices?: DirtyNotice[];
|
|
67
|
+
__vmzLeafDirty?: Array<{
|
|
68
|
+
root: string;
|
|
69
|
+
field: string;
|
|
70
|
+
idxs: number[];
|
|
71
|
+
}>;
|
|
72
|
+
__vmzDrainLeafDirty?: boolean;
|
|
73
|
+
__vmzBinders?: Record<string, BinderEntry | PatchFn>;
|
|
74
|
+
__vmzBindings?: Record<string, BinderEntry | PatchFn>;
|
|
75
|
+
__vmzDepToBindings?: Record<string, Array<string | number>>;
|
|
76
|
+
__vmzPendingChildMounts?: Array<Promise<unknown>>;
|
|
77
|
+
__vmzComponentListeners?: ComponentListenerBag;
|
|
78
|
+
__vmzOnParentProp?: (propName: string, value: unknown) => void;
|
|
79
|
+
__vmzApplyProps?: (props: object) => void;
|
|
80
|
+
__vmzTasks?: Record<string, TaskEntry>;
|
|
81
|
+
__vmzPlan?: VmzPlan | null;
|
|
82
|
+
__vmzCtor?: ComponentCtorLike | null;
|
|
83
|
+
__vmzQuiet?: boolean;
|
|
84
|
+
__vmzEventDepth?: number;
|
|
85
|
+
__vmzEachApplyLeaf?: unknown;
|
|
86
|
+
__vmzEachCompoundStride?: unknown;
|
|
87
|
+
__vmzEachTranspose?: unknown;
|
|
88
|
+
onMount?: () => void | Promise<void>;
|
|
89
|
+
onDestroy?: () => void | Promise<void>;
|
|
90
|
+
};
|
|
91
|
+
export type ComponentCtorLike = (new (props?: object) => DirectInstance) & {
|
|
92
|
+
__vmzDirect?: boolean;
|
|
93
|
+
__vmzCreate?: (this: DirectInstance, api: DirectApi) => Node;
|
|
94
|
+
__vmzSerialize?: (this: DirectInstance, api: DirectApi) => unknown;
|
|
95
|
+
__vmzPlan?: VmzPlan | unknown;
|
|
96
|
+
__vmzHostBox?: string;
|
|
97
|
+
__vmzTag?: string;
|
|
98
|
+
__vmzState?: string[];
|
|
99
|
+
__vmzProps?: string[];
|
|
100
|
+
__vmzWBInstalled?: boolean;
|
|
101
|
+
__vmzCtorAppliesProps?: boolean;
|
|
102
|
+
name?: string;
|
|
103
|
+
};
|
|
104
|
+
export type DirectEachCtx = {
|
|
105
|
+
noteItemBind: (bindingId: BindingId, deps: string[], fn: PatchFn) => void;
|
|
106
|
+
needDelegate: (type: string) => void;
|
|
107
|
+
};
|
|
108
|
+
/** Browser + generated artifact platform surface (0.2.0 thin runtime). */
|
|
109
|
+
export type DirectApi = {
|
|
110
|
+
_inst: DirectInstance | null;
|
|
111
|
+
_branchBinds: Array<{
|
|
112
|
+
deps: string[];
|
|
113
|
+
fn: PatchFn;
|
|
114
|
+
bindingId?: BindingId;
|
|
115
|
+
}> | null;
|
|
116
|
+
_itemPatches: PatchFn[] | null;
|
|
117
|
+
_eachCtx: DirectEachCtx | null;
|
|
118
|
+
_resumeAdopt: null | Record<string, unknown>;
|
|
119
|
+
el: (tag: string) => Element;
|
|
120
|
+
text: (value?: unknown) => Text;
|
|
121
|
+
frag: () => DocumentFragment;
|
|
122
|
+
comment: (value?: string) => Comment;
|
|
123
|
+
attr: (el: Element, name: string, value: unknown) => void;
|
|
124
|
+
mergeClass: (...parts: unknown[]) => string;
|
|
125
|
+
mergeStyle: (...parts: unknown[]) => string;
|
|
126
|
+
insertBefore: (parent: Node, node: Node, ref: Node | null) => void;
|
|
127
|
+
removeNode: (node: Node) => void;
|
|
128
|
+
trackPatch: (inst: DirectInstance, deps: string[], patch: PatchFn, bindingId?: BindingId) => void;
|
|
129
|
+
specFieldText: (inst: DirectInstance, bindingId: BindingId, fieldName: string, textNode: Text) => void;
|
|
130
|
+
specFieldAttr: (inst: DirectInstance, bindingId: BindingId, fieldName: string, el: Element, name: string) => void;
|
|
131
|
+
on: (el: Element, type: string, handler: EventListener | PatchFn) => void;
|
|
132
|
+
onMethod: (el: Element, type: string, method: string) => void;
|
|
133
|
+
onComponentEvent: (el: Element, type: string, method: string) => void;
|
|
134
|
+
adoptEnter: (node: Element) => boolean;
|
|
135
|
+
adoptLeave: () => void;
|
|
136
|
+
projectDefaultSlot: (hostEl: HTMLElement, node: Node) => void;
|
|
137
|
+
setHtml: (el: Element, value: unknown) => void;
|
|
138
|
+
bindHtml: (inst: DirectInstance, bindingId: BindingId, deps: string[], get: PatchFn, el: Element) => void;
|
|
139
|
+
bindComponentProp: (inst: DirectInstance, bindingId: BindingId, deps: string[], get: PatchFn, hostInst: DirectInstance, propName: string) => void;
|
|
140
|
+
component: (Ctor: ComponentCtorLike | string, props?: object) => HTMLElement;
|
|
141
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct host box model (`ui-direct-host-box`).
|
|
3
|
+
* Chip hosts opt into `display: contents`; block surfaces keep a real layout box.
|
|
4
|
+
*/
|
|
5
|
+
import type { ComponentCtor } from './dom-core.types.js';
|
|
6
|
+
export declare const INLINE_HOST_CONTENTS: ReadonlySet<string>;
|
|
7
|
+
export declare function resolveDirectHostBox(name: string, Ctor: ComponentCtor | null | undefined): 'contents' | 'block';
|
|
8
|
+
/** Apply host box to a live DOM element (create / hydrate / resume). */
|
|
9
|
+
export declare function applyDirectHostBox(host: HTMLElement, name: string, Ctor: ComponentCtor | null | undefined): void;
|
|
10
|
+
/** SSR attrs fragment for host box (only when contents). */
|
|
11
|
+
export declare function directHostBoxStyleAttr(name: string, Ctor: ComponentCtor | null | undefined): string | null;
|
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
* Direct host box model (`ui-direct-host-box`).
|
|
3
3
|
* Chip hosts opt into `display: contents`; block surfaces keep a real layout box.
|
|
4
4
|
*/
|
|
5
|
-
/** @type {ReadonlySet<string>} */
|
|
6
5
|
export const INLINE_HOST_CONTENTS = new Set(['Button', 'Badge', 'Link', 'Tag', 'Icon']);
|
|
7
|
-
/**
|
|
8
|
-
* @param {string} name
|
|
9
|
-
* @param {{ __vmzHostBox?: string } | null | undefined} Ctor
|
|
10
|
-
* @returns {'contents' | 'block'}
|
|
11
|
-
*/
|
|
12
6
|
export function resolveDirectHostBox(name, Ctor) {
|
|
13
7
|
const explicit = Ctor && typeof Ctor.__vmzHostBox === 'string' ? Ctor.__vmzHostBox : null;
|
|
14
8
|
if (explicit === 'contents' || explicit === 'block')
|
|
@@ -17,12 +11,7 @@ export function resolveDirectHostBox(name, Ctor) {
|
|
|
17
11
|
return 'contents';
|
|
18
12
|
return 'block';
|
|
19
13
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Apply host box to a live DOM element (create / hydrate / resume).
|
|
22
|
-
* @param {HTMLElement} host
|
|
23
|
-
* @param {string} name
|
|
24
|
-
* @param {{ __vmzHostBox?: string } | null | undefined} Ctor
|
|
25
|
-
*/
|
|
14
|
+
/** Apply host box to a live DOM element (create / hydrate / resume). */
|
|
26
15
|
export function applyDirectHostBox(host, name, Ctor) {
|
|
27
16
|
if (!host || !host.style)
|
|
28
17
|
return;
|
|
@@ -30,12 +19,7 @@ export function applyDirectHostBox(host, name, Ctor) {
|
|
|
30
19
|
host.style.display = 'contents';
|
|
31
20
|
}
|
|
32
21
|
}
|
|
33
|
-
/**
|
|
34
|
-
* SSR attrs fragment for host box (only when contents).
|
|
35
|
-
* @param {string} name
|
|
36
|
-
* @param {{ __vmzHostBox?: string } | null | undefined} Ctor
|
|
37
|
-
* @returns {string | null}
|
|
38
|
-
*/
|
|
22
|
+
/** SSR attrs fragment for host box (only when contents). */
|
|
39
23
|
export function directHostBoxStyleAttr(name, Ctor) {
|
|
40
24
|
return resolveDirectHostBox(name, Ctor) === 'contents' ? 'display:contents' : null;
|
|
41
25
|
}
|