@vmz/core 0.1.29 → 0.1.31
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.js +78 -6
- package/dist/dom.browser.d.ts +7 -0
- package/dist/dom.browser.js +8 -0
- package/dist/localize-body-links.d.ts +4 -2
- package/dist/localize-body-links.js +20 -3
- package/dist/serve-host.d.ts +2 -2
- package/dist/serve-host.mjs +58 -55
- package/package.json +11 -7
package/dist/client-nav.js
CHANGED
|
@@ -56,7 +56,7 @@ export function installClientNavigation(opts = {}) {
|
|
|
56
56
|
win.vmzDestroy(inst);
|
|
57
57
|
}
|
|
58
58
|
async function loadDomFallback() {
|
|
59
|
-
return import(/* @vite-ignore */ '/
|
|
59
|
+
return import(/* @vite-ignore */ '/dom.browser.js');
|
|
60
60
|
}
|
|
61
61
|
function navKey(pathname, search) {
|
|
62
62
|
return `${pathname || '/'}${search || ''}`;
|
|
@@ -370,21 +370,40 @@ export function installClientNavigation(opts = {}) {
|
|
|
370
370
|
if (a.hasAttribute('download') || a.getAttribute('target') === '_blank')
|
|
371
371
|
return;
|
|
372
372
|
ev.preventDefault();
|
|
373
|
-
//
|
|
374
|
-
const realized = localizeClickHref(u.pathname + u.search + u.hash);
|
|
373
|
+
// Prefer frozen RouteId×LocaleId href table (0.1.30); fall back only when table missing.
|
|
374
|
+
const realized = localizeClickHref(u.pathname + u.search + u.hash, a.getAttribute('data-vmz-route'));
|
|
375
375
|
void transitionTo(realized, {
|
|
376
376
|
replace: a.getAttribute('data-vmz-replace') === 'true',
|
|
377
377
|
});
|
|
378
378
|
}
|
|
379
379
|
/**
|
|
380
380
|
* @param {string} href
|
|
381
|
+
* @param {string | null} [routeId]
|
|
381
382
|
*/
|
|
382
|
-
function localizeClickHref(href) {
|
|
383
|
+
function localizeClickHref(href, routeId) {
|
|
383
384
|
if (!doc?.documentElement)
|
|
384
385
|
return href;
|
|
385
386
|
const locale = doc.documentElement.getAttribute('data-locale');
|
|
387
|
+
if (!locale)
|
|
388
|
+
return href;
|
|
389
|
+
const frozen = lookupFrozenLocaleHref(routeId, locale);
|
|
390
|
+
if (frozen) {
|
|
391
|
+
let search = '';
|
|
392
|
+
let hash = '';
|
|
393
|
+
const hashIdx = href.indexOf('#');
|
|
394
|
+
let pathPart = href;
|
|
395
|
+
if (hashIdx >= 0) {
|
|
396
|
+
hash = pathPart.slice(hashIdx);
|
|
397
|
+
pathPart = pathPart.slice(0, hashIdx);
|
|
398
|
+
}
|
|
399
|
+
const qIdx = pathPart.indexOf('?');
|
|
400
|
+
if (qIdx >= 0) {
|
|
401
|
+
search = pathPart.slice(qIdx);
|
|
402
|
+
}
|
|
403
|
+
return `${frozen}${search}${hash}`;
|
|
404
|
+
}
|
|
386
405
|
const raw = doc.documentElement.getAttribute('data-vmz-locale-routing');
|
|
387
|
-
if (!
|
|
406
|
+
if (!raw)
|
|
388
407
|
return href;
|
|
389
408
|
let routing;
|
|
390
409
|
try {
|
|
@@ -617,7 +636,7 @@ export function installClientNavigation(opts = {}) {
|
|
|
617
636
|
}
|
|
618
637
|
}
|
|
619
638
|
/**
|
|
620
|
-
* Re-realize current URL under target LocaleId
|
|
639
|
+
* Re-realize current URL under target LocaleId via frozen href table when present.
|
|
621
640
|
* @param {string} href
|
|
622
641
|
* @param {string} localeId
|
|
623
642
|
* @param {{ strategy?: string, defaultPrefix?: string, defaultLocale?: string, locales?: string[] }} routing
|
|
@@ -638,6 +657,13 @@ export function installClientNavigation(opts = {}) {
|
|
|
638
657
|
}
|
|
639
658
|
if (!pathname)
|
|
640
659
|
pathname = '/';
|
|
660
|
+
const fromLocale = doc.documentElement?.getAttribute('data-locale') || null;
|
|
661
|
+
const routeId = doc.documentElement?.getAttribute('data-vmz-route') ||
|
|
662
|
+
doc.querySelector?.('[data-vmz-app][data-vmz-route]')?.getAttribute?.('data-vmz-route') ||
|
|
663
|
+
resolveRouteIdFromHrefTable(pathname, fromLocale);
|
|
664
|
+
const frozen = lookupFrozenLocaleHref(routeId, localeId);
|
|
665
|
+
if (frozen)
|
|
666
|
+
return `${frozen}${search}${hash}`;
|
|
641
667
|
const supported = Array.isArray(routing.locales) ? routing.locales : [];
|
|
642
668
|
const parts = pathname.split('/').filter(Boolean);
|
|
643
669
|
let rest = pathname;
|
|
@@ -659,6 +685,52 @@ export function installClientNavigation(opts = {}) {
|
|
|
659
685
|
const pathOut = rest === '/' ? `/${localeId}` : `/${localeId}${rest}`;
|
|
660
686
|
return `${pathOut}${search}${hash}`;
|
|
661
687
|
}
|
|
688
|
+
/**
|
|
689
|
+
* @returns {Record<string, Record<string, string>> | null}
|
|
690
|
+
*/
|
|
691
|
+
function readLocaleHrefTable() {
|
|
692
|
+
const raw = doc.documentElement?.getAttribute('data-vmz-locale-hrefs');
|
|
693
|
+
if (!raw)
|
|
694
|
+
return null;
|
|
695
|
+
try {
|
|
696
|
+
const table = JSON.parse(raw);
|
|
697
|
+
return table && typeof table === 'object' ? table : null;
|
|
698
|
+
}
|
|
699
|
+
catch {
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* @param {string | null | undefined} routeId
|
|
705
|
+
* @param {string} localeId
|
|
706
|
+
*/
|
|
707
|
+
function lookupFrozenLocaleHref(routeId, localeId) {
|
|
708
|
+
if (!routeId || !localeId)
|
|
709
|
+
return null;
|
|
710
|
+
const table = readLocaleHrefTable();
|
|
711
|
+
const href = table?.[routeId]?.[localeId];
|
|
712
|
+
return typeof href === 'string' && href && !/\[[^\]]+\]/.test(href) && !/\/:[^/]+/.test(href) ? href : null;
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Reverse-lookup RouteId from frozen table when html lacks data-vmz-route.
|
|
716
|
+
* @param {string} pathname
|
|
717
|
+
* @param {string | null} localeId
|
|
718
|
+
*/
|
|
719
|
+
function resolveRouteIdFromHrefTable(pathname, localeId) {
|
|
720
|
+
const table = readLocaleHrefTable();
|
|
721
|
+
if (!table || !localeId)
|
|
722
|
+
return null;
|
|
723
|
+
const norm = pathname.length > 1 && pathname.endsWith('/') ? pathname.slice(0, -1) : pathname || '/';
|
|
724
|
+
for (const [routeId, byLocale] of Object.entries(table)) {
|
|
725
|
+
const href = byLocale?.[localeId];
|
|
726
|
+
if (typeof href !== 'string')
|
|
727
|
+
continue;
|
|
728
|
+
const h = href.length > 1 && href.endsWith('/') ? href.slice(0, -1) : href;
|
|
729
|
+
if (h === norm)
|
|
730
|
+
return routeId;
|
|
731
|
+
}
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
662
734
|
if (win) {
|
|
663
735
|
win.__vmzTransitionLocale = transitionLocale;
|
|
664
736
|
win.__vmzClientNavSetFetch = (fn) => {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser delivery face (0.1.31): DOM core + hydrate/resume/route attach.
|
|
3
|
+
* Does NOT re-export Node SSR `renderToString` / `renderToStream`.
|
|
4
|
+
* Full barrel remains `@vmz/core/dom` (`dom.js` / dist `vmz-dom.js`) for Node host.
|
|
5
|
+
*/
|
|
6
|
+
export * from './dom-core.js';
|
|
7
|
+
export { attachEventEntries, hydrate, hydrateIslands, hydrateRoute, hydrateRoutePage, resume, resumeIslands, } from './dom-ssr.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Browser delivery face (0.1.31): DOM core + hydrate/resume/route attach.
|
|
4
|
+
* Does NOT re-export Node SSR `renderToString` / `renderToStream`.
|
|
5
|
+
* Full barrel remains `@vmz/core/dom` (`dom.js` / dist `vmz-dom.js`) for Node host.
|
|
6
|
+
*/
|
|
7
|
+
export * from './dom-core.js';
|
|
8
|
+
export { attachEventEntries, hydrate, hydrateIslands, hydrateRoute, hydrateRoutePage, resume, resumeIslands, } from './dom-ssr.js';
|
|
@@ -49,7 +49,9 @@ export declare function linkRouteAliasesFromUnits(units: Array<{
|
|
|
49
49
|
export declare function buildLocaleLinkPlan(artifact: LocaleHrefArtifact | null | undefined): LocaleLinkPlan;
|
|
50
50
|
/** Apply plan rows to `<a data-vmz-route="RouteId">` markers for one LocaleId. */
|
|
51
51
|
export declare function applyLocaleLinkPlan(html: string, localeId: string, plan: LocaleLinkPlan, escapeAttr?: (s: string) => string): string;
|
|
52
|
-
/** Rewrite `<a data-vmz-route href>` using locale
|
|
53
|
-
export declare function localizeBodyLinks(html: string, localeId: string, artifact: LocaleHrefArtifact, escapeAttr?: (s: string) => string): string;
|
|
52
|
+
/** Rewrite `<a data-vmz-route href>` using a frozen locale link plan (preferred) or build once from realization. */
|
|
53
|
+
export declare function localizeBodyLinks(html: string, localeId: string, artifact: LocaleHrefArtifact, escapeAttr?: (s: string) => string, frozenPlan?: LocaleLinkPlan | null): string;
|
|
54
|
+
/** Compact RouteId → LocaleId → href table for browser (no path-prefix algebra). */
|
|
55
|
+
export declare function localeHrefTableFromPlan(plan: LocaleLinkPlan | null | undefined): Record<string, Record<string, string>>;
|
|
54
56
|
/** Legacy same-app href helper for callers without `data-vmz-route` markers. */
|
|
55
57
|
export declare function localizeSameAppHref(href: string, localeId: string, artifact: LocaleHrefArtifact): string;
|
|
@@ -71,11 +71,28 @@ export function applyLocaleLinkPlan(html, localeId, plan, escapeAttr = (s) => St
|
|
|
71
71
|
return `<a${newAttrs}>`;
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
/** Rewrite `<a data-vmz-route href>` using locale
|
|
75
|
-
export function localizeBodyLinks(html, localeId, artifact, escapeAttr) {
|
|
74
|
+
/** Rewrite `<a data-vmz-route href>` using a frozen locale link plan (preferred) or build once from realization. */
|
|
75
|
+
export function localizeBodyLinks(html, localeId, artifact, escapeAttr, frozenPlan) {
|
|
76
76
|
if (!html || !localeId || !artifact)
|
|
77
77
|
return html;
|
|
78
|
-
|
|
78
|
+
const plan = frozenPlan && frozenPlan.schema === LOCALE_LINK_PLAN_SCHEMA && Array.isArray(frozenPlan.rows)
|
|
79
|
+
? frozenPlan
|
|
80
|
+
: buildLocaleLinkPlan(artifact);
|
|
81
|
+
return applyLocaleLinkPlan(html, localeId, plan, escapeAttr);
|
|
82
|
+
}
|
|
83
|
+
/** Compact RouteId → LocaleId → href table for browser (no path-prefix algebra). */
|
|
84
|
+
export function localeHrefTableFromPlan(plan) {
|
|
85
|
+
/** @type {Record<string, Record<string, string>>} */
|
|
86
|
+
const out = Object.create(null);
|
|
87
|
+
for (const row of plan?.rows || []) {
|
|
88
|
+
if (!row?.routeId || !row?.localeId || !row?.href)
|
|
89
|
+
continue;
|
|
90
|
+
if (/\[[^\]]+\]/.test(row.href) || /\/:[^/]+/.test(row.href))
|
|
91
|
+
continue;
|
|
92
|
+
const byLocale = out[row.routeId] || (out[row.routeId] = Object.create(null));
|
|
93
|
+
byLocale[row.localeId] = row.href;
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
79
96
|
}
|
|
80
97
|
function normalizePath(pathname) {
|
|
81
98
|
let p = String(pathname || '/');
|
package/dist/serve-host.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Invoked by `vmz serve` / `vmz dev` (or: node dist/vmz-serve-host.mjs).
|
|
5
5
|
*
|
|
6
|
-
* Pathname matches `
|
|
7
|
-
*
|
|
6
|
+
* Pathname matches compiled `_vmz/route-catalog.json` (frozen segs / pathPattern).
|
|
7
|
+
* Hosts must not re-parse deployment pathPattern into a live catalog.
|
|
8
8
|
* Not an SPA shell.
|
|
9
9
|
*
|
|
10
10
|
* `VMZ_DEV=1`: POST `/__vmz/reload` soft-reloads modules (cache-bust import);
|
package/dist/serve-host.mjs
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Invoked by `vmz serve` / `vmz dev` (or: node dist/vmz-serve-host.mjs).
|
|
6
6
|
*
|
|
7
|
-
* Pathname matches `
|
|
8
|
-
*
|
|
7
|
+
* Pathname matches compiled `_vmz/route-catalog.json` (frozen segs / pathPattern).
|
|
8
|
+
* Hosts must not re-parse deployment pathPattern into a live catalog.
|
|
9
9
|
* Not an SPA shell.
|
|
10
10
|
*
|
|
11
11
|
* `VMZ_DEV=1`: POST `/__vmz/reload` soft-reloads modules (cache-bust import);
|
|
@@ -23,13 +23,28 @@ import { createRequire, registerHooks } from 'node:module';
|
|
|
23
23
|
import path from 'node:path';
|
|
24
24
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
25
25
|
import { listClientComponents } from './list-client-components.js';
|
|
26
|
-
import { linkRouteAliasesFromUnits, localizeBodyLinks } from './localize-body-links.js';
|
|
26
|
+
import { LOCALE_LINK_PLAN_SCHEMA, linkRouteAliasesFromUnits, localeHrefTableFromPlan, localizeBodyLinks } from './localize-body-links.js';
|
|
27
27
|
import { loadNativeAddon } from './native-addon.js';
|
|
28
28
|
import { createRenderHost } from './render-host.js';
|
|
29
29
|
import { resolveRouteLayoutChain } from './route-layout-chain.js';
|
|
30
30
|
import { handleNodeRequest, setRoutes, setServerModuleResolver } from './vmz-runtime.js';
|
|
31
|
+
const ROUTE_CATALOG_SCHEMA = 'vmz.route.catalog.v0';
|
|
32
|
+
const ROUTE_CATALOG_REL = '_vmz/route-catalog.json';
|
|
33
|
+
const LOCALE_LINK_PLAN_REL = '_vmz/locale-link-plan.json';
|
|
31
34
|
const require = createRequire(import.meta.url);
|
|
32
|
-
|
|
35
|
+
/** App delivery root. Host companions may live under `_vmz/host/` (0.1.31). */
|
|
36
|
+
function resolveServeDistDir() {
|
|
37
|
+
if (process.env.VMZ_DIST)
|
|
38
|
+
return path.resolve(process.env.VMZ_DIST);
|
|
39
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
40
|
+
const norm = here.replace(/\\/g, '/');
|
|
41
|
+
const marker = '/_vmz/host';
|
|
42
|
+
const idx = norm.toLowerCase().lastIndexOf(marker);
|
|
43
|
+
if (idx >= 0)
|
|
44
|
+
return path.resolve(here.slice(0, idx));
|
|
45
|
+
return here;
|
|
46
|
+
}
|
|
47
|
+
const distDir = resolveServeDistDir();
|
|
33
48
|
/** App project root for bare import / JSON resolve (dev SSR ≡ build node_modules). */
|
|
34
49
|
function projectRootForResolve() {
|
|
35
50
|
const fromEnv = typeof process.env.VMZ_PROJECT_ROOT === 'string' ? process.env.VMZ_PROJECT_ROOT.trim() : '';
|
|
@@ -154,6 +169,8 @@ let styleBundleHash = null;
|
|
|
154
169
|
let styleTheme = null;
|
|
155
170
|
/** Locale route realization artifact from `_vmz/locale-route-realization.json` (optional). */
|
|
156
171
|
let localeArtifact = null;
|
|
172
|
+
/** Frozen locale link plan from `_vmz/locale-link-plan.json` (0.1.30 authority). */
|
|
173
|
+
let localeLinkPlan = null;
|
|
157
174
|
/** @type {Set<import('node:http').ServerResponse>} */
|
|
158
175
|
const sseClients = new Set();
|
|
159
176
|
/** In-flight HTTP requests (graceful shutdown drain). */
|
|
@@ -505,7 +522,7 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
505
522
|
hideOverlay();
|
|
506
523
|
if (msg.mode === "island") {
|
|
507
524
|
try {
|
|
508
|
-
const { registerComponents, hydrate } = await import("/
|
|
525
|
+
const { registerComponents, hydrate } = await import("/dom.browser.js?t=" + msg.token);
|
|
509
526
|
const names = (msg.affectedChunks || [])
|
|
510
527
|
.map((c) => String(c))
|
|
511
528
|
.filter((c) => c.startsWith("components/") || !c.includes("/"))
|
|
@@ -575,6 +592,10 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
575
592
|
defaultLocale: localeArtifact.defaultLocale,
|
|
576
593
|
locales: (localeArtifact.locales || []).map((l) => l.id),
|
|
577
594
|
}));
|
|
595
|
+
const hrefTable = localeHrefTableFromPlan(localeLinkPlan);
|
|
596
|
+
if (hrefTable && Object.keys(hrefTable).length) {
|
|
597
|
+
htmlExtraAttrs.push('data-vmz-locale-hrefs', JSON.stringify(hrefTable));
|
|
598
|
+
}
|
|
578
599
|
}
|
|
579
600
|
const pageDocMeta = resolvePageDocumentMeta(Page);
|
|
580
601
|
const prevLocaleHint = globalThis.__vmzLocaleIdHint;
|
|
@@ -605,9 +626,9 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
605
626
|
if (signal?.aborted)
|
|
606
627
|
return;
|
|
607
628
|
}
|
|
608
|
-
// Locale discipline:
|
|
629
|
+
// Locale discipline: apply frozen link plan rows (0.1.30) — no path algebra.
|
|
609
630
|
if (localeArtifact && localeId) {
|
|
610
|
-
bodyHtml = localizeBodyLinks(bodyHtml, localeId, localeArtifact);
|
|
631
|
+
bodyHtml = localizeBodyLinks(bodyHtml, localeId, localeArtifact, undefined, localeLinkPlan);
|
|
611
632
|
}
|
|
612
633
|
}
|
|
613
634
|
finally {
|
|
@@ -790,7 +811,6 @@ async function softReload(opts = {}) {
|
|
|
790
811
|
reloadToken = nextToken;
|
|
791
812
|
const affected = opts.payload?.affectedChunks ?? [];
|
|
792
813
|
const seeds = opts.payload?.seedChunks ?? [];
|
|
793
|
-
const emitted = opts.payload?.emitted ?? [];
|
|
794
814
|
const full = opts.payload?.full;
|
|
795
815
|
const islandHmr = Boolean(opts.payload?.islandHmr);
|
|
796
816
|
const rerunLoaders = opts.payload?.rerunLoaders ?? [];
|
|
@@ -800,7 +820,8 @@ async function softReload(opts = {}) {
|
|
|
800
820
|
const bundleRevision = opts.payload?.bundleRevision != null ? String(opts.payload.bundleRevision) : null;
|
|
801
821
|
if (buildId)
|
|
802
822
|
lastDevBuildId = buildId;
|
|
803
|
-
|
|
823
|
+
// 0.1.31: payload-only scope. `reloadToken` is opaque cache-bust — never invent full/affected here.
|
|
824
|
+
const reloadAllPages = Boolean(full) || (!islandHmr && rerunLoaders.length === 0 && affected.length === 0);
|
|
804
825
|
try {
|
|
805
826
|
try {
|
|
806
827
|
const routes = JSON.parse(await readFile(path.join(distDir, 'vmz-routes.json'), 'utf8'));
|
|
@@ -816,6 +837,13 @@ async function softReload(opts = {}) {
|
|
|
816
837
|
catch {
|
|
817
838
|
localeArtifact = null;
|
|
818
839
|
}
|
|
840
|
+
try {
|
|
841
|
+
const rawPlan = JSON.parse(await readFile(path.join(distDir, ...LOCALE_LINK_PLAN_REL.split('/')), 'utf8'));
|
|
842
|
+
localeLinkPlan = rawPlan?.schema === LOCALE_LINK_PLAN_SCHEMA && Array.isArray(rawPlan.rows) ? rawPlan : null;
|
|
843
|
+
}
|
|
844
|
+
catch {
|
|
845
|
+
localeLinkPlan = null;
|
|
846
|
+
}
|
|
819
847
|
const componentEntries = await listClientComponents(distDir, { strict: !isDev });
|
|
820
848
|
ssrRenderHost = await createRenderHost(distDir, {
|
|
821
849
|
strictDeployment: !isDev,
|
|
@@ -924,28 +952,6 @@ async function softReload(opts = {}) {
|
|
|
924
952
|
throw err;
|
|
925
953
|
}
|
|
926
954
|
}
|
|
927
|
-
/**
|
|
928
|
-
* Shared lib / full rebuild / missing affected list → refresh every page ctor.
|
|
929
|
-
* Otherwise only re-import the dirty page chunks (Vite-like module graph).
|
|
930
|
-
* @param {{ full?: boolean, affected: string[], emitted: string[], islandHmr: boolean }} opts
|
|
931
|
-
*/
|
|
932
|
-
function shouldReloadAllPages(opts) {
|
|
933
|
-
if (opts.islandHmr)
|
|
934
|
-
return false;
|
|
935
|
-
if (opts.full)
|
|
936
|
-
return true;
|
|
937
|
-
if (Array.isArray(opts.rerunLoaders) && opts.rerunLoaders.length > 0)
|
|
938
|
-
return false;
|
|
939
|
-
if (!opts.affected.length)
|
|
940
|
-
return true;
|
|
941
|
-
for (const f of opts.emitted) {
|
|
942
|
-
const n = String(f).replace(/\\/g, '/');
|
|
943
|
-
if (n.includes('/lib/') || /\/Application\.client\.js$/.test(n) || /\/vmz-(dom|runtime|http|client-nav)\.js$/.test(n)) {
|
|
944
|
-
return true;
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
return false;
|
|
948
|
-
}
|
|
949
955
|
/** @param {string} chunkId @param {string[]} affected */
|
|
950
956
|
function pageNeedsReload(chunkId, affected) {
|
|
951
957
|
if (chunkId === 'pages/Layout' || chunkId.endsWith('/Layout'))
|
|
@@ -1108,54 +1114,51 @@ async function loadPageCtor(chunkId) {
|
|
|
1108
1114
|
return mod.default;
|
|
1109
1115
|
}
|
|
1110
1116
|
/**
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1117
|
+
* Load compiled page catalog from `_vmz/route-catalog.json` (0.1.30 authority).
|
|
1118
|
+
* Hosts must not re-parse deployment pathPattern into a live catalog.
|
|
1113
1119
|
* @param {string} dir
|
|
1114
1120
|
*/
|
|
1115
1121
|
async function listPageClientFiles(dir) {
|
|
1116
|
-
const
|
|
1117
|
-
if (!
|
|
1122
|
+
const fromCatalog = await listPagesFromRouteCatalog(dir);
|
|
1123
|
+
if (!fromCatalog.length) {
|
|
1118
1124
|
if (isDev) {
|
|
1119
|
-
console.warn(`vmz dev:
|
|
1125
|
+
console.warn(`vmz dev: missing compiled ${ROUTE_CATALOG_SCHEMA} at ${path.join(dir, ...ROUTE_CATALOG_REL.split('/'))}`);
|
|
1120
1126
|
return [];
|
|
1121
1127
|
}
|
|
1122
|
-
throw new Error(`vmz serve:
|
|
1128
|
+
throw new Error(`vmz serve: missing compiled ${ROUTE_CATALOG_SCHEMA} at ${path.join(dir, ...ROUTE_CATALOG_REL.split('/'))}`);
|
|
1123
1129
|
}
|
|
1124
|
-
return
|
|
1130
|
+
return fromCatalog;
|
|
1125
1131
|
}
|
|
1126
1132
|
/**
|
|
1127
1133
|
* @param {string} dir
|
|
1128
1134
|
*/
|
|
1129
|
-
async function
|
|
1130
|
-
/** @type {Array<{ chunkId: string, pageRel: string, segs: ReturnType<typeof parsePathPattern> }>} */
|
|
1135
|
+
async function listPagesFromRouteCatalog(dir) {
|
|
1136
|
+
/** @type {Array<{ chunkId: string, pageRel: string, routeId?: string, pathPattern?: string, segs: ReturnType<typeof parsePathPattern> }>} */
|
|
1131
1137
|
const out = [];
|
|
1132
1138
|
try {
|
|
1133
|
-
const raw = await readFile(path.join(dir,
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
const chunkId = String(
|
|
1139
|
+
const raw = await readFile(path.join(dir, ...ROUTE_CATALOG_REL.split('/')), 'utf8');
|
|
1140
|
+
const catalog = JSON.parse(raw);
|
|
1141
|
+
if (catalog?.schema !== ROUTE_CATALOG_SCHEMA || !Array.isArray(catalog.pages))
|
|
1142
|
+
return [];
|
|
1143
|
+
for (const page of catalog.pages) {
|
|
1144
|
+
const chunkId = String(page?.chunkId || '').replace(/\\/g, '/');
|
|
1139
1145
|
if (!chunkId.startsWith('pages/'))
|
|
1140
1146
|
continue;
|
|
1141
1147
|
const stem = chunkId.split('/').pop() || '';
|
|
1142
1148
|
if (isRouteBoundaryStem(stem))
|
|
1143
1149
|
continue;
|
|
1144
|
-
const
|
|
1145
|
-
|
|
1146
|
-
throw new Error(`vmz serve: page unit ${chunkId} missing pathPattern in vmz-deployment.json (plan-only host)`);
|
|
1147
|
-
}
|
|
1148
|
-
const pageRel = String(unit.clientEntry || `${chunkId}.client.js`).replace(/\\/g, '/');
|
|
1150
|
+
const segs = Array.isArray(page?.segs) && page.segs.length ? page.segs : parsePathPattern(String(page?.pathPattern || ''));
|
|
1151
|
+
const pageRel = String(page?.pageRel || `${chunkId}.client.js`).replace(/\\/g, '/');
|
|
1149
1152
|
out.push({
|
|
1150
1153
|
chunkId,
|
|
1151
1154
|
pageRel,
|
|
1152
|
-
|
|
1155
|
+
routeId: typeof page?.routeId === 'string' ? page.routeId : chunkId,
|
|
1156
|
+
pathPattern: typeof page?.pathPattern === 'string' ? page.pathPattern : undefined,
|
|
1157
|
+
segs,
|
|
1153
1158
|
});
|
|
1154
1159
|
}
|
|
1155
1160
|
}
|
|
1156
|
-
catch
|
|
1157
|
-
if (err && typeof err.message === 'string' && err.message.includes('plan-only host'))
|
|
1158
|
-
throw err;
|
|
1161
|
+
catch {
|
|
1159
1162
|
return [];
|
|
1160
1163
|
}
|
|
1161
1164
|
return out;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vmz/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "VMZ production runtime core — DOM / SSR / HTTP / WriteBarrier (no compiler)",
|
|
6
6
|
"exports": {
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"types": "./dist/dom.client.d.ts",
|
|
17
17
|
"default": "./dist/dom.client.js"
|
|
18
18
|
},
|
|
19
|
+
"./dom/browser": {
|
|
20
|
+
"types": "./dist/dom.browser.d.ts",
|
|
21
|
+
"default": "./dist/dom.browser.js"
|
|
22
|
+
},
|
|
19
23
|
"./server": {
|
|
20
24
|
"types": "./dist/server.d.ts",
|
|
21
25
|
"default": "./dist/server.js"
|
|
@@ -66,12 +70,12 @@
|
|
|
66
70
|
"linkedom": "^0.18.13"
|
|
67
71
|
},
|
|
68
72
|
"optionalDependencies": {
|
|
69
|
-
"@vmz/vmz-win32-x64": "0.1.
|
|
70
|
-
"@vmz/vmz-win32-arm64": "0.1.
|
|
71
|
-
"@vmz/vmz-darwin-x64": "0.1.
|
|
72
|
-
"@vmz/vmz-darwin-arm64": "0.1.
|
|
73
|
-
"@vmz/vmz-linux-x64": "0.1.
|
|
74
|
-
"@vmz/vmz-linux-arm64": "0.1.
|
|
73
|
+
"@vmz/vmz-win32-x64": "0.1.31",
|
|
74
|
+
"@vmz/vmz-win32-arm64": "0.1.31",
|
|
75
|
+
"@vmz/vmz-darwin-x64": "0.1.31",
|
|
76
|
+
"@vmz/vmz-darwin-arm64": "0.1.31",
|
|
77
|
+
"@vmz/vmz-linux-x64": "0.1.31",
|
|
78
|
+
"@vmz/vmz-linux-arm64": "0.1.31"
|
|
75
79
|
},
|
|
76
80
|
"publishConfig": {
|
|
77
81
|
"access": "public"
|