@vmz/core 0.1.32 → 0.2.0
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 +58 -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} +64 -220
- package/dist/browser/dom-core.js +2453 -0
- package/dist/browser/dom-core.types.d.ts +138 -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} +4 -28
- 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/{http.d.ts → faces/http.d.ts} +1 -1
- package/dist/{http.js → faces/http.js} +1 -2
- package/dist/{server.d.ts → faces/server.d.ts} +8 -25
- package/dist/{server.js → faces/server.js} +25 -155
- 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} +0 -16
- 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/host.types.d.ts +67 -0
- package/dist/shared/host.types.js +4 -0
- package/dist/shared/server.types.d.ts +39 -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} +159 -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/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/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,6 +1,5 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
/**
|
|
3
|
-
* Generic VMZ Node host
|
|
2
|
+
* Generic VMZ Node host �?SSR Route Graph pages + dist static + RPC/REST.
|
|
4
3
|
*
|
|
5
4
|
* Invoked by `vmz serve` / `vmz dev` (or: node dist/vmz-serve-host.mjs).
|
|
6
5
|
*
|
|
@@ -10,8 +9,8 @@
|
|
|
10
9
|
*
|
|
11
10
|
* `VMZ_DEV=1`: POST `/__vmz/reload` soft-reloads modules (cache-bust import);
|
|
12
11
|
* GET `/__vmz/events` SSE notifies the browser:
|
|
13
|
-
* - island HMR
|
|
14
|
-
* - otherwise
|
|
12
|
+
* - island HMR �?re-import `entry-client.js` (no full document reload)
|
|
13
|
+
* - otherwise �?`location.reload`
|
|
15
14
|
*
|
|
16
15
|
* Dev resolve hook propagates `?t=` onto nested relative `file:` imports under
|
|
17
16
|
* dist so soft reload does not keep a stale `lib/*.js` ESM cache entry.
|
|
@@ -27,12 +26,11 @@ import { LOCALE_LINK_PLAN_SCHEMA, linkRouteAliasesFromUnits, localeHrefTableFrom
|
|
|
27
26
|
import { loadNativeAddon } from './native-addon.js';
|
|
28
27
|
import { createRenderHost } from './render-host.js';
|
|
29
28
|
import { resolveRouteLayoutChain } from './route-layout-chain.js';
|
|
30
|
-
import { handleNodeRequest, setRoutes, setServerModuleResolver } from '
|
|
29
|
+
import { handleNodeRequest, setRoutes, setServerModuleResolver } from '../faces/vmz-runtime.js';
|
|
31
30
|
const ROUTE_CATALOG_SCHEMA = 'vmz.route.catalog.v0';
|
|
32
31
|
const ROUTE_CATALOG_REL = '_vmz/route-catalog.json';
|
|
33
32
|
const LOCALE_LINK_PLAN_REL = '_vmz/locale-link-plan.json';
|
|
34
33
|
const require = createRequire(import.meta.url);
|
|
35
|
-
/** App delivery root. Host companions may live under `_vmz/host/` (0.1.31). */
|
|
36
34
|
function resolveServeDistDir() {
|
|
37
35
|
if (process.env.VMZ_DIST)
|
|
38
36
|
return path.resolve(process.env.VMZ_DIST);
|
|
@@ -45,14 +43,12 @@ function resolveServeDistDir() {
|
|
|
45
43
|
return here;
|
|
46
44
|
}
|
|
47
45
|
const distDir = resolveServeDistDir();
|
|
48
|
-
/** App project root for bare import / JSON resolve (dev SSR ≡ build node_modules). */
|
|
49
46
|
function projectRootForResolve() {
|
|
50
47
|
const fromEnv = typeof process.env.VMZ_PROJECT_ROOT === 'string' ? process.env.VMZ_PROJECT_ROOT.trim() : '';
|
|
51
48
|
if (fromEnv)
|
|
52
49
|
return path.resolve(fromEnv);
|
|
53
50
|
return process.cwd();
|
|
54
51
|
}
|
|
55
|
-
/** @type {ReturnType<typeof createRequire> | null} */
|
|
56
52
|
let appPackageRequire = null;
|
|
57
53
|
function appPackageRequireResolve() {
|
|
58
54
|
if (!appPackageRequire) {
|
|
@@ -112,7 +108,7 @@ const isDev = process.env.VMZ_DEV === '1' || process.env.VMZ_DEV === 'true';
|
|
|
112
108
|
globalThis.__VMZ_RPC_ORIGIN = `http://${host}:${port}`;
|
|
113
109
|
/**
|
|
114
110
|
* Soft reload only busts the top-level `import(page?t=token)`. Nested relative
|
|
115
|
-
* imports (`../../lib/units.js`) keep the first-loaded ESM cache entry
|
|
111
|
+
* imports (`../../lib/units.js`) keep the first-loaded ESM cache entry �?so a
|
|
116
112
|
* page can demand exports that the stale dep never had (or vice versa).
|
|
117
113
|
* Propagate `t` from parentURL onto file: children under this dist.
|
|
118
114
|
*/
|
|
@@ -151,34 +147,20 @@ if (isDev) {
|
|
|
151
147
|
},
|
|
152
148
|
});
|
|
153
149
|
}
|
|
154
|
-
/** @type {number} */
|
|
155
150
|
let reloadToken = Date.now();
|
|
156
|
-
/** @type {Awaited<ReturnType<typeof createRenderHost>> | null} */
|
|
157
151
|
let ssrRenderHost = null;
|
|
158
|
-
/** @type {string | null} Correlatable build id from vmz dev (Living §12.8). */
|
|
159
152
|
let lastDevBuildId = null;
|
|
160
|
-
/** @type {Array<{ chunkId: string, pageRel: string, segs: ReturnType<typeof parsePathPattern> }>} */
|
|
161
153
|
let pageCatalog = [];
|
|
162
|
-
/** @type {Map<string, any>} */
|
|
163
154
|
const pageCtors = new Map();
|
|
164
|
-
/** Stylesheet from deployment `cssEntry` (e.g. vmz.css). */
|
|
165
155
|
let cssEntry = null;
|
|
166
|
-
/** Fingerprint of style inputs — busts `@import` siblings when tokens change (VMZ-8). */
|
|
167
156
|
let styleBundleHash = null;
|
|
168
|
-
/** @type {{ defaultThemeId: string, themeIds: string[], activationAttr: string, contentHash: string|null } | null} */
|
|
169
157
|
let styleTheme = null;
|
|
170
|
-
/** Locale route realization artifact from `_vmz/locale-route-realization.json` (optional). */
|
|
171
158
|
let localeArtifact = null;
|
|
172
|
-
/** Frozen locale link plan from `_vmz/locale-link-plan.json` (0.1.30 authority). */
|
|
173
159
|
let localeLinkPlan = null;
|
|
174
|
-
/** @type {Set<import('node:http').ServerResponse>} */
|
|
175
160
|
const sseClients = new Set();
|
|
176
|
-
/** In-flight HTTP requests (graceful shutdown drain). */
|
|
177
161
|
let inFlight = 0;
|
|
178
|
-
/** When true, refuse new work except health. */
|
|
179
162
|
let shuttingDown = false;
|
|
180
163
|
let ready = false;
|
|
181
|
-
/** @type {{ message: string, stack?: string, at: number } | null} */
|
|
182
164
|
let lastDevError = null;
|
|
183
165
|
setServerModuleResolver((moduleId) => {
|
|
184
166
|
const rel = moduleId.replace(/^#server\//, '') + '.js';
|
|
@@ -190,13 +172,9 @@ try {
|
|
|
190
172
|
}
|
|
191
173
|
catch (err) {
|
|
192
174
|
lastDevError = normalizeDevError(err);
|
|
193
|
-
ready = true; // still accept HTTP
|
|
175
|
+
ready = true; // still accept HTTP �?serve error page / recover on next reload
|
|
194
176
|
console.error('vmz serve: initial load failed (dev host stays up)', lastDevError.message);
|
|
195
177
|
}
|
|
196
|
-
/**
|
|
197
|
-
* @param {import('node:http').IncomingMessage} req
|
|
198
|
-
* @returns {Promise<string>}
|
|
199
|
-
*/
|
|
200
178
|
function readRequestBody(req) {
|
|
201
179
|
return new Promise((resolve, reject) => {
|
|
202
180
|
const chunks = [];
|
|
@@ -205,17 +183,16 @@ function readRequestBody(req) {
|
|
|
205
183
|
req.on('error', reject);
|
|
206
184
|
});
|
|
207
185
|
}
|
|
208
|
-
/**
|
|
209
|
-
* @param {string} pathname
|
|
210
|
-
* @returns {Promise<string | null>}
|
|
211
|
-
*/
|
|
212
186
|
async function renderPage(pathname, opts = {}) {
|
|
213
187
|
const rendered = await renderPageStream(pathname, opts);
|
|
214
188
|
if (!rendered)
|
|
215
189
|
return null;
|
|
216
|
-
const
|
|
190
|
+
const raw = rendered.stream ?? rendered;
|
|
191
|
+
if (!raw || typeof raw !== 'object' || typeof raw[Symbol.asyncIterator] !== 'function') {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
217
194
|
let html = '';
|
|
218
|
-
for await (const chunk of
|
|
195
|
+
for await (const chunk of raw) {
|
|
219
196
|
html += chunk;
|
|
220
197
|
}
|
|
221
198
|
return html;
|
|
@@ -224,9 +201,6 @@ async function renderPage(pathname, opts = {}) {
|
|
|
224
201
|
* Stream shell + Direct serialize body for the matched file-route page.
|
|
225
202
|
* Runs Page.access (closed allow/redirect/not-found/deny) before load;
|
|
226
203
|
* POST may run Page.action before re-render.
|
|
227
|
-
* @param {string} pathname
|
|
228
|
-
* @param {{ signal?: AbortSignal, searchParams?: URLSearchParams, cookieHeader?: string, method?: string, body?: unknown }} [opts]
|
|
229
|
-
* @returns {Promise<{ status: number, stream?: AsyncGenerator<string, void, void>, redirect?: string, headers?: Record<string, string> } | null>}
|
|
230
204
|
*/
|
|
231
205
|
async function renderPageStream(pathname, opts = {}) {
|
|
232
206
|
try {
|
|
@@ -239,11 +213,6 @@ async function renderPageStream(pathname, opts = {}) {
|
|
|
239
213
|
return { status: 500, stream: emitDevErrorHtml(normalized) };
|
|
240
214
|
}
|
|
241
215
|
}
|
|
242
|
-
/**
|
|
243
|
-
* @param {string} pathname
|
|
244
|
-
* @param {{ signal?: AbortSignal, searchParams?: URLSearchParams, cookieHeader?: string, method?: string, body?: unknown }} [opts]
|
|
245
|
-
* @returns {Promise<{ status: number, stream?: AsyncGenerator<string, void, void>, redirect?: string, headers?: Record<string, string> } | null>}
|
|
246
|
-
*/
|
|
247
216
|
async function renderPageStreamInner(pathname, opts = {}) {
|
|
248
217
|
if (isDev && lastDevError && pageCtors.size === 0) {
|
|
249
218
|
return { status: 500, stream: emitDevErrorHtml(lastDevError) };
|
|
@@ -376,10 +345,6 @@ async function renderPageStreamInner(pathname, opts = {}) {
|
|
|
376
345
|
stream: emitPageHtml(Page, match.chunkId, eventOnlyShell, props, opts, layoutChain, localeCtx),
|
|
377
346
|
};
|
|
378
347
|
}
|
|
379
|
-
/**
|
|
380
|
-
* @param {unknown} access
|
|
381
|
-
* @returns {{ kind: 'allow' } | { kind: 'redirect', location: string } | { kind: 'deny' } | { kind: 'not-found' }}
|
|
382
|
-
*/
|
|
383
348
|
function normalizeAccessResult(access) {
|
|
384
349
|
if (access == null || access === true)
|
|
385
350
|
return { kind: 'allow' };
|
|
@@ -392,10 +357,11 @@ function normalizeAccessResult(access) {
|
|
|
392
357
|
if (k === 'not-found' || k === 'notfound')
|
|
393
358
|
return { kind: 'not-found' };
|
|
394
359
|
}
|
|
395
|
-
if (typeof access === 'object') {
|
|
396
|
-
const
|
|
360
|
+
if (typeof access === 'object' && access) {
|
|
361
|
+
const row = access;
|
|
362
|
+
const kind = String(row.kind || row.type || 'allow').toLowerCase();
|
|
397
363
|
if (kind === 'redirect') {
|
|
398
|
-
const location = String(
|
|
364
|
+
const location = String(row.location || row.to || row.href || '');
|
|
399
365
|
if (!location)
|
|
400
366
|
return { kind: 'deny' };
|
|
401
367
|
return { kind: 'redirect', location };
|
|
@@ -408,30 +374,28 @@ function normalizeAccessResult(access) {
|
|
|
408
374
|
}
|
|
409
375
|
return { kind: 'allow' };
|
|
410
376
|
}
|
|
411
|
-
/**
|
|
412
|
-
* @param {unknown} acted
|
|
413
|
-
* @returns {{ kind: 'allow', props?: Record<string, unknown> } | { kind: 'redirect', location: string } | { kind: 'deny' } | { kind: 'not-found' }}
|
|
414
|
-
*/
|
|
415
377
|
function normalizeActionResult(acted) {
|
|
416
378
|
const base = normalizeAccessResult(acted);
|
|
417
379
|
if (base.kind !== 'allow')
|
|
418
380
|
return base;
|
|
419
|
-
if (acted && typeof acted === 'object' &&
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
381
|
+
if (acted && typeof acted === 'object' && !Array.isArray(acted)) {
|
|
382
|
+
const row = acted;
|
|
383
|
+
if (row.props && typeof row.props === 'object') {
|
|
384
|
+
return { kind: 'allow', props: row.props };
|
|
385
|
+
}
|
|
386
|
+
if (!('kind' in row) && !('type' in row)) {
|
|
387
|
+
return { kind: 'allow', props: row };
|
|
388
|
+
}
|
|
424
389
|
}
|
|
425
390
|
return { kind: 'allow' };
|
|
426
391
|
}
|
|
427
392
|
/**
|
|
428
393
|
* Minimal HTML for closed access/action results when no NotFound page exists.
|
|
429
|
-
* @param {string} marker
|
|
430
394
|
*/
|
|
431
395
|
async function* emitAccessShell(marker) {
|
|
432
396
|
const native = loadNativeAddon();
|
|
433
397
|
if (typeof native.generateHtmlShell !== 'function') {
|
|
434
|
-
throw new Error('vmz native addon missing generateHtmlShell
|
|
398
|
+
throw new Error('vmz native addon missing generateHtmlShell �?rebuild with `pnpm napi:build`');
|
|
435
399
|
}
|
|
436
400
|
yield native.generateHtmlShell({
|
|
437
401
|
title: 'App',
|
|
@@ -442,8 +406,7 @@ async function* emitAccessShell(marker) {
|
|
|
442
406
|
});
|
|
443
407
|
}
|
|
444
408
|
/**
|
|
445
|
-
* Prefer page `static meta()` for document title/description
|
|
446
|
-
* @param {any} Page
|
|
409
|
+
* Prefer page `static meta()` for document title/description �?never brand the framework in business HTML.
|
|
447
410
|
*/
|
|
448
411
|
function resolvePageDocumentMeta(Page) {
|
|
449
412
|
try {
|
|
@@ -460,14 +423,6 @@ function resolvePageDocumentMeta(Page) {
|
|
|
460
423
|
return { title: 'App', description: '' };
|
|
461
424
|
}
|
|
462
425
|
}
|
|
463
|
-
/**
|
|
464
|
-
* @param {any} Page
|
|
465
|
-
* @param {string} chunkId
|
|
466
|
-
* @param {boolean} eventOnlyShell
|
|
467
|
-
* @param {Record<string, unknown>} props
|
|
468
|
-
* @param {{ signal?: AbortSignal, searchParams?: URLSearchParams, cookieHeader?: string }} [opts]
|
|
469
|
-
* @param {string[]} [layoutChain] layout chunk ids outer→inner
|
|
470
|
-
*/
|
|
471
426
|
async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {}, layoutChain = [], localeCtx = {}) {
|
|
472
427
|
const signal = opts.signal;
|
|
473
428
|
const live = isDev
|
|
@@ -477,7 +432,7 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
477
432
|
let sawDisconnect = false;
|
|
478
433
|
es.onerror = () => { sawDisconnect = true; };
|
|
479
434
|
es.onopen = () => {
|
|
480
|
-
// Host respawn drops SSE
|
|
435
|
+
// Host respawn drops SSE �?reload once the new process is up (no manual restart).
|
|
481
436
|
if (sawDisconnect) location.reload();
|
|
482
437
|
};
|
|
483
438
|
function showOverlay(err) {
|
|
@@ -501,7 +456,7 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
501
456
|
+ "<p style=\\"margin:0 0 .5rem;color:#f87171;font-weight:700\\">Dev Error</p>"
|
|
502
457
|
+ "<pre style=\\"white-space:pre-wrap;margin:0 0 1rem;font-size:13px;line-height:1.45\\">" + esc(msg) + "</pre>"
|
|
503
458
|
+ (stack ? "<pre style=\\"white-space:pre-wrap;opacity:.7;font-size:12px\\">" + esc(stack) + "</pre>" : "")
|
|
504
|
-
+ "<p style=\\"opacity:.65;font-size:12px\\">Fix the file and save
|
|
459
|
+
+ "<p style=\\"opacity:.65;font-size:12px\\">Fix the file and save �?soft reload will clear this overlay.</p>"
|
|
505
460
|
+ "</div>";
|
|
506
461
|
}
|
|
507
462
|
function hideOverlay() {
|
|
@@ -568,7 +523,6 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
568
523
|
const propsJson = JSON.stringify(props ?? {});
|
|
569
524
|
const localeId = localeCtx.localeId || localeArtifact?.defaultLocale || 'en';
|
|
570
525
|
const dir = localeCtx.dir || 'ltr';
|
|
571
|
-
/** @type {string[]} */
|
|
572
526
|
const htmlExtraAttrs = [...htmlThemeAttrPair(themeId)];
|
|
573
527
|
if (localeArtifact?.routing) {
|
|
574
528
|
htmlExtraAttrs.push('data-vmz-locale-routing', JSON.stringify({
|
|
@@ -602,7 +556,7 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
602
556
|
}
|
|
603
557
|
if (signal?.aborted)
|
|
604
558
|
return;
|
|
605
|
-
// Wrap page HTML in layout chain (outer
|
|
559
|
+
// Wrap page HTML in layout chain (outer �?inner) via default slot injection.
|
|
606
560
|
for (let i = layoutChain.length - 1; i >= 0; i--) {
|
|
607
561
|
const Layout = await loadPageCtor(layoutChain[i]);
|
|
608
562
|
if (!Layout)
|
|
@@ -611,7 +565,7 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
611
565
|
if (signal?.aborted)
|
|
612
566
|
return;
|
|
613
567
|
}
|
|
614
|
-
// Locale discipline: apply frozen link plan rows (0.1.30)
|
|
568
|
+
// Locale discipline: apply frozen link plan rows (0.1.30) �?no path algebra.
|
|
615
569
|
if (localeArtifact && localeId) {
|
|
616
570
|
bodyHtml = localizeBodyLinks(bodyHtml, localeId, localeArtifact, undefined, localeLinkPlan);
|
|
617
571
|
}
|
|
@@ -626,7 +580,7 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
626
580
|
return;
|
|
627
581
|
const native = loadNativeAddon();
|
|
628
582
|
if (typeof native.generatePageShell !== 'function') {
|
|
629
|
-
throw new Error('vmz native addon missing generatePageShell
|
|
583
|
+
throw new Error('vmz native addon missing generatePageShell �?rebuild with `pnpm napi:build`');
|
|
630
584
|
}
|
|
631
585
|
const entrySrc = `/${eventOnlyShell ? 'entry-event.js' : 'entry-client.js'}?t=${reloadToken}`;
|
|
632
586
|
const cssHref = cssEntryWithBust(cssEntry);
|
|
@@ -690,7 +644,7 @@ const server = http.createServer((req, res) => {
|
|
|
690
644
|
.then((raw) => {
|
|
691
645
|
let payload = {};
|
|
692
646
|
try {
|
|
693
|
-
payload = raw ? JSON.parse(raw) : {};
|
|
647
|
+
payload = raw ? JSON.parse(String(raw)) : {};
|
|
694
648
|
}
|
|
695
649
|
catch {
|
|
696
650
|
payload = {};
|
|
@@ -739,7 +693,7 @@ async function gracefulShutdown(signal) {
|
|
|
739
693
|
return;
|
|
740
694
|
shuttingDown = true;
|
|
741
695
|
ready = false;
|
|
742
|
-
console.log(`vmz serve: ${signal}
|
|
696
|
+
console.log(`vmz serve: ${signal} �?draining in-flight=${inFlight} timeout=${SHUTDOWN_TIMEOUT_MS}ms`);
|
|
743
697
|
server.close();
|
|
744
698
|
const start = Date.now();
|
|
745
699
|
while (inFlight > 0 && Date.now() - start < SHUTDOWN_TIMEOUT_MS) {
|
|
@@ -765,8 +719,6 @@ process.on('SIGINT', () => {
|
|
|
765
719
|
/**
|
|
766
720
|
* Attach author Link RouteId aliases so localizeBodyLinks can match `data-vmz-route="IndexPage"`
|
|
767
721
|
* against realization rows keyed by chunk path (`pages/index`).
|
|
768
|
-
* @param {any} artifact
|
|
769
|
-
* @param {string} dir
|
|
770
722
|
*/
|
|
771
723
|
function attachLinkRouteAliases(artifact, dir) {
|
|
772
724
|
if (!artifact || typeof artifact !== 'object')
|
|
@@ -786,7 +738,6 @@ function attachLinkRouteAliases(artifact, dir) {
|
|
|
786
738
|
* Re-import routes / pages / components with a new cache-bust token.
|
|
787
739
|
* Keeps the HTTP server process alive (no Node restart).
|
|
788
740
|
* Failed reloads keep the previous in-memory modules (Vite-like resilience).
|
|
789
|
-
* @param {{ quiet?: boolean, payload?: { affectedChunks?: string[], seedChunks?: string[], emitted?: string[], full?: boolean, islandHmr?: boolean, buildId?: string, sourceRevision?: string, bundleRevision?: string, changed?: string[] } }} [opts]
|
|
790
741
|
*/
|
|
791
742
|
async function softReload(opts = {}) {
|
|
792
743
|
const prevToken = reloadToken;
|
|
@@ -805,7 +756,7 @@ async function softReload(opts = {}) {
|
|
|
805
756
|
const bundleRevision = opts.payload?.bundleRevision != null ? String(opts.payload.bundleRevision) : null;
|
|
806
757
|
if (buildId)
|
|
807
758
|
lastDevBuildId = buildId;
|
|
808
|
-
// 0.1.31: payload-only scope. `reloadToken` is opaque cache-bust
|
|
759
|
+
// 0.1.31: payload-only scope. `reloadToken` is opaque cache-bust �?never invent full/affected here.
|
|
809
760
|
const reloadAllPages = Boolean(full) || (!islandHmr && rerunLoaders.length === 0 && affected.length === 0);
|
|
810
761
|
try {
|
|
811
762
|
try {
|
|
@@ -837,7 +788,6 @@ async function softReload(opts = {}) {
|
|
|
837
788
|
});
|
|
838
789
|
const nextCatalog = await listPageClientFiles(distDir);
|
|
839
790
|
// empty catalog throws inside listPageClientFiles
|
|
840
|
-
/** @type {Map<string, any>} */
|
|
841
791
|
const nextCtors = new Map();
|
|
842
792
|
if (!islandHmr) {
|
|
843
793
|
const reloadSet = rerunLoaders.length > 0
|
|
@@ -937,7 +887,6 @@ async function softReload(opts = {}) {
|
|
|
937
887
|
throw err;
|
|
938
888
|
}
|
|
939
889
|
}
|
|
940
|
-
/** @param {string} chunkId @param {string[]} affected */
|
|
941
890
|
function pageNeedsReload(chunkId, affected) {
|
|
942
891
|
if (chunkId === 'pages/Layout' || chunkId.endsWith('/Layout'))
|
|
943
892
|
return true;
|
|
@@ -946,7 +895,6 @@ function pageNeedsReload(chunkId, affected) {
|
|
|
946
895
|
return id === chunkId || chunkId.startsWith(`${id}/`) || id.startsWith(`${chunkId}/`);
|
|
947
896
|
});
|
|
948
897
|
}
|
|
949
|
-
/** @param {string} event */
|
|
950
898
|
function notifySse(event) {
|
|
951
899
|
for (const client of [...sseClients]) {
|
|
952
900
|
try {
|
|
@@ -957,10 +905,9 @@ function notifySse(event) {
|
|
|
957
905
|
}
|
|
958
906
|
}
|
|
959
907
|
}
|
|
960
|
-
/** @param {unknown} err */
|
|
961
908
|
function normalizeDevError(err) {
|
|
962
909
|
if (err && typeof err === 'object') {
|
|
963
|
-
const e =
|
|
910
|
+
const e = err;
|
|
964
911
|
return {
|
|
965
912
|
message: e.message ? String(e.message) : String(err),
|
|
966
913
|
stack: e.stack ? String(e.stack) : undefined,
|
|
@@ -969,7 +916,6 @@ function normalizeDevError(err) {
|
|
|
969
916
|
}
|
|
970
917
|
return { message: String(err), at: Date.now() };
|
|
971
918
|
}
|
|
972
|
-
/** @param {{ message: string, stack?: string }} err */
|
|
973
919
|
async function* emitDevErrorHtml(err) {
|
|
974
920
|
const msg = escapeHtml(err.message || 'Unknown error');
|
|
975
921
|
const stack = err.stack ? escapeHtml(err.stack) : '';
|
|
@@ -985,7 +931,7 @@ async function* emitDevErrorHtml(err) {
|
|
|
985
931
|
<h1>Dev Error</h1>
|
|
986
932
|
<pre>${msg}</pre>
|
|
987
933
|
${stack ? `<pre style="opacity:.7;font-size:12px">${stack}</pre>` : ''}
|
|
988
|
-
<p class="hint">Dev host stayed up. Fix the source and save
|
|
934
|
+
<p class="hint">Dev host stayed up. Fix the source and save �?soft reload will recover.</p>
|
|
989
935
|
</main>
|
|
990
936
|
<script>
|
|
991
937
|
(() => {
|
|
@@ -1011,11 +957,10 @@ async function* emitDevErrorHtml(err) {
|
|
|
1011
957
|
}
|
|
1012
958
|
}
|
|
1013
959
|
catch {
|
|
1014
|
-
/* fall through to plain HTML
|
|
960
|
+
/* fall through to plain HTML �?never throw from the error page itself */
|
|
1015
961
|
}
|
|
1016
962
|
yield `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8" /><title>Dev Error</title></head><body>${body}</body></html>`;
|
|
1017
963
|
}
|
|
1018
|
-
/** @param {string} s */
|
|
1019
964
|
function escapeHtml(s) {
|
|
1020
965
|
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
1021
966
|
}
|
|
@@ -1024,8 +969,6 @@ function escapeHtml(s) {
|
|
|
1024
969
|
* - `prefix`: LocaleId from URL path (existing).
|
|
1025
970
|
* - `none`: Host preference from cookie `vmz.locale` (validated), else defaultLocale.
|
|
1026
971
|
* URL never carries LocaleId.
|
|
1027
|
-
* @param {string} pathname
|
|
1028
|
-
* @param {string | undefined} cookieHeader
|
|
1029
972
|
*/
|
|
1030
973
|
function resolveLocalePath(pathname, cookieHeader) {
|
|
1031
974
|
const raw = String(pathname || '/');
|
|
@@ -1073,10 +1016,6 @@ function resolveLocalePath(pathname, cookieHeader) {
|
|
|
1073
1016
|
redirectTo: null,
|
|
1074
1017
|
};
|
|
1075
1018
|
}
|
|
1076
|
-
/**
|
|
1077
|
-
* @param {string} chunkId
|
|
1078
|
-
* @param {string} localeId
|
|
1079
|
-
*/
|
|
1080
1019
|
function pageMetaAlternates(chunkId, localeId) {
|
|
1081
1020
|
if (!localeArtifact?.pageMetas)
|
|
1082
1021
|
return [];
|
|
@@ -1084,13 +1023,11 @@ function pageMetaAlternates(chunkId, localeId) {
|
|
|
1084
1023
|
localeArtifact.pageMetas.find((m) => m.routeId === chunkId && m.locale === localeArtifact.defaultLocale);
|
|
1085
1024
|
return Array.isArray(meta?.alternates) ? meta.alternates : [];
|
|
1086
1025
|
}
|
|
1087
|
-
/** @param {string} href */
|
|
1088
1026
|
function bustUrl(href) {
|
|
1089
1027
|
const u = new URL(href);
|
|
1090
1028
|
u.searchParams.set('t', String(reloadToken));
|
|
1091
1029
|
return u.href;
|
|
1092
1030
|
}
|
|
1093
|
-
/** @param {string} chunkId */
|
|
1094
1031
|
async function loadPageCtor(chunkId) {
|
|
1095
1032
|
const pageRel = `${chunkId}.client.js`;
|
|
1096
1033
|
const href = bustUrl(pathToFileURL(path.join(distDir, pageRel)).href);
|
|
@@ -1101,7 +1038,6 @@ async function loadPageCtor(chunkId) {
|
|
|
1101
1038
|
/**
|
|
1102
1039
|
* Load compiled page catalog from `_vmz/route-catalog.json` (0.1.30 authority).
|
|
1103
1040
|
* Hosts must not re-parse deployment pathPattern into a live catalog.
|
|
1104
|
-
* @param {string} dir
|
|
1105
1041
|
*/
|
|
1106
1042
|
async function listPageClientFiles(dir) {
|
|
1107
1043
|
const fromCatalog = await listPagesFromRouteCatalog(dir);
|
|
@@ -1114,11 +1050,7 @@ async function listPageClientFiles(dir) {
|
|
|
1114
1050
|
}
|
|
1115
1051
|
return fromCatalog;
|
|
1116
1052
|
}
|
|
1117
|
-
/**
|
|
1118
|
-
* @param {string} dir
|
|
1119
|
-
*/
|
|
1120
1053
|
async function listPagesFromRouteCatalog(dir) {
|
|
1121
|
-
/** @type {Array<{ chunkId: string, pageRel: string, routeId?: string, pathPattern?: string, segs: ReturnType<typeof parsePathPattern> }>} */
|
|
1122
1054
|
const out = [];
|
|
1123
1055
|
try {
|
|
1124
1056
|
const raw = await readFile(path.join(dir, ...ROUTE_CATALOG_REL.split('/')), 'utf8');
|
|
@@ -1150,14 +1082,12 @@ async function listPagesFromRouteCatalog(dir) {
|
|
|
1150
1082
|
}
|
|
1151
1083
|
/**
|
|
1152
1084
|
* Browser HTTP pattern (`/` / `/home` / `/users/:id` / `/blog/[...slug]`).
|
|
1153
|
-
* @param {string} pattern
|
|
1154
1085
|
*/
|
|
1155
1086
|
function parsePathPattern(pattern) {
|
|
1156
1087
|
const raw = String(pattern || '').trim();
|
|
1157
1088
|
if (!raw || raw === '/')
|
|
1158
1089
|
return [];
|
|
1159
1090
|
const parts = raw.replace(/^\/+/, '').split('/').filter(Boolean);
|
|
1160
|
-
/** @type {Array<{ kind: 'static' | 'param' | 'catch', value?: string, name?: string }>} */
|
|
1161
1091
|
const segs = [];
|
|
1162
1092
|
for (const p of parts) {
|
|
1163
1093
|
if (isRouteGroupDir(p))
|
|
@@ -1166,9 +1096,6 @@ function parsePathPattern(pattern) {
|
|
|
1166
1096
|
}
|
|
1167
1097
|
return segs;
|
|
1168
1098
|
}
|
|
1169
|
-
/**
|
|
1170
|
-
* @param {string} p
|
|
1171
|
-
*/
|
|
1172
1099
|
function parsePathSegment(p) {
|
|
1173
1100
|
const catchAll = /^\[\.\.\.([^\]]+)\]$/.exec(p);
|
|
1174
1101
|
const param = /^\[([^\]]+)\]$/.exec(p);
|
|
@@ -1187,10 +1114,6 @@ function isRouteGroupDir(seg) {
|
|
|
1187
1114
|
function isRouteBoundaryStem(stem) {
|
|
1188
1115
|
return stem === 'Layout' || stem === 'Loading' || stem === 'Error' || stem === 'NotFound';
|
|
1189
1116
|
}
|
|
1190
|
-
/**
|
|
1191
|
-
* @param {string} pathname
|
|
1192
|
-
* @param {typeof pageCatalog} catalog
|
|
1193
|
-
*/
|
|
1194
1117
|
function matchFileRoute(pathname, catalog) {
|
|
1195
1118
|
const pathParts = decodeURIComponent(pathname.split('?')[0] || '/')
|
|
1196
1119
|
.replace(/\/+$/, '')
|
|
@@ -1210,17 +1133,11 @@ function matchFileRoute(pathname, catalog) {
|
|
|
1210
1133
|
}
|
|
1211
1134
|
return best;
|
|
1212
1135
|
}
|
|
1213
|
-
/**
|
|
1214
|
-
* @param {ReturnType<typeof parsePathPattern>} segs
|
|
1215
|
-
* @param {string} pathname
|
|
1216
|
-
* @returns {Record<string, string>}
|
|
1217
|
-
*/
|
|
1218
1136
|
function extractRouteParams(segs, pathname) {
|
|
1219
1137
|
const pathParts = decodeURIComponent(pathname.split('?')[0] || '/')
|
|
1220
1138
|
.replace(/\/+$/, '')
|
|
1221
1139
|
.split('/')
|
|
1222
1140
|
.filter(Boolean);
|
|
1223
|
-
/** @type {Record<string, string>} */
|
|
1224
1141
|
const params = {};
|
|
1225
1142
|
let j = 0;
|
|
1226
1143
|
for (let i = 0; i < segs.length; i++) {
|
|
@@ -1239,11 +1156,6 @@ function extractRouteParams(segs, pathname) {
|
|
|
1239
1156
|
}
|
|
1240
1157
|
return params;
|
|
1241
1158
|
}
|
|
1242
|
-
/**
|
|
1243
|
-
* @param {ReturnType<typeof parsePathPattern>} segs
|
|
1244
|
-
* @param {string[]} pathParts
|
|
1245
|
-
* @returns {number | null}
|
|
1246
|
-
*/
|
|
1247
1159
|
function scoreRoute(segs, pathParts) {
|
|
1248
1160
|
let i = 0;
|
|
1249
1161
|
let j = 0;
|
|
@@ -1251,7 +1163,7 @@ function scoreRoute(segs, pathParts) {
|
|
|
1251
1163
|
while (i < segs.length) {
|
|
1252
1164
|
const s = segs[i];
|
|
1253
1165
|
if (s.kind === 'catch') {
|
|
1254
|
-
// Required catch-all `[...slug]` needs
|
|
1166
|
+
// Required catch-all `[...slug]` needs �? remaining segment (not `/`).
|
|
1255
1167
|
if (j >= pathParts.length)
|
|
1256
1168
|
return null;
|
|
1257
1169
|
score += 1;
|
|
@@ -1281,9 +1193,7 @@ function findRootCatchAll(catalog) {
|
|
|
1281
1193
|
return catalog.find((p) => isRootCatchAll(p)) || null;
|
|
1282
1194
|
}
|
|
1283
1195
|
/**
|
|
1284
|
-
* Optional app gate: `dist/vmz-route-gate.mjs`
|
|
1285
|
-
* @param {string} pathname
|
|
1286
|
-
* @param {string | undefined} chunkId
|
|
1196
|
+
* Optional app gate: `dist/vmz-route-gate.mjs` �?`{ check(pathname, chunkId) => 'not_found' | null }`.
|
|
1287
1197
|
*/
|
|
1288
1198
|
async function runRouteGate(pathname, chunkId) {
|
|
1289
1199
|
try {
|
|
@@ -1297,29 +1207,23 @@ async function runRouteGate(pathname, chunkId) {
|
|
|
1297
1207
|
return null;
|
|
1298
1208
|
}
|
|
1299
1209
|
}
|
|
1300
|
-
/**
|
|
1301
|
-
* @param {Array<{ name: string, entry: string }>} eager
|
|
1302
|
-
* @param {Array<{ name: string, entry: string }>} lazy
|
|
1303
|
-
* @param {number} token
|
|
1304
|
-
*/
|
|
1305
1210
|
function emitEntryClient(eager, lazy, token) {
|
|
1306
1211
|
const q = `?t=${token}`;
|
|
1307
1212
|
const native = loadNativeAddon();
|
|
1308
1213
|
if (typeof native.generateServeEntryClient !== 'function') {
|
|
1309
|
-
throw new Error('vmz native addon missing generateServeEntryClient
|
|
1214
|
+
throw new Error('vmz native addon missing generateServeEntryClient �?rebuild with `pnpm napi:build`');
|
|
1310
1215
|
}
|
|
1311
1216
|
return native.generateServeEntryClient(eager, lazy, q);
|
|
1312
1217
|
}
|
|
1313
1218
|
/**
|
|
1314
1219
|
* EventEntry zero-framework bootstrap: no static import of vmz-dom / page / islands.
|
|
1315
1220
|
* Framework bytes load only inside the first matching DOM event handler.
|
|
1316
|
-
* @param {number} token
|
|
1317
1221
|
*/
|
|
1318
1222
|
function emitEntryEvent(token) {
|
|
1319
1223
|
const q = `?t=${token}`;
|
|
1320
1224
|
const native = loadNativeAddon();
|
|
1321
1225
|
if (typeof native.generateServeEntryEvent !== 'function') {
|
|
1322
|
-
throw new Error('vmz native addon missing generateServeEntryEvent
|
|
1226
|
+
throw new Error('vmz native addon missing generateServeEntryEvent �?rebuild with `pnpm napi:build`');
|
|
1323
1227
|
}
|
|
1324
1228
|
return native.generateServeEntryEvent(q);
|
|
1325
1229
|
}
|
|
@@ -1327,11 +1231,9 @@ function emitEntryEvent(token) {
|
|
|
1327
1231
|
* Style Theme cookie / localStorage key (host contract, not a second theme API).
|
|
1328
1232
|
*/
|
|
1329
1233
|
const THEME_STORE_KEY = 'vmz-theme';
|
|
1330
|
-
/** Host preference key for `routing.strategy: 'none'` (cookie + localStorage). */
|
|
1331
1234
|
const LOCALE_STORE_KEY = 'vmz.locale';
|
|
1332
1235
|
/**
|
|
1333
1236
|
* Cache-bust stylesheet entry for dev reload (token + serve revision).
|
|
1334
|
-
* @param {string | null | undefined} entry
|
|
1335
1237
|
*/
|
|
1336
1238
|
function cssEntryWithBust(entry) {
|
|
1337
1239
|
if (!entry)
|
|
@@ -1343,10 +1245,6 @@ function cssEntryWithBust(entry) {
|
|
|
1343
1245
|
params.set('h', styleBundleHash);
|
|
1344
1246
|
return `${base}?${params.toString()}`;
|
|
1345
1247
|
}
|
|
1346
|
-
/**
|
|
1347
|
-
* @param {string} dir
|
|
1348
|
-
* @returns {Promise<{ cssEntry: string|null, styleTheme: typeof styleTheme, styleBundleHash: string|null }>}
|
|
1349
|
-
*/
|
|
1350
1248
|
async function loadDeploymentStyle(dir) {
|
|
1351
1249
|
try {
|
|
1352
1250
|
const raw = await readFile(path.join(dir, 'vmz-deployment.json'), 'utf8');
|
|
@@ -1374,11 +1272,8 @@ async function loadDeploymentStyle(dir) {
|
|
|
1374
1272
|
}
|
|
1375
1273
|
}
|
|
1376
1274
|
/**
|
|
1377
|
-
* Priority: `?theme=`
|
|
1275
|
+
* Priority: `?theme=` �?cookie �?none (CSS `:root` + prefers-color-scheme media).
|
|
1378
1276
|
* Explicit ids (including default) always win over OS preference via activation attr.
|
|
1379
|
-
* @param {URLSearchParams | undefined} searchParams
|
|
1380
|
-
* @param {string | undefined} cookieHeader
|
|
1381
|
-
* @returns {string|null}
|
|
1382
1277
|
*/
|
|
1383
1278
|
function resolveThemeId(searchParams, cookieHeader) {
|
|
1384
1279
|
if (!styleTheme)
|
|
@@ -1394,8 +1289,6 @@ function resolveThemeId(searchParams, cookieHeader) {
|
|
|
1394
1289
|
}
|
|
1395
1290
|
/**
|
|
1396
1291
|
* Always emit activation attr for an explicit theme id (incl. default) so it overrides OS media.
|
|
1397
|
-
* @param {string|null} themeId
|
|
1398
|
-
* @returns {[string, string] | []} flattened attr pair for generatePageShell
|
|
1399
1292
|
*/
|
|
1400
1293
|
function htmlThemeAttrPair(themeId) {
|
|
1401
1294
|
if (!styleTheme || !themeId)
|
|
@@ -1407,7 +1300,7 @@ function htmlThemeAttrPair(themeId) {
|
|
|
1407
1300
|
}
|
|
1408
1301
|
/**
|
|
1409
1302
|
* Inline boot when SSR had no query/cookie: apply explicit `localStorage` only.
|
|
1410
|
-
* No stored choice
|
|
1303
|
+
* No stored choice �?leave bare `<html>` so CSS `@media (prefers-color-scheme)` follows OS live.
|
|
1411
1304
|
* Explicit ids (incl. default) always set the activation attr so they override OS media.
|
|
1412
1305
|
*/
|
|
1413
1306
|
function themeBootstrapScript() {
|
|
@@ -1421,7 +1314,7 @@ function themeBootstrapScript() {
|
|
|
1421
1314
|
/**
|
|
1422
1315
|
* LocaleId as client state (routing.strategy = none): apply localStorage before any
|
|
1423
1316
|
* page/client module runs so `#locales/*` pick the right variant. Prefix strategy
|
|
1424
|
-
* keeps LocaleId in the URL
|
|
1317
|
+
* keeps LocaleId in the URL �?no boot rewrite.
|
|
1425
1318
|
* Also mirrors into cookie so the next SSR negotiate sees Host preference.
|
|
1426
1319
|
*/
|
|
1427
1320
|
function localeBootstrapScript() {
|
|
@@ -1438,8 +1331,8 @@ function localeBootstrapScript() {
|
|
|
1438
1331
|
return ` <script>(function(){try{var k=${key},ids=${idList};var id=localStorage.getItem(k);if(!id||ids.indexOf(id)<0)return;document.documentElement.setAttribute("data-locale",id);document.documentElement.setAttribute("lang",id);window.__vmzLocaleIdHint=id;document.cookie=k+"="+encodeURIComponent(id)+"; path=/; max-age=31536000; SameSite=Lax";}catch(e){}})();</script>\n`;
|
|
1439
1332
|
}
|
|
1440
1333
|
/**
|
|
1441
|
-
* Site favicon links from build artifact `_vmz/site-favicon.json` (author SVG
|
|
1442
|
-
* Empty when skipped / missing
|
|
1334
|
+
* Site favicon links from build artifact `_vmz/site-favicon.json` (author SVG �?PNG/ICO).
|
|
1335
|
+
* Empty when skipped / missing �?do not invent broken <link>s.
|
|
1443
1336
|
*/
|
|
1444
1337
|
function siteFaviconHeadHtml() {
|
|
1445
1338
|
try {
|
|
@@ -1457,10 +1350,6 @@ function siteFaviconHeadHtml() {
|
|
|
1457
1350
|
return '';
|
|
1458
1351
|
}
|
|
1459
1352
|
}
|
|
1460
|
-
/**
|
|
1461
|
-
* @param {string|undefined} header
|
|
1462
|
-
* @param {string} name
|
|
1463
|
-
*/
|
|
1464
1353
|
function readCookie(header, name) {
|
|
1465
1354
|
if (!header)
|
|
1466
1355
|
return null;
|
|
@@ -1481,19 +1370,10 @@ function readCookie(header, name) {
|
|
|
1481
1370
|
}
|
|
1482
1371
|
return null;
|
|
1483
1372
|
}
|
|
1484
|
-
/**
|
|
1485
|
-
* @param {string} dir
|
|
1486
|
-
* @returns {Promise<string|null>}
|
|
1487
|
-
*/
|
|
1488
1373
|
async function loadCssEntry(dir) {
|
|
1489
1374
|
const meta = await loadDeploymentStyle(dir);
|
|
1490
1375
|
return meta.cssEntry;
|
|
1491
1376
|
}
|
|
1492
|
-
/**
|
|
1493
|
-
* @param {string} dir
|
|
1494
|
-
* @param {string} chunkId
|
|
1495
|
-
* @returns {Promise<Array<{ component: string, strategy: string }>>}
|
|
1496
|
-
*/
|
|
1497
1377
|
async function loadPageResumeEntries(dir, chunkId) {
|
|
1498
1378
|
try {
|
|
1499
1379
|
const raw = await readFile(path.join(dir, 'vmz-deployment.json'), 'utf8');
|
|
@@ -1510,17 +1390,14 @@ async function loadPageResumeEntries(dir, chunkId) {
|
|
|
1510
1390
|
return [];
|
|
1511
1391
|
}
|
|
1512
1392
|
}
|
|
1513
|
-
/** @param {string} strategy */
|
|
1514
1393
|
function isEventStrategy(strategy) {
|
|
1515
1394
|
return strategy === 'event' || strategy === 'click' || strategy.startsWith('event:');
|
|
1516
1395
|
}
|
|
1517
|
-
/** @param {string[]} strategies */
|
|
1518
1396
|
function isEventOnlyShell(strategies) {
|
|
1519
1397
|
if (!strategies.length)
|
|
1520
1398
|
return false;
|
|
1521
1399
|
return strategies.every((s) => isEventStrategy(s));
|
|
1522
1400
|
}
|
|
1523
|
-
/** @param {string} s */
|
|
1524
1401
|
function escapeAttr(s) {
|
|
1525
1402
|
return String(s).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');
|
|
1526
1403
|
}
|