@vmz/vmz 0.1.0 → 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.
Files changed (40) hide show
  1. package/dist/build-assemble.js +4 -3
  2. package/dist/cdn-policy.js +5 -8
  3. package/dist/cli.js +20 -7
  4. package/dist/content-addressed-assets.js +2 -1
  5. package/dist/delivery-profile.d.ts +10 -0
  6. package/dist/delivery-profile.js +69 -0
  7. package/dist/dev-session.d.ts +6 -0
  8. package/dist/dev-session.js +166 -40
  9. package/dist/document-build.js +24 -28
  10. package/dist/document-cmd.js +2 -9
  11. package/dist/document-integrate.js +10 -17
  12. package/dist/embedded-packaging.js +3 -7
  13. package/dist/index.d.ts +19 -1
  14. package/dist/index.js +62 -2
  15. package/dist/locale-check.js +28 -58
  16. package/dist/locale-cmd.js +10 -42
  17. package/dist/locale-route-emit.d.ts +4 -1
  18. package/dist/locale-route-emit.js +7 -32
  19. package/dist/mini-host.d.ts +47 -0
  20. package/dist/mini-host.js +202 -0
  21. package/dist/native-addon.d.ts +9 -0
  22. package/dist/native-addon.js +84 -0
  23. package/dist/pack-client-packages.d.ts +25 -0
  24. package/dist/pack-client-packages.js +399 -0
  25. package/dist/pack.d.ts +21 -3
  26. package/dist/pack.js +21 -6
  27. package/dist/pretty-json.d.ts +19 -0
  28. package/dist/pretty-json.js +43 -0
  29. package/dist/production-observability.js +3 -2
  30. package/dist/production-test-pack.js +4 -3
  31. package/dist/release-pack.js +5 -18
  32. package/dist/route-path.d.ts +35 -0
  33. package/dist/route-path.js +77 -0
  34. package/dist/server-artifact.js +5 -6
  35. package/dist/site-delivery.js +3 -2
  36. package/dist/static-emit.js +67 -86
  37. package/dist/test-cmd.js +2 -1
  38. package/dist/wechat-packaging.d.ts +22 -0
  39. package/dist/wechat-packaging.js +59 -0
  40. package/package.json +12 -12
@@ -7,34 +7,9 @@ import fs from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import { checkLocales, localeHasErrors } from './locale-check.js';
9
9
  import { buildLocalePageMeta, buildLocaleRouteRealizationTable } from './locale-router.js';
10
+ import { writePrettyJsonFile } from './pretty-json.js';
11
+ import { listPublicPageUnits, unitBrowserPathPattern } from './route-path.js';
10
12
  export const LOCALE_ROUTE_REALIZATION_ARTIFACT_SCHEMA = 'vmz.locale.route_realization.v0';
11
- /**
12
- * @param {string} chunkId
13
- */
14
- function pathPatternFromChunk(chunkId) {
15
- const rel = String(chunkId || '').replace(/^pages\//, '');
16
- const parts = rel.split('/').filter(Boolean);
17
- const segs = [];
18
- for (let i = 0; i < parts.length; i++) {
19
- const p = parts[i];
20
- if (p === 'index' && i === parts.length - 1)
21
- continue;
22
- segs.push(p);
23
- }
24
- return segs.length ? `/${segs.join('/')}` : '/';
25
- }
26
- /**
27
- * Layouts are not public RouteNodes for locale realization / SEO.
28
- * @param {string} chunkId
29
- */
30
- function isPublicPageChunk(chunkId) {
31
- const id = String(chunkId || '');
32
- if (!id.startsWith('pages/'))
33
- return false;
34
- if (/(^|\/)Layout$/.test(id))
35
- return false;
36
- return true;
37
- }
38
13
  /**
39
14
  * @param {string} projectRoot
40
15
  * @param {string} distDir
@@ -59,10 +34,10 @@ export function emitLocaleRouteRealization(projectRoot, distDir, opts = {}) {
59
34
  };
60
35
  }
61
36
  const deployment = JSON.parse(fs.readFileSync(deploymentPath, 'utf8'));
62
- const pages = (deployment.units || []).filter((u) => u.kind === 'page' && isPublicPageChunk(String(u.chunkId)));
37
+ const pages = listPublicPageUnits(deployment);
63
38
  const routes = pages.map((u) => ({
64
39
  routeId: String(u.chunkId),
65
- path: pathPatternFromChunk(String(u.chunkId)),
40
+ path: unitBrowserPathPattern(u),
66
41
  }));
67
42
  const localeEntries = report.manifest?.locales || [];
68
43
  const locales = localeEntries.map((l) => l.id);
@@ -117,14 +92,14 @@ export function emitLocaleRouteRealization(projectRoot, distDir, opts = {}) {
117
92
  const vmzDir = path.join(distDir, '_vmz');
118
93
  fs.mkdirSync(vmzDir, { recursive: true });
119
94
  const outPath = path.join(vmzDir, 'locale-route-realization.json');
120
- fs.writeFileSync(outPath, `${JSON.stringify(artifact, null, 2)}\n`, 'utf8');
95
+ writePrettyJsonFile(outPath, artifact);
121
96
  const manifestOut = path.join(vmzDir, 'locale-manifest.json');
122
- fs.writeFileSync(manifestOut, `${JSON.stringify({
97
+ writePrettyJsonFile(manifestOut, {
123
98
  schema: 'vmz.locale.manifest.v0',
124
99
  defaultLocale,
125
100
  locales: artifact.locales,
126
101
  routing: artifact.routing,
127
- }, null, 2)}\n`, 'utf8');
102
+ });
128
103
  return {
129
104
  ok: true,
130
105
  written: ['_vmz/locale-route-realization.json', '_vmz/locale-manifest.json'],
@@ -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,9 @@
1
+ /**
2
+ * Load the N-API addon or throw (production printers must not fall back to TS).
3
+ * @returns {any}
4
+ */
5
+ export declare function requireNativeAddon(): any;
6
+ /**
7
+ * @returns {any | null}
8
+ */
9
+ export declare function tryLoadNativeAddon(): any;
@@ -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;