@vmz/vmz 0.0.4 → 0.1.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/build-assemble.d.ts +52 -0
- package/dist/build-assemble.js +192 -0
- package/dist/cdn-policy.d.ts +22 -4
- package/dist/cdn-policy.js +109 -13
- package/dist/cli.js +122 -27
- package/dist/content-addressed-assets.js +2 -1
- package/dist/delivery-profile.d.ts +84 -0
- package/dist/delivery-profile.js +348 -0
- package/dist/dev-session.d.ts +6 -0
- package/dist/dev-session.js +167 -40
- package/dist/document-build.js +33 -32
- package/dist/document-cmd.js +2 -9
- package/dist/document-enrich.js +8 -0
- package/dist/document-integrate.js +10 -17
- package/dist/embedded-packaging.d.ts +22 -0
- package/dist/embedded-packaging.js +109 -0
- package/dist/index.d.ts +26 -1
- package/dist/index.js +69 -2
- package/dist/locale-check.js +39 -66
- package/dist/locale-cmd.js +12 -44
- package/dist/locale-route-emit.d.ts +37 -0
- package/dist/locale-route-emit.js +109 -0
- package/dist/locale-router.d.ts +20 -0
- package/dist/locale-router.js +68 -0
- package/dist/log.d.ts +2 -2
- package/dist/log.js +11 -3
- package/dist/mini-host.d.ts +47 -0
- package/dist/mini-host.js +202 -0
- package/dist/native-addon.d.ts +9 -0
- package/dist/native-addon.js +84 -0
- package/dist/pack-client-packages.d.ts +25 -0
- package/dist/pack-client-packages.js +399 -0
- package/dist/pack.d.ts +58 -0
- package/dist/pack.js +123 -0
- package/dist/plugin-host.d.ts +1 -1
- package/dist/plugin-host.js +2 -2
- package/dist/pretty-json.d.ts +19 -0
- package/dist/pretty-json.js +43 -0
- package/dist/production-observability.js +3 -2
- package/dist/production-test-pack.d.ts +0 -14
- package/dist/production-test-pack.js +27 -31
- package/dist/release-pack.js +17 -21
- package/dist/route-path.d.ts +35 -0
- package/dist/route-path.js +77 -0
- package/dist/server-artifact.d.ts +140 -0
- package/dist/server-artifact.js +204 -0
- package/dist/server-language-backend.d.ts +89 -0
- package/dist/server-language-backend.js +121 -0
- package/dist/site-delivery.js +3 -2
- package/dist/static-emit.d.ts +10 -1
- package/dist/static-emit.js +192 -97
- package/dist/test-cmd.js +2 -1
- package/dist/wechat-packaging.d.ts +22 -0
- package/dist/wechat-packaging.js +59 -0
- package/package.json +12 -12
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Emit RouteId × LocaleId realization (+ PageMeta / hreflang seed) into dist/_vmz.
|
|
4
|
+
* Consumed by serve-host + static-emit; LocaleId stays out of stable RouteId.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { checkLocales, localeHasErrors } from './locale-check.js';
|
|
9
|
+
import { buildLocalePageMeta, buildLocaleRouteRealizationTable } from './locale-router.js';
|
|
10
|
+
import { writePrettyJsonFile } from './pretty-json.js';
|
|
11
|
+
import { listPublicPageUnits, unitBrowserPathPattern } from './route-path.js';
|
|
12
|
+
export const LOCALE_ROUTE_REALIZATION_ARTIFACT_SCHEMA = 'vmz.locale.route_realization.v0';
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} projectRoot
|
|
15
|
+
* @param {string} distDir
|
|
16
|
+
* @param {{ origin?: string }} [opts]
|
|
17
|
+
*/
|
|
18
|
+
export function emitLocaleRouteRealization(projectRoot, distDir, opts = {}) {
|
|
19
|
+
const report = checkLocales({ projectRoot, checkUnused: false });
|
|
20
|
+
if (localeHasErrors(report)) {
|
|
21
|
+
return { ok: false, written: [], diagnostics: report.diagnostics || [], artifact: null };
|
|
22
|
+
}
|
|
23
|
+
// Missing /locales is warning (not error) — still never silent; no realization artifact yet.
|
|
24
|
+
if (!report.manifest) {
|
|
25
|
+
return { ok: true, written: [], artifact: null, diagnostics: report.diagnostics || [] };
|
|
26
|
+
}
|
|
27
|
+
const deploymentPath = path.join(distDir, 'vmz-deployment.json');
|
|
28
|
+
if (!fs.existsSync(deploymentPath)) {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
written: [],
|
|
32
|
+
diagnostics: [{ severity: 'error', code: 'locale.route.missing_deployment', message: 'missing vmz-deployment.json' }],
|
|
33
|
+
artifact: null,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const deployment = JSON.parse(fs.readFileSync(deploymentPath, 'utf8'));
|
|
37
|
+
const pages = listPublicPageUnits(deployment);
|
|
38
|
+
const routes = pages.map((u) => ({
|
|
39
|
+
routeId: String(u.chunkId),
|
|
40
|
+
path: unitBrowserPathPattern(u),
|
|
41
|
+
}));
|
|
42
|
+
const localeEntries = report.manifest?.locales || [];
|
|
43
|
+
const locales = localeEntries.map((l) => l.id);
|
|
44
|
+
const directions = Object.fromEntries(localeEntries.map((l) => [l.id, l.direction || 'ltr']));
|
|
45
|
+
const defaultLocale = report.manifest?.defaultLocale;
|
|
46
|
+
const routing = report.manifest?.routing || { strategy: 'prefix', defaultPrefix: 'include' };
|
|
47
|
+
const table = buildLocaleRouteRealizationTable({
|
|
48
|
+
routes,
|
|
49
|
+
locales,
|
|
50
|
+
defaultLocale,
|
|
51
|
+
routing,
|
|
52
|
+
});
|
|
53
|
+
if (table.status === 'failed') {
|
|
54
|
+
return { ok: false, written: [], diagnostics: table.diagnostics || [], artifact: null };
|
|
55
|
+
}
|
|
56
|
+
const origin = String(opts.origin || process.env.VMZ_SITE_ORIGIN || 'https://example.test').replace(/\/$/, '');
|
|
57
|
+
/** @type {any[]} */
|
|
58
|
+
const pageMetas = [];
|
|
59
|
+
for (const route of routes) {
|
|
60
|
+
for (const loc of locales) {
|
|
61
|
+
const meta = buildLocalePageMeta({
|
|
62
|
+
routeId: route.routeId,
|
|
63
|
+
localeId: loc,
|
|
64
|
+
direction: directions[loc],
|
|
65
|
+
title: route.routeId,
|
|
66
|
+
origin,
|
|
67
|
+
realizations: table.realizations,
|
|
68
|
+
locales,
|
|
69
|
+
defaultLocale,
|
|
70
|
+
});
|
|
71
|
+
pageMetas.push(meta);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const artifact = {
|
|
75
|
+
schema: LOCALE_ROUTE_REALIZATION_ARTIFACT_SCHEMA,
|
|
76
|
+
defaultLocale,
|
|
77
|
+
locales: localeEntries.map((l) => ({
|
|
78
|
+
id: l.id,
|
|
79
|
+
label: l.label || l.id,
|
|
80
|
+
direction: l.direction || 'ltr',
|
|
81
|
+
})),
|
|
82
|
+
routing: {
|
|
83
|
+
strategy: routing.strategy || 'prefix',
|
|
84
|
+
defaultPrefix: routing.defaultPrefix || 'include',
|
|
85
|
+
defaultLocale,
|
|
86
|
+
},
|
|
87
|
+
origin,
|
|
88
|
+
routes,
|
|
89
|
+
realizations: table.realizations,
|
|
90
|
+
pageMetas,
|
|
91
|
+
};
|
|
92
|
+
const vmzDir = path.join(distDir, '_vmz');
|
|
93
|
+
fs.mkdirSync(vmzDir, { recursive: true });
|
|
94
|
+
const outPath = path.join(vmzDir, 'locale-route-realization.json');
|
|
95
|
+
writePrettyJsonFile(outPath, artifact);
|
|
96
|
+
const manifestOut = path.join(vmzDir, 'locale-manifest.json');
|
|
97
|
+
writePrettyJsonFile(manifestOut, {
|
|
98
|
+
schema: 'vmz.locale.manifest.v0',
|
|
99
|
+
defaultLocale,
|
|
100
|
+
locales: artifact.locales,
|
|
101
|
+
routing: artifact.routing,
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
ok: true,
|
|
105
|
+
written: ['_vmz/locale-route-realization.json', '_vmz/locale-manifest.json'],
|
|
106
|
+
artifact,
|
|
107
|
+
diagnostics: [],
|
|
108
|
+
};
|
|
109
|
+
}
|
package/dist/locale-router.d.ts
CHANGED
|
@@ -116,6 +116,26 @@ export declare function parseLocaleFromPath(pathname: any, supportedLocales: any
|
|
|
116
116
|
localeId: string;
|
|
117
117
|
restPath: string;
|
|
118
118
|
};
|
|
119
|
+
/**
|
|
120
|
+
* Rewrite a same-app href to the given LocaleId via route realization.
|
|
121
|
+
* Stable path (no locale) is recovered first, then re-realized — Link never hardcodes locale.
|
|
122
|
+
* @param {string} href
|
|
123
|
+
* @param {string} localeId
|
|
124
|
+
* @param {{
|
|
125
|
+
* locales?: Array<{ id: string }|string>,
|
|
126
|
+
* defaultLocale?: string,
|
|
127
|
+
* routing?: { strategy?: string, defaultPrefix?: string, defaultLocale?: string },
|
|
128
|
+
* }} artifact
|
|
129
|
+
*/
|
|
130
|
+
export declare function localizeSameAppHref(href: any, localeId: any, artifact: any): any;
|
|
131
|
+
/**
|
|
132
|
+
* Rewrite `<a data-vmz-route href>` in HTML body to retain `localeId`.
|
|
133
|
+
* @param {string} html
|
|
134
|
+
* @param {string} localeId
|
|
135
|
+
* @param {Parameters<typeof localizeSameAppHref>[2]} artifact
|
|
136
|
+
* @param {(s: string) => string} [escapeAttr]
|
|
137
|
+
*/
|
|
138
|
+
export declare function localizeBodyLinks(html: any, localeId: any, artifact: any, escapeAttr?: (s: any) => string): any;
|
|
119
139
|
/**
|
|
120
140
|
* Plan redirect / negotiation for an incoming URL (omit-prefix aware).
|
|
121
141
|
* @param {{
|
package/dist/locale-router.js
CHANGED
|
@@ -260,6 +260,74 @@ export function parseLocaleFromPath(pathname, supportedLocales) {
|
|
|
260
260
|
}
|
|
261
261
|
return { localeId: null, restPath: normalizePath(pathname) };
|
|
262
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Rewrite a same-app href to the given LocaleId via route realization.
|
|
265
|
+
* Stable path (no locale) is recovered first, then re-realized — Link never hardcodes locale.
|
|
266
|
+
* @param {string} href
|
|
267
|
+
* @param {string} localeId
|
|
268
|
+
* @param {{
|
|
269
|
+
* locales?: Array<{ id: string }|string>,
|
|
270
|
+
* defaultLocale?: string,
|
|
271
|
+
* routing?: { strategy?: string, defaultPrefix?: string, defaultLocale?: string },
|
|
272
|
+
* }} artifact
|
|
273
|
+
*/
|
|
274
|
+
export function localizeSameAppHref(href, localeId, artifact) {
|
|
275
|
+
if (!href || !localeId || !artifact)
|
|
276
|
+
return href;
|
|
277
|
+
if (href.startsWith('#') || /^(mailto|tel|javascript):/i.test(href))
|
|
278
|
+
return href;
|
|
279
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(href) && !href.startsWith('/'))
|
|
280
|
+
return href;
|
|
281
|
+
let pathname = String(href);
|
|
282
|
+
let search = '';
|
|
283
|
+
let hash = '';
|
|
284
|
+
const hashIdx = pathname.indexOf('#');
|
|
285
|
+
if (hashIdx >= 0) {
|
|
286
|
+
hash = pathname.slice(hashIdx);
|
|
287
|
+
pathname = pathname.slice(0, hashIdx);
|
|
288
|
+
}
|
|
289
|
+
const qIdx = pathname.indexOf('?');
|
|
290
|
+
if (qIdx >= 0) {
|
|
291
|
+
search = pathname.slice(qIdx);
|
|
292
|
+
pathname = pathname.slice(0, qIdx);
|
|
293
|
+
}
|
|
294
|
+
if (!pathname)
|
|
295
|
+
pathname = '/';
|
|
296
|
+
const supported = (artifact.locales || []).map((l) => (typeof l === 'string' ? l : l.id)).filter(Boolean);
|
|
297
|
+
const defaultLocale = artifact.defaultLocale || artifact.routing?.defaultLocale;
|
|
298
|
+
const routing = {
|
|
299
|
+
strategy: artifact.routing?.strategy || 'prefix',
|
|
300
|
+
defaultPrefix: artifact.routing?.defaultPrefix || 'include',
|
|
301
|
+
defaultLocale,
|
|
302
|
+
};
|
|
303
|
+
const parsed = parseLocaleFromPath(pathname, supported);
|
|
304
|
+
const rest = parsed.restPath || '/';
|
|
305
|
+
const realized = realizeRoutePath(localeId, rest, routing);
|
|
306
|
+
return `${realized.path}${search}${hash}`;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Rewrite `<a data-vmz-route href>` in HTML body to retain `localeId`.
|
|
310
|
+
* @param {string} html
|
|
311
|
+
* @param {string} localeId
|
|
312
|
+
* @param {Parameters<typeof localizeSameAppHref>[2]} artifact
|
|
313
|
+
* @param {(s: string) => string} [escapeAttr]
|
|
314
|
+
*/
|
|
315
|
+
export function localizeBodyLinks(html, localeId, artifact, escapeAttr = (s) => String(s).replace(/&/g, '&').replace(/"/g, '"')) {
|
|
316
|
+
if (!html || !localeId || !artifact)
|
|
317
|
+
return html;
|
|
318
|
+
return String(html).replace(/<a\b([^>]*)>/gi, (full, attrs) => {
|
|
319
|
+
if (!/\bdata-vmz-route\s*=/.test(attrs))
|
|
320
|
+
return full;
|
|
321
|
+
const hm = attrs.match(/\bhref\s*=\s*"([^"]*)"/i);
|
|
322
|
+
if (!hm)
|
|
323
|
+
return full;
|
|
324
|
+
const next = localizeSameAppHref(hm[1], localeId, artifact);
|
|
325
|
+
if (next === hm[1])
|
|
326
|
+
return full;
|
|
327
|
+
const newAttrs = attrs.replace(/\bhref\s*=\s*"[^"]*"/i, `href="${escapeAttr(next)}"`);
|
|
328
|
+
return `<a${newAttrs}>`;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
263
331
|
/**
|
|
264
332
|
* Plan redirect / negotiation for an incoming URL (omit-prefix aware).
|
|
265
333
|
* @param {{
|
package/dist/log.d.ts
CHANGED
|
@@ -8,10 +8,10 @@ export declare const log: {
|
|
|
8
8
|
warn(...args: any[]): void;
|
|
9
9
|
/** @param {...unknown} args */
|
|
10
10
|
error(...args: any[]): void;
|
|
11
|
-
/** @param {{ severity: string, path
|
|
11
|
+
/** @param {{ severity: string, path?: string, message: string, code?: string }} d */
|
|
12
12
|
diagnostic(d: any): void;
|
|
13
13
|
/**
|
|
14
|
-
* @param {Array<{ severity: string, path
|
|
14
|
+
* @param {Array<{ severity: string, path?: string, message: string, code?: string }>} diagnostics
|
|
15
15
|
* @param {{ denyWarnings?: boolean }} [opts]
|
|
16
16
|
* @returns {number} failing count (errors, and warnings if denyWarnings)
|
|
17
17
|
*/
|
package/dist/log.js
CHANGED
|
@@ -19,12 +19,20 @@ export const log = {
|
|
|
19
19
|
error(...args) {
|
|
20
20
|
console.error(stamp('error'), ...args);
|
|
21
21
|
},
|
|
22
|
-
/** @param {{ severity: string, path
|
|
22
|
+
/** @param {{ severity: string, path?: string, message: string, code?: string }} d */
|
|
23
23
|
diagnostic(d) {
|
|
24
|
-
|
|
24
|
+
const sev = d.severity || 'error';
|
|
25
|
+
const code = d.code ? `${d.code}: ` : '';
|
|
26
|
+
const loc = d.path ? ` (${d.path})` : '';
|
|
27
|
+
// Prefer `vmz warn|error CODE: message (path)` so locale warnings are visible, not silent.
|
|
28
|
+
if (sev === 'warning') {
|
|
29
|
+
console.error(`${stamp('warn')} ${code}${d.message}${loc}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
console.error(`${stamp(sev === 'error' ? 'error' : sev)} ${code}${d.path ? `${d.path}: ` : ''}${d.message}`);
|
|
25
33
|
},
|
|
26
34
|
/**
|
|
27
|
-
* @param {Array<{ severity: string, path
|
|
35
|
+
* @param {Array<{ severity: string, path?: string, message: string, code?: string }>} diagnostics
|
|
28
36
|
* @param {{ denyWarnings?: boolean }} [opts]
|
|
29
37
|
* @returns {number} failing count (errors, and warnings if denyWarnings)
|
|
30
38
|
*/
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VMZ-owned deterministic Mini Host (TemplateSurface).
|
|
3
|
+
* Interprets MiniProgramArtifact tables — not vendor runtime, not WXML.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Create a deterministic Mini Host over a deploy package + on-disk artifacts.
|
|
7
|
+
* @param {object} opts
|
|
8
|
+
* @param {MiniDeployPackage} opts.package
|
|
9
|
+
* @param {(artifactPath: string) => MiniPageArtifact} opts.loadArtifact
|
|
10
|
+
*/
|
|
11
|
+
export declare function createMiniHost(opts: any): {
|
|
12
|
+
package: any;
|
|
13
|
+
/** @param {string} [chunkId] */
|
|
14
|
+
mount(chunkId: any): {
|
|
15
|
+
chunkId: any;
|
|
16
|
+
data: {};
|
|
17
|
+
};
|
|
18
|
+
/** @param {string} handlerId */
|
|
19
|
+
dispatchEvent(handlerId: any): {
|
|
20
|
+
handlerId: any;
|
|
21
|
+
method: any;
|
|
22
|
+
patchPaths: any[];
|
|
23
|
+
data: {};
|
|
24
|
+
};
|
|
25
|
+
/** @param {string} routeId */
|
|
26
|
+
navigate(routeId: any): {
|
|
27
|
+
routeId: any;
|
|
28
|
+
chunkId: any;
|
|
29
|
+
data: {};
|
|
30
|
+
};
|
|
31
|
+
/** @param {string} method */
|
|
32
|
+
callServerStub(method: any): {
|
|
33
|
+
method: any;
|
|
34
|
+
scheme: string;
|
|
35
|
+
transport: string;
|
|
36
|
+
pending: boolean;
|
|
37
|
+
bodyShipped: boolean;
|
|
38
|
+
};
|
|
39
|
+
getState(): {
|
|
40
|
+
chunkId: any;
|
|
41
|
+
data: {};
|
|
42
|
+
appliedPatches: any[];
|
|
43
|
+
navigations: any[];
|
|
44
|
+
serverCalls: any[];
|
|
45
|
+
lifecycle: any[];
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VMZ-owned deterministic Mini Host (TemplateSurface).
|
|
3
|
+
* Interprets MiniProgramArtifact tables — not vendor runtime, not WXML.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {object} MiniDeployPackage
|
|
7
|
+
* @property {string} schema
|
|
8
|
+
* @property {string} [platformId]
|
|
9
|
+
* @property {object} [host]
|
|
10
|
+
* @property {object} [vendorTooling]
|
|
11
|
+
* @property {object} [constraints]
|
|
12
|
+
* @property {Array<{chunkId?: string, artifactPath?: string}>} [artifacts]
|
|
13
|
+
* @property {Array<{routeId?: string, chunkId?: string}>} [pages]
|
|
14
|
+
* @property {Array<{routeId?: string, fromChunkId?: string}>} [routeLinks]
|
|
15
|
+
* @property {Array<{method?: string, chunkId?: string}>} [serverCapabilities]
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {object} MiniPageArtifact
|
|
19
|
+
* @property {string} [template]
|
|
20
|
+
* @property {string} [logic]
|
|
21
|
+
* @property {string} [eventTable]
|
|
22
|
+
* @property {string} [dataPatchTable]
|
|
23
|
+
* @property {string} [manifest]
|
|
24
|
+
* @property {string} [style]
|
|
25
|
+
* @property {string} [platformId]
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* @param {unknown} value
|
|
29
|
+
* @returns {any}
|
|
30
|
+
*/
|
|
31
|
+
function parseMaybeJson(value) {
|
|
32
|
+
if (value == null)
|
|
33
|
+
return null;
|
|
34
|
+
if (typeof value === 'object')
|
|
35
|
+
return value;
|
|
36
|
+
if (typeof value === 'string') {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(value);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} path
|
|
48
|
+
* @param {Record<string, unknown>} data
|
|
49
|
+
* @param {unknown} value
|
|
50
|
+
*/
|
|
51
|
+
function setDataPath(path, data, value) {
|
|
52
|
+
const parts = String(path).split('.').filter(Boolean);
|
|
53
|
+
if (!parts.length)
|
|
54
|
+
return;
|
|
55
|
+
/** @type {any} */
|
|
56
|
+
let cur = data;
|
|
57
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
58
|
+
const key = parts[i];
|
|
59
|
+
if (cur[key] == null || typeof cur[key] !== 'object')
|
|
60
|
+
cur[key] = {};
|
|
61
|
+
cur = cur[key];
|
|
62
|
+
}
|
|
63
|
+
cur[parts[parts.length - 1]] = value;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Create a deterministic Mini Host over a deploy package + on-disk artifacts.
|
|
67
|
+
* @param {object} opts
|
|
68
|
+
* @param {MiniDeployPackage} opts.package
|
|
69
|
+
* @param {(artifactPath: string) => MiniPageArtifact} opts.loadArtifact
|
|
70
|
+
*/
|
|
71
|
+
export function createMiniHost(opts) {
|
|
72
|
+
const pkg = opts.package;
|
|
73
|
+
if (!pkg || pkg.schema !== 'vmz.mini.deploy_package.v0') {
|
|
74
|
+
throw new Error(`createMiniHost: expected vmz.mini.deploy_package.v0, got ${pkg?.schema}`);
|
|
75
|
+
}
|
|
76
|
+
if (pkg.constraints?.wxmlEmitter === true || pkg.constraints?.wxssEmitter === true) {
|
|
77
|
+
throw new Error('createMiniHost: package must not claim WXML/WXSS emitter');
|
|
78
|
+
}
|
|
79
|
+
if (pkg.constraints?.serverImplInMiniPackage === true) {
|
|
80
|
+
throw new Error('createMiniHost: server impl must not ship in mini package');
|
|
81
|
+
}
|
|
82
|
+
if (pkg.host?.schema !== 'vmz.mini.host.v0' || pkg.host?.kind !== 'deterministic-interpreter') {
|
|
83
|
+
throw new Error(`createMiniHost: invalid host descriptor ${JSON.stringify(pkg.host)}`);
|
|
84
|
+
}
|
|
85
|
+
if (pkg.vendorTooling?.role !== 'transport-conformance') {
|
|
86
|
+
throw new Error('createMiniHost: vendor tooling must stay transport-conformance');
|
|
87
|
+
}
|
|
88
|
+
if (pkg.vendorTooling?.invokedInCi !== false) {
|
|
89
|
+
throw new Error('createMiniHost: vendor tooling must not be invoked in CI gate');
|
|
90
|
+
}
|
|
91
|
+
/** @type {string | null} */
|
|
92
|
+
let currentChunkId = null;
|
|
93
|
+
/** @type {MiniPageArtifact | null} */
|
|
94
|
+
let currentArt = null;
|
|
95
|
+
/** @type {Record<string, unknown>} */
|
|
96
|
+
let data = {};
|
|
97
|
+
/** @type {string[]} */
|
|
98
|
+
const appliedPatches = [];
|
|
99
|
+
/** @type {Array<{method: string, scheme: string}>} */
|
|
100
|
+
const serverCalls = [];
|
|
101
|
+
/** @type {string[]} */
|
|
102
|
+
const navigations = [];
|
|
103
|
+
/** @type {string[]} */
|
|
104
|
+
const lifecycle = [];
|
|
105
|
+
function resolveArtifact(chunkId) {
|
|
106
|
+
const row = (pkg.artifacts || []).find((a) => a.chunkId === chunkId);
|
|
107
|
+
if (!row?.artifactPath) {
|
|
108
|
+
throw new Error(`createMiniHost: unknown chunk ${chunkId}`);
|
|
109
|
+
}
|
|
110
|
+
return opts.loadArtifact(row.artifactPath);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
package: pkg,
|
|
114
|
+
/** @param {string} [chunkId] */
|
|
115
|
+
mount(chunkId) {
|
|
116
|
+
const pages = pkg.pages || [];
|
|
117
|
+
const home = pages.find((p) => p.routeId === 'IndexPage' || String(p.chunkId || '').includes('pages/index')) || pages[0];
|
|
118
|
+
const pick = chunkId || home?.chunkId || pkg.artifacts?.[0]?.chunkId;
|
|
119
|
+
if (!pick)
|
|
120
|
+
throw new Error('createMiniHost.mount: no page chunk');
|
|
121
|
+
currentArt = resolveArtifact(pick);
|
|
122
|
+
currentChunkId = pick;
|
|
123
|
+
const logic = parseMaybeJson(currentArt.logic) || {};
|
|
124
|
+
data = structuredClone(logic.initialData || {});
|
|
125
|
+
lifecycle.push('mount');
|
|
126
|
+
return { chunkId: pick, data };
|
|
127
|
+
},
|
|
128
|
+
/** @param {string} handlerId */
|
|
129
|
+
dispatchEvent(handlerId) {
|
|
130
|
+
if (!currentArt)
|
|
131
|
+
throw new Error('createMiniHost.dispatchEvent: not mounted');
|
|
132
|
+
const events = parseMaybeJson(currentArt.eventTable);
|
|
133
|
+
if (!events || events.schema !== 'vmz.mini.event_table.v0') {
|
|
134
|
+
throw new Error('createMiniHost: missing event_table');
|
|
135
|
+
}
|
|
136
|
+
const handler = (events.handlers || []).find((h) => h.handlerId === handlerId);
|
|
137
|
+
if (!handler)
|
|
138
|
+
throw new Error(`createMiniHost: unknown handler ${handlerId}`);
|
|
139
|
+
const stamp = `patched:${handler.method || handlerId}`;
|
|
140
|
+
for (const p of handler.patchPaths || []) {
|
|
141
|
+
setDataPath(p, data, stamp);
|
|
142
|
+
appliedPatches.push(p);
|
|
143
|
+
}
|
|
144
|
+
lifecycle.push(`event:${handlerId}`);
|
|
145
|
+
return {
|
|
146
|
+
handlerId,
|
|
147
|
+
method: handler.method,
|
|
148
|
+
patchPaths: [...(handler.patchPaths || [])],
|
|
149
|
+
data,
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
/** @param {string} routeId */
|
|
153
|
+
navigate(routeId) {
|
|
154
|
+
const pages = pkg.pages || [];
|
|
155
|
+
const hit = pages.find((p) => p.routeId === routeId) || pages.find((p) => String(p.chunkId || '').includes(routeId));
|
|
156
|
+
if (!hit?.chunkId) {
|
|
157
|
+
throw new Error(`createMiniHost.navigate: unknown RouteId ${routeId}`);
|
|
158
|
+
}
|
|
159
|
+
const links = pkg.routeLinks || [];
|
|
160
|
+
const linked = !links.length || links.some((l) => l.routeId === routeId) || String(currentChunkId || '') === hit.chunkId;
|
|
161
|
+
if (!linked && currentChunkId) {
|
|
162
|
+
// Allow workspace-level pages even without a Link from current page.
|
|
163
|
+
}
|
|
164
|
+
currentArt = resolveArtifact(hit.chunkId);
|
|
165
|
+
currentChunkId = hit.chunkId;
|
|
166
|
+
const logic = parseMaybeJson(currentArt.logic) || {};
|
|
167
|
+
data = structuredClone(logic.initialData || {});
|
|
168
|
+
navigations.push(routeId);
|
|
169
|
+
lifecycle.push(`navigate:${routeId}`);
|
|
170
|
+
return { routeId, chunkId: hit.chunkId, data };
|
|
171
|
+
},
|
|
172
|
+
/** @param {string} method */
|
|
173
|
+
callServerStub(method) {
|
|
174
|
+
const caps = pkg.serverCapabilities || [];
|
|
175
|
+
const hit = caps.find((c) => c.method === method);
|
|
176
|
+
if (!hit)
|
|
177
|
+
throw new Error(`createMiniHost.callServerStub: unknown ${method}`);
|
|
178
|
+
if (pkg.constraints?.serverImplInMiniPackage !== false) {
|
|
179
|
+
throw new Error('createMiniHost: server impl leaked into mini package');
|
|
180
|
+
}
|
|
181
|
+
serverCalls.push({ method, scheme: '#server' });
|
|
182
|
+
lifecycle.push(`server-stub:${method}`);
|
|
183
|
+
return {
|
|
184
|
+
method,
|
|
185
|
+
scheme: '#server',
|
|
186
|
+
transport: 'mini-request',
|
|
187
|
+
pending: true,
|
|
188
|
+
bodyShipped: false,
|
|
189
|
+
};
|
|
190
|
+
},
|
|
191
|
+
getState() {
|
|
192
|
+
return {
|
|
193
|
+
chunkId: currentChunkId,
|
|
194
|
+
data: structuredClone(data),
|
|
195
|
+
appliedPatches: [...appliedPatches],
|
|
196
|
+
navigations: [...navigations],
|
|
197
|
+
serverCalls: [...serverCalls],
|
|
198
|
+
lifecycle: [...lifecycle],
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Load the N-API `.node` addon without importing `index.js`
|
|
4
|
+
* (avoids cycles with modules re-exported from the package entry).
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import { createRequire } from 'node:module';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
/** @type {any} */
|
|
12
|
+
let cached;
|
|
13
|
+
/**
|
|
14
|
+
* Load the N-API addon or throw (production printers must not fall back to TS).
|
|
15
|
+
* @returns {any}
|
|
16
|
+
*/
|
|
17
|
+
export function requireNativeAddon() {
|
|
18
|
+
const native = tryLoadNativeAddon();
|
|
19
|
+
if (!native) {
|
|
20
|
+
throw new Error('vmz native addon missing — run `pnpm napi:build` (CodeGenerators live in vmz-generator via N-API)');
|
|
21
|
+
}
|
|
22
|
+
return native;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @returns {any | null}
|
|
26
|
+
*/
|
|
27
|
+
export function tryLoadNativeAddon() {
|
|
28
|
+
if (cached !== undefined)
|
|
29
|
+
return cached;
|
|
30
|
+
try {
|
|
31
|
+
const envPath = (typeof process.env.VMZ_NATIVE_NODE === 'string' && process.env.VMZ_NATIVE_NODE.trim()) || '';
|
|
32
|
+
if (envPath) {
|
|
33
|
+
cached = require(path.resolve(envPath));
|
|
34
|
+
return cached;
|
|
35
|
+
}
|
|
36
|
+
const { platform, arch } = process;
|
|
37
|
+
let triple = `${platform}-${arch}`;
|
|
38
|
+
if (platform === 'win32' && arch === 'x64')
|
|
39
|
+
triple = 'win32-x64-msvc';
|
|
40
|
+
else if (platform === 'win32' && arch === 'arm64')
|
|
41
|
+
triple = 'win32-arm64-msvc';
|
|
42
|
+
else if (platform === 'darwin' && arch === 'arm64')
|
|
43
|
+
triple = 'darwin-arm64';
|
|
44
|
+
else if (platform === 'darwin' && arch === 'x64')
|
|
45
|
+
triple = 'darwin-x64';
|
|
46
|
+
else if (platform === 'linux' && arch === 'x64')
|
|
47
|
+
triple = 'linux-x64-gnu';
|
|
48
|
+
else if (platform === 'linux' && arch === 'arm64')
|
|
49
|
+
triple = 'linux-arm64-gnu';
|
|
50
|
+
const short = triple === 'win32-x64-msvc'
|
|
51
|
+
? 'win32-x64'
|
|
52
|
+
: triple === 'win32-arm64-msvc'
|
|
53
|
+
? 'win32-arm64'
|
|
54
|
+
: triple === 'linux-x64-gnu'
|
|
55
|
+
? 'linux-x64'
|
|
56
|
+
: triple === 'linux-arm64-gnu'
|
|
57
|
+
? 'linux-arm64'
|
|
58
|
+
: triple;
|
|
59
|
+
const pkgRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
60
|
+
const name = `@vmz/vmz-${short}`;
|
|
61
|
+
/** @type {string[]} */
|
|
62
|
+
const candidates = [];
|
|
63
|
+
try {
|
|
64
|
+
const resolved = require.resolve(`${name}/package.json`);
|
|
65
|
+
const dir = path.dirname(resolved);
|
|
66
|
+
candidates.push(path.join(dir, `vmz.${triple}.node`), path.join(dir, 'vmz.node'));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
/* optional */
|
|
70
|
+
}
|
|
71
|
+
candidates.push(path.join(pkgRoot, 'node_modules', name, `vmz.${triple}.node`), path.join(pkgRoot, 'node_modules', name, 'vmz.node'));
|
|
72
|
+
for (const p of candidates) {
|
|
73
|
+
if (fs.existsSync(p)) {
|
|
74
|
+
cached = require(p);
|
|
75
|
+
return cached;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
cached = null;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
cached = null;
|
|
82
|
+
}
|
|
83
|
+
return cached;
|
|
84
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe client package lowering (Pack stage thin slice).
|
|
3
|
+
*
|
|
4
|
+
* Bare npm/workspace imports are legal on the author surface (01/04).
|
|
5
|
+
* Browser ESM cannot resolve them. Pack materializes reachable package
|
|
6
|
+
* modules under `dist/vendor/<pkg>/…` and rewrites importers to relative paths.
|
|
7
|
+
*
|
|
8
|
+
* Not full oxc chunk-split/minify (`oxc-pending` remains for release minify).
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} outDir
|
|
12
|
+
* @param {{ projectRoot?: string | null }} [opts]
|
|
13
|
+
* @returns {{ rewrittenFiles: number, vendoredModules: string[], bareSpecs: string[] }}
|
|
14
|
+
*/
|
|
15
|
+
export declare function packClientBareImports(outDir: any, opts?: {}): {
|
|
16
|
+
rewrittenFiles: number;
|
|
17
|
+
vendoredModules: string[];
|
|
18
|
+
bareSpecs: any[];
|
|
19
|
+
unresolvedBareSpecs: any[];
|
|
20
|
+
skippedVmzExports: any[];
|
|
21
|
+
remainingBareSpecs: any[];
|
|
22
|
+
};
|
|
23
|
+
/** @param {string} js */
|
|
24
|
+
export declare function collectBareSpecs(js: any): unknown[];
|
|
25
|
+
export declare function rewriteRelativeTsSpecs(js: any): any;
|