@vmz/vmz 0.3.12 → 0.3.13

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 (51) hide show
  1. package/delivery-runtime-files.json +1 -0
  2. package/dist/cli/cli.js +46 -23
  3. package/dist/cli/format-cmd.d.ts +9 -17
  4. package/dist/cli/format-cmd.js +37 -49
  5. package/dist/cli/format-config.d.ts +6 -9
  6. package/dist/cli/format-config.js +5 -29
  7. package/dist/delivery/delivery-profile.js +1 -1
  8. package/dist/dev/dev-session.js +16 -5
  9. package/dist/document/document-base-css.d.ts +15 -0
  10. package/dist/document/document-base-css.js +578 -0
  11. package/dist/document/document-build.d.ts +1 -0
  12. package/dist/document/document-build.js +94 -78
  13. package/dist/document/document-client.d.ts +13 -0
  14. package/dist/document/document-client.js +217 -0
  15. package/dist/document/document-cmd.js +3 -1
  16. package/dist/document/document-designs.d.ts +9 -3
  17. package/dist/document/document-designs.js +20 -34
  18. package/dist/document/document-enrich.d.ts +9 -0
  19. package/dist/document/document-enrich.js +55 -0
  20. package/dist/document/document-highlight.d.ts +26 -0
  21. package/dist/document/document-highlight.js +54 -0
  22. package/dist/document/document-integrate.d.ts +5 -0
  23. package/dist/document/document-integrate.js +1 -0
  24. package/dist/document/document-interactive.js +10 -2
  25. package/dist/document/document-layout-render.js +6 -2
  26. package/dist/document/document-schema.d.ts +5 -0
  27. package/dist/workspace/build-assemble.js +18 -6
  28. package/dist/workspace/content-addressed-assets.d.ts +6 -17
  29. package/dist/workspace/content-addressed-assets.js +10 -95
  30. package/dist/workspace/delivery-runtime-imports.js +3 -0
  31. package/dist/workspace/native-addon.d.ts +4 -0
  32. package/dist/workspace/pack-native.d.ts +139 -0
  33. package/dist/workspace/pack-native.js +273 -0
  34. package/dist/workspace/pack.d.ts +112 -32
  35. package/dist/workspace/pack.js +270 -65
  36. package/dist/workspace/plugin-host.d.ts +4 -4
  37. package/dist/workspace/plugin-host.js +6 -6
  38. package/dist/workspace/production-observability.d.ts +2 -0
  39. package/dist/workspace/production-observability.js +6 -2
  40. package/dist/workspace/project-build.js +26 -4
  41. package/dist/workspace/public-api.d.ts +7 -3
  42. package/dist/workspace/public-api.js +5 -3
  43. package/dist/workspace/static-emit.d.ts +30 -0
  44. package/dist/workspace/static-emit.js +211 -18
  45. package/dist/workspace/user-config.d.ts +112 -0
  46. package/dist/workspace/user-config.js +12 -0
  47. package/host-runtime-files.json +1 -0
  48. package/locales/en-US/cli.json +5 -4
  49. package/package.json +20 -20
  50. package/dist/workspace/pack-client-packages.d.ts +0 -34
  51. package/dist/workspace/pack-client-packages.js +0 -550
@@ -1,26 +1,69 @@
1
1
  /**
2
- * Pack stage: consume Deployment IR (VPG-owned units), emit pack manifest,
3
- * and lower browser-unreachable bare package imports to `dist/vendor/**`.
2
+ * Pack stage: run the browser delivery graph through the Rust `vmz-pack`
3
+ * binding (N-API `packPlan`/`packWrite`, frozen v0 contract) and record the
4
+ * resulting chunk/asset mapping in `_vmz/pack-manifest.json`.
4
5
  *
5
- * Current status: a thin module-moving slice — reachable module closure
6
- * materialization, specifier rewriting, and TypeScript `transpileModule` type
7
- * erasure. Linking, dead-code elimination, chunk splitting, and minification
8
- * are NOT implemented in this stage; the manifest reports that honestly.
9
- * (Any compaction of compile-emitted modules happens upstream in the compiler,
10
- * not here — pack never rewrites bytes beyond specifier lowering.)
6
+ * Browser graph roots are the bootstrap modules (`entry-client.js` /
7
+ * `entry-event.js`) emitted by the compiler / static delivery path; page and
8
+ * island modules enter the graph through the bootstrap's literal dynamic
9
+ * import tables, never as pack entries. Bootstrap entries declare
10
+ * `execution: 'full'` + `exports: { kind: 'all' }` (script-tag load,
11
+ * whole-module execution).
12
+ *
13
+ * SSR/Node is a separate graph: every deployment unit's `clientEntry` is a
14
+ * Node entry (logical = `chunkId`), packed under `_vmz/pack-node/` and indexed
15
+ * by `_vmz/pack-node-manifest.json`. Node consumers never fall back to the
16
+ * unpacked root `${chunkId}.client.js`; browser bootstrap modules are not Node
17
+ * entries because they bind `dom.browser` / `document`.
18
+ *
19
+ * No vendor materialization, no specifier lowering, and no second hashing
20
+ * pass happen here: `packWrite` emits the final browser JS set under
21
+ * `_vmz/pack/` with content-hashed names, and consumers resolve logical
22
+ * entries through `packEntryHref()` against this manifest.
11
23
  */
12
24
  import crypto from 'node:crypto';
13
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
25
+ import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
14
26
  import path from 'node:path';
15
- import { loadDeploymentIr, planBundleInputs } from './bundler-adapter.js';
16
- import { packClientBareImports } from './pack-client-packages.js';
27
+ import { loadDeploymentIr } from './bundler-adapter.js';
28
+ import { buildPackRequest, runPackWrite } from './pack-native.js';
17
29
  import { deliveryRuntimeFilePairs } from './delivery-runtime-files.js';
18
30
  import { rewriteDeliveryImports } from './delivery-runtime-imports.js';
19
31
  import { writePrettyJsonFile } from './pretty-json.js';
20
32
  export const PACK_MANIFEST_SCHEMA = 'vmz.pack.manifest.v0';
33
+ /** Directory (relative to outDir) where `packWrite` emits the final browser JS set. */
34
+ export const PACK_OUTPUT_DIR_REL = '_vmz/pack';
35
+ /** Site root base — VMZ static delivery has no configurable base (see delivery-profile). */
36
+ export const PACK_PUBLIC_BASE = '/';
37
+ /**
38
+ * Projection provenance for the pack graph: each deployment unit's emitted
39
+ * client entry maps back to its source `.vmz`. Bare-specifier package discovery
40
+ * then walks up from the source location — dependency components (e.g. a
41
+ * `@vmz/ui` component importing its own peer) keep their source package's
42
+ * node_modules visibility, matching Node realpath semantics. Relative
43
+ * specifiers keep resolving against the emitted tree.
44
+ */
45
+ export function deploymentUnitOrigins(absOut, ir) {
46
+ const origins = {};
47
+ for (const unit of ir.units || []) {
48
+ const source = String(unit.source || '');
49
+ const chunkId = String(unit.chunkId || '').replace(/\\/g, '/');
50
+ if (!source || !chunkId)
51
+ continue;
52
+ const rel = (unit.clientEntry ? String(unit.clientEntry) : `${chunkId}.client.js`).replace(/\\/g, '/');
53
+ const emitted = path.join(absOut, ...rel.split('/'));
54
+ if (!existsSync(emitted) || !existsSync(source))
55
+ continue;
56
+ origins[realpathSync(emitted)] = realpathSync(source);
57
+ }
58
+ return origins;
59
+ }
60
+ /** Bootstrap modules that are browser pack entries when present in outDir. */
61
+ const BOOTSTRAP_ENTRIES = ['entry-client.js', 'entry-event.js'];
21
62
  /**
22
63
  * Ensure dom split companions sit next to vmz-dom.js (barrel imports ./dom-core.js).
23
64
  * Always refresh from `@vmz/core` when present so SSR/runtime fixes are not sticky in outDir.
65
+ * Document surfaces (Node-side document layout mount) consume this; browser delivery
66
+ * runtime files reach the pack graph through `@vmz/core` bare imports in the Rust graph.
24
67
  * @param {string} outDir
25
68
  * @param {string | null | undefined} coreDist `@vmz/core` dist root
26
69
  * @returns {string[]} copied relative names
@@ -41,37 +84,54 @@ export function ensureRuntimeCompanions(outDir, coreDist) {
41
84
  }
42
85
  return copied;
43
86
  }
87
+ /**
88
+ * Pack the browser delivery graph via the Rust `vmz-pack` binding and write
89
+ * `_vmz/pack-manifest.json` (v0). Entries are the bootstrap modules that
90
+ * already exist in `outDir` — an empty bootstrap set is an explicit request
91
+ * error, never a silently skipped stage.
92
+ */
44
93
  export function packFromDeploymentIr(outDir, opts = {}) {
45
- ensureRuntimeCompanions(outDir, opts.coreDist);
46
- // Browser ESM cannot resolve bare npm specs — materialize reachable packages first
47
- // so digests below reflect the rewritten graph.
48
- const clientPackages = packClientBareImports(outDir, { projectRoot: opts.projectRoot || null });
49
- const ir = loadDeploymentIr(outDir);
50
- const inputs = planBundleInputs(outDir, ir);
51
- const units = [];
52
- for (const entry of inputs) {
53
- const abs = entry.entry;
54
- let digest = null;
55
- let bytes = 0;
56
- let present = false;
57
- if (existsSync(abs)) {
58
- present = true;
59
- const buf = readFileSync(abs);
60
- bytes = buf.length;
61
- digest = crypto.createHash('sha256').update(buf).digest('hex');
62
- }
63
- units.push({
64
- chunkId: entry.chunkId,
65
- kind: entry.kind,
66
- entry: path.relative(outDir, abs).replace(/\\/g, '/'),
67
- programIr: path.relative(outDir, entry.programIr).replace(/\\/g, '/'),
68
- source: entry.source,
69
- present,
70
- bytes,
71
- digest,
72
- rebuilt: Boolean(entry.rebuilt),
73
- });
94
+ const absOut = path.resolve(outDir);
95
+ const root = opts.projectRoot ? path.resolve(opts.projectRoot) : path.dirname(absOut);
96
+ const entries = {};
97
+ for (const logical of BOOTSTRAP_ENTRIES) {
98
+ const abs = path.join(absOut, logical);
99
+ if (existsSync(abs))
100
+ entries[logical] = abs;
101
+ }
102
+ const uses = Object.keys(entries).map((logical) => ({
103
+ logical,
104
+ execution: 'full',
105
+ exports: { kind: 'all' },
106
+ }));
107
+ // Browser graph entries are the bootstrap modules; page/island units join
108
+ // through the bootstrap dynamic-import tables and need projection provenance.
109
+ const origins = deploymentUnitOrigins(absOut, loadDeploymentIr(absOut));
110
+ const requestJson = buildPackRequest({ entries, target: 'browser', root, uses, publicBase: PACK_PUBLIC_BASE, origins });
111
+ const packOutDir = path.join(absOut, ...PACK_OUTPUT_DIR_REL.split('/'));
112
+ // 重建不得残留上一轮 pack 的过期 chunk:manifest/磁盘闭包是交付合同,
113
+ // 写盘前整体清空旧输出目录(`packWrite` 只增不删)。
114
+ rmSync(packOutDir, { recursive: true, force: true });
115
+ const { plan } = runPackWrite(requestJson, packOutDir, opts.binding ? { binding: opts.binding } : {});
116
+ const ir = loadDeploymentIr(absOut);
117
+ let nativeManifest;
118
+ try {
119
+ nativeManifest = JSON.parse(plan.manifestJson);
120
+ }
121
+ catch (error) {
122
+ throw new Error(`pack manifestJson from binding is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
74
123
  }
124
+ const chunks = plan.artifacts.map((a) => {
125
+ const buf = Buffer.from(a.code, 'utf8');
126
+ return {
127
+ chunkId: a.chunkId,
128
+ fileName: a.fileName,
129
+ modules: a.modules,
130
+ bytes: buf.length,
131
+ digest: sha256Hex(buf),
132
+ sourceMap: a.sourceMapFileName,
133
+ };
134
+ });
75
135
  const body = {
76
136
  schema: PACK_MANIFEST_SCHEMA,
77
137
  profileId: opts.profileId || null,
@@ -79,33 +139,177 @@ export function packFromDeploymentIr(outDir, opts = {}) {
79
139
  release: Boolean(opts.release),
80
140
  preferredClientFace: opts.preferredClientFace || '@vmz/core/dom/client',
81
141
  deploymentSchema: ir.schema,
82
- unitCount: units.length,
83
- units,
84
- // Pack runs no compressor in either mode: it reports the bytes it was
85
- // given (dev output written verbatim). Release artifacts may already be
86
- // compacted by the compiler's release print path upstream — pack does not
87
- // claim, add, or verify that compaction.
88
- minify: opts.release ? 'none' : 'dev-identity',
89
- // Names how VPG deployment units are selected for this manifest. It does
90
- // NOT mean JavaScript dead-code elimination ran — that is not implemented.
91
- treeShakeBasis: 'vpg-deployment-ir',
92
- bundler: 'vmz-pack',
93
- clientPackageLowering: {
94
- status: 'thin',
95
- rewrittenFiles: clientPackages.rewrittenFiles,
96
- bareSpecs: clientPackages.bareSpecs,
97
- vendoredModules: clientPackages.vendoredModules,
98
- unresolvedBareSpecs: clientPackages.unresolvedBareSpecs || [],
99
- skippedVmzExports: clientPackages.skippedVmzExports || [],
100
- remainingBareSpecs: clientPackages.remainingBareSpecs || [],
101
- },
142
+ target: 'browser',
143
+ publicBase: PACK_PUBLIC_BASE,
144
+ entries: Object.keys(entries)
145
+ .sort()
146
+ .map((logical) => ({ logical, file: path.relative(absOut, entries[logical]).replace(/\\/g, '/') })),
147
+ entryFiles: plan.entryFiles,
148
+ dynamicFiles: plan.dynamicFiles,
149
+ // CSS artifacts are final bytes under `_vmz/pack/` (`packWrite` wrote
150
+ // them); the wire carries identity/provenance only, never content.
151
+ styles: plan.styles,
152
+ assets: plan.assets,
153
+ chunks,
154
+ chunkCount: chunks.length,
155
+ styleCount: plan.styles.length,
156
+ entryCount: plan.entryFiles.length,
157
+ // `vmz-pack` runs no compressor: chunk bytes are the linked output verbatim.
158
+ minify: 'none',
159
+ // `link` active fixed-point pruning really ran — bootstrap entries
160
+ // declare full execution, internal modules are pruned to reachable exports.
161
+ treeShake: 'vmz-pack-link-pruning',
162
+ // Chunk source maps are line-level v3: every assembled line maps back
163
+ // to its module line (`<packOut>/<fileName>.map`, `sourceMapFileName`
164
+ // on the wire); token-level mappings and `names` are not implemented.
165
+ sourceMaps: 'chunk-v3-line-mappings',
166
+ nativeManifest,
102
167
  };
103
168
  body.packDigest = sha256Hex(stableStringify({ ...body }));
104
- const vmzDir = path.join(outDir, '_vmz');
169
+ const vmzDir = path.join(absOut, '_vmz');
105
170
  mkdirSync(vmzDir, { recursive: true });
106
171
  const file = path.join(vmzDir, 'pack-manifest.json');
107
172
  writePrettyJsonFile(file, body);
108
- return { manifest: body, path: file, clientPackages };
173
+ return { manifest: body, path: file };
174
+ }
175
+ export const PACK_NODE_MANIFEST_SCHEMA = 'vmz.pack.node-manifest.v0';
176
+ /** Directory (relative to outDir) where `packWrite` emits the Node JS set. */
177
+ export const PACK_NODE_OUTPUT_DIR_REL = '_vmz/pack-node';
178
+ /** Node pack manifest (relative to outDir); `entryFiles` maps logical → chunk file. */
179
+ export const PACK_NODE_MANIFEST_REL = '_vmz/pack-node-manifest.json';
180
+ /**
181
+ * Pack the SSR/Node graph via the Rust `vmz-pack` binding and write
182
+ * `_vmz/pack-node-manifest.json` (v0). Entries are the deployment units'
183
+ * compiled client modules (`clientEntry`, empty → mirrors `vmz-artifacts`
184
+ * `component_entries`: `${chunkId}.client.js`). Node consumers only read
185
+ * `default`, but the import itself is evaluated, hence `side-effects` +
186
+ * named default rather than `all` / `full` / `unconsumed`.
187
+ */
188
+ export function packSsrFromDeploymentIr(outDir, opts = {}) {
189
+ const absOut = path.resolve(outDir);
190
+ const root = opts.projectRoot ? path.resolve(opts.projectRoot) : path.dirname(absOut);
191
+ const ir = loadDeploymentIr(absOut);
192
+ const entries = {};
193
+ const uses = [];
194
+ // Deployment IR may list the same component unit more than once (one entry
195
+ // per consuming page). The node graph identity is `chunkId`, so duplicates
196
+ // collapse into one entry; a same-identity unit pointing at a different
197
+ // client file is a contract violation, not something to silently pick.
198
+ const seen = new Map();
199
+ for (const unit of ir.units || []) {
200
+ const chunkId = String(unit.chunkId || '').replace(/\\/g, '/');
201
+ if (!chunkId)
202
+ throw new Error(`packSsrFromDeploymentIr: deployment unit has an empty chunkId in ${absOut}`);
203
+ // `vmz-artifacts` derives the component entry as `${chunkId}.client.js`
204
+ // when `clientEntry` is empty. Decide it here so the pack request stays
205
+ // explicit, then require the file — a missing entry is never skipped.
206
+ const rel = (unit.clientEntry ? String(unit.clientEntry) : `${chunkId}.client.js`).replace(/\\/g, '/');
207
+ if (seen.has(chunkId)) {
208
+ if (seen.get(chunkId) !== rel) {
209
+ throw new Error(`packSsrFromDeploymentIr: deployment unit "${chunkId}" maps to conflicting client entries: ` +
210
+ `${seen.get(chunkId)} and ${rel} (in ${absOut})`);
211
+ }
212
+ continue;
213
+ }
214
+ const abs = path.join(absOut, ...rel.split('/'));
215
+ if (!existsSync(abs)) {
216
+ throw new Error(`packSsrFromDeploymentIr: unit "${chunkId}" client entry missing: ${rel} (in ${absOut})`);
217
+ }
218
+ seen.set(chunkId, rel);
219
+ entries[chunkId] = abs;
220
+ uses.push({ logical: chunkId, execution: 'side-effects', exports: { kind: 'named', names: ['default'] } });
221
+ }
222
+ if (!Object.keys(entries).length) {
223
+ throw new Error(`packSsrFromDeploymentIr: deployment IR has no units in ${absOut}`);
224
+ }
225
+ const origins = deploymentUnitOrigins(absOut, ir);
226
+ const requestJson = buildPackRequest({ entries, target: 'node', root, uses, publicBase: PACK_PUBLIC_BASE, origins });
227
+ const packOutDir = path.join(absOut, ...PACK_NODE_OUTPUT_DIR_REL.split('/'));
228
+ // 与浏览器 pack 同口径:写盘前清空旧 Node chunk 目录,闭包合同不接受残留。
229
+ rmSync(packOutDir, { recursive: true, force: true });
230
+ const { plan } = runPackWrite(requestJson, packOutDir, opts.binding ? { binding: opts.binding } : {});
231
+ let nativeManifest;
232
+ try {
233
+ nativeManifest = JSON.parse(plan.manifestJson);
234
+ }
235
+ catch (error) {
236
+ throw new Error(`node pack manifestJson from binding is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
237
+ }
238
+ const chunks = plan.artifacts.map((a) => {
239
+ const buf = Buffer.from(a.code, 'utf8');
240
+ return {
241
+ chunkId: a.chunkId,
242
+ fileName: a.fileName,
243
+ modules: a.modules,
244
+ bytes: buf.length,
245
+ digest: sha256Hex(buf),
246
+ sourceMap: a.sourceMapFileName,
247
+ };
248
+ });
249
+ // dist roots carry no package.json; Node needs one beside the chunk set to
250
+ // treat content-hashed `.js` files as ESM.
251
+ mkdirSync(packOutDir, { recursive: true });
252
+ writeFileSync(path.join(packOutDir, 'package.json'), `${JSON.stringify({ type: 'module' })}\n`, 'utf8');
253
+ const body = {
254
+ schema: PACK_NODE_MANIFEST_SCHEMA,
255
+ target: 'node',
256
+ deploymentSchema: ir.schema,
257
+ entries: Object.keys(entries)
258
+ .sort()
259
+ .map((logical) => ({ logical, file: path.relative(absOut, entries[logical]).replace(/\\/g, '/') })),
260
+ entryFiles: plan.entryFiles,
261
+ dynamicFiles: plan.dynamicFiles,
262
+ chunks,
263
+ chunkCount: chunks.length,
264
+ entryCount: plan.entryFiles.length,
265
+ nativeManifest,
266
+ };
267
+ body.packDigest = sha256Hex(stableStringify({ ...body }));
268
+ const file = path.join(absOut, ...PACK_NODE_MANIFEST_REL.split('/'));
269
+ writePrettyJsonFile(file, body);
270
+ return { manifest: body, path: file };
271
+ }
272
+ /**
273
+ * Resolve a Node logical entry (`chunkId`) to its packed absolute file from
274
+ * `_vmz/pack-node-manifest.json`. Missing manifest, missing logical, or a
275
+ * missing chunk file is an explicit error — never a fallback to the unpacked
276
+ * root `${chunkId}.client.js`.
277
+ */
278
+ export function packNodeEntryFile(outDir, chunkId) {
279
+ const absOut = path.resolve(outDir);
280
+ const manifestPath = path.join(absOut, ...PACK_NODE_MANIFEST_REL.split('/'));
281
+ if (!existsSync(manifestPath)) {
282
+ throw new Error(`packNodeEntryFile: missing ${manifestPath} — run vmz build (node pack) first`);
283
+ }
284
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
285
+ const hit = (manifest.entryFiles || []).find((e) => e && e.logical === chunkId);
286
+ if (!hit || typeof hit.file !== 'string' || !hit.file) {
287
+ throw new Error(`packNodeEntryFile: node pack manifest has no entry file for "${chunkId}"`);
288
+ }
289
+ const abs = path.join(absOut, ...PACK_NODE_OUTPUT_DIR_REL.split('/'), ...hit.file.split('/'));
290
+ if (!existsSync(abs)) {
291
+ throw new Error(`packNodeEntryFile: node pack chunk missing on disk: ${path.relative(absOut, abs)}`);
292
+ }
293
+ return abs;
294
+ }
295
+ /**
296
+ * Resolve the final browser URL of a packed logical entry from
297
+ * `_vmz/pack-manifest.json` (`entryFiles` is the only logical→physical map).
298
+ * Missing manifest or missing logical entry is an explicit error — no
299
+ * fallback to unpacked root bytes.
300
+ */
301
+ export function packEntryHref(outDir, logical) {
302
+ const manifestPath = path.join(path.resolve(outDir), '_vmz', 'pack-manifest.json');
303
+ if (!existsSync(manifestPath)) {
304
+ throw new Error(`packEntryHref: missing ${manifestPath} — run vmz build (pack) first`);
305
+ }
306
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
307
+ const hit = (manifest.entryFiles || []).find((e) => e && e.logical === logical);
308
+ if (!hit || typeof hit.file !== 'string' || !hit.file) {
309
+ throw new Error(`packEntryHref: pack manifest has no entry file for "${logical}"`);
310
+ }
311
+ const base = String(manifest.publicBase || PACK_PUBLIC_BASE).replace(/\/$/, '');
312
+ return `${base}/${PACK_OUTPUT_DIR_REL}/${hit.file}`;
109
313
  }
110
314
  function stableStringify(value) {
111
315
  return JSON.stringify(sortKeys(value));
@@ -115,12 +319,13 @@ function sortKeys(value) {
115
319
  return value.map(sortKeys);
116
320
  if (value && typeof value === 'object') {
117
321
  const out = {};
118
- for (const k of Object.keys(value).sort())
322
+ for (const k of Object.keys(value).sort()) {
119
323
  out[k] = sortKeys(value[k]);
324
+ }
120
325
  return out;
121
326
  }
122
327
  return value;
123
328
  }
124
- function sha256Hex(text) {
125
- return crypto.createHash('sha256').update(text, 'utf8').digest('hex');
329
+ function sha256Hex(data) {
330
+ return crypto.createHash('sha256').update(data).digest('hex');
126
331
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Plugin protocol v1 helpers + typed config loading.
3
3
  */
4
- import { contentHash, defineConfig, definePlugin } from '@vmz/plugin';
5
- export { contentHash, defineConfig, definePlugin };
4
+ import { contentHash, definePlugin } from '@vmz/plugin';
5
+ export { contentHash, definePlugin };
6
6
  export declare const PLUGIN_PROTOCOL = "0.1.0";
7
7
  export declare const CONFIG_NAMES: string[];
8
8
  export declare const CONFIG_LOAD_FAILED = "vmz::config::load_failed";
@@ -13,9 +13,9 @@ export declare const PLUGIN_ENTRY_INVALID = "vmz::plugin::invalid_entry";
13
13
  * @returns {Promise<{
14
14
  * plugins: import('@vmz/plugin').VmzPlugin[],
15
15
  * engines: import('@vmz/plugin').VmzEngines,
16
- * delivery: import('@vmz/plugin').DeliveryAuthoring | null,
16
+ * delivery: import('./user-config.js').DeliveryAuthoring | null,
17
17
  * application: { id?: string } | null,
18
- * format: import('@vmz/plugin').VmzFormatConfig | null,
18
+ * format: import('./user-config.js').VmzFormatConfig | null,
19
19
  * path: string | null,
20
20
  * pluginPath: string | null,
21
21
  * }>}
@@ -4,10 +4,10 @@
4
4
  import { existsSync } from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import { pathToFileURL } from 'node:url';
7
- import { PLUGIN_PROTOCOL as PLUGIN_PROTOCOL_PKG, contentHash, defineConfig, definePlugin } from '@vmz/plugin';
7
+ import { PLUGIN_PROTOCOL as PLUGIN_PROTOCOL_PKG, contentHash, definePlugin } from '@vmz/plugin';
8
8
  import { log } from './log.js';
9
9
  import { resolveWorkspacePackages } from './packages.js';
10
- export { contentHash, defineConfig, definePlugin };
10
+ export { contentHash, definePlugin };
11
11
  export const PLUGIN_PROTOCOL = PLUGIN_PROTOCOL_PKG;
12
12
  export const CONFIG_NAMES = ['vmz.config.ts', 'vmz.config.mts', 'vmz.config.mjs', 'vmz.config.js'];
13
13
  const ROOT_PLUGIN_NAMES = ['vmz.plugin.ts', 'vmz.plugin.mts', 'vmz.plugin.mjs', 'vmz.plugin.js'];
@@ -61,9 +61,9 @@ function configDiagnosticError(code, sourcePath, err) {
61
61
  * @returns {Promise<{
62
62
  * plugins: import('@vmz/plugin').VmzPlugin[],
63
63
  * engines: import('@vmz/plugin').VmzEngines,
64
- * delivery: import('@vmz/plugin').DeliveryAuthoring | null,
64
+ * delivery: import('./user-config.js').DeliveryAuthoring | null,
65
65
  * application: { id?: string } | null,
66
- * format: import('@vmz/plugin').VmzFormatConfig | null,
66
+ * format: import('./user-config.js').VmzFormatConfig | null,
67
67
  * path: string | null,
68
68
  * pluginPath: string | null,
69
69
  * }>}
@@ -73,11 +73,11 @@ export async function loadVmzConfig(project) {
73
73
  const plugins = [];
74
74
  /** @type {import('@vmz/plugin').VmzEngines} */
75
75
  let engines = {};
76
- /** @type {import('@vmz/plugin').DeliveryAuthoring | null} */
76
+ /** @type {import('./user-config.js').DeliveryAuthoring | null} */
77
77
  let delivery = null;
78
78
  /** @type {{ id?: string } | null} */
79
79
  let application = null;
80
- /** @type {import('@vmz/plugin').VmzFormatConfig | null} */
80
+ /** @type {import('./user-config.js').VmzFormatConfig | null} */
81
81
  let format = null;
82
82
  /** @type {string | null} */
83
83
  let configPath = null;
@@ -95,6 +95,8 @@ export declare function checkCapabilityClosure(cap: any, policy: any): {
95
95
  export declare function applySecurityHeadersToCdnPolicy(cdnPolicy: any, security: any): any;
96
96
  /**
97
97
  * Measure dist sizes for budget gates.
98
+ * Browser delivery JS final set = packed chunks under `_vmz/pack/`
99
+ * (root `*.client.js` / bootstrap files are Node SSR / pack inputs, not delivery bytes).
98
100
  * @param {string} distDir
99
101
  */
100
102
  export declare function measureDistBudgets(distDir: any): {
@@ -398,13 +398,16 @@ export function applySecurityHeadersToCdnPolicy(cdnPolicy, security) {
398
398
  }
399
399
  /**
400
400
  * Measure dist sizes for budget gates.
401
+ * Browser delivery JS final set = packed chunks under `_vmz/pack/`
402
+ * (root `*.client.js` / bootstrap files are Node SSR / pack inputs, not delivery bytes).
401
403
  * @param {string} distDir
402
404
  */
403
405
  export function measureDistBudgets(distDir) {
404
406
  let htmlBytes = 0;
405
407
  let clientJsBytes = 0;
406
408
  let artifactBytes = 0;
407
- const stack = [distDir];
409
+ const abs = path.resolve(distDir);
410
+ const stack = [abs];
408
411
  while (stack.length) {
409
412
  const dir = stack.pop();
410
413
  if (!fs.existsSync(dir))
@@ -419,7 +422,8 @@ export function measureDistBudgets(distDir) {
419
422
  artifactBytes += st.size;
420
423
  if (name.endsWith('.html'))
421
424
  htmlBytes += st.size;
422
- if (name.endsWith('.client.js') || name === 'entry-client.js' || name === 'entry-event.js') {
425
+ const rel = path.relative(abs, full).replace(/\\/g, '/');
426
+ if (rel.startsWith('_vmz/pack/') && name.endsWith('.js')) {
423
427
  clientJsBytes += st.size;
424
428
  }
425
429
  }
@@ -9,9 +9,11 @@ import { normalizeDeliveryAuthoring, resolveProfileArtifactDir, selectBuildProfi
9
9
  import { buildIntegratedDocuments, projectHasDocuments } from '../document/document-integrate.js';
10
10
  import { emitLocaleRuntimeModules, localeHasErrors } from '../locale/locale-check.js';
11
11
  import { emitLocaleRouteRealization } from '../locale/locale-route-emit.js';
12
- import { packFromDeploymentIr } from './pack.js';
12
+ import { ensureRuntimeCompanions, packFromDeploymentIr, packSsrFromDeploymentIr } from './pack.js';
13
13
  import { loadVmzConfig } from './plugin-host.js';
14
14
  import { emitRouteCatalog } from './route-catalog-emit.js';
15
+ import { resolveCoreRuntimeDist } from './runtime-dist.js';
16
+ import { emitStaticBootstrapEntries } from './static-emit.js';
15
17
  function dedupeDiagnostics(list) {
16
18
  const seen = new Set();
17
19
  const out = [];
@@ -81,7 +83,7 @@ export async function buildProjectToOutDirRoot(project, outDirRoot, opts = {}) {
81
83
  const assembly = String(selected.selection.assembly || '');
82
84
  const profileId = String(selected.selection.profileId || '');
83
85
  // Dynamic import avoids init cycle with `./index.js` re-exports.
84
- const { createWorkspace, resolveCoreRuntimeDist } = await import('./public-api.js');
86
+ const { createWorkspace } = await import('./public-api.js');
85
87
  const ws = createWorkspace({ root: project, outDir: artifactDir });
86
88
  try {
87
89
  const code = await runWithPlugins(ws, project, artifactDir, () => {
@@ -141,8 +143,26 @@ export async function buildProjectToOutDirRoot(project, outDirRoot, opts = {}) {
141
143
  error: routeCatalog.error || 'route catalog emit failed',
142
144
  };
143
145
  }
146
+ try {
147
+ // Node graph must be packed before any SSR consumer (document build,
148
+ // static emit, render host) imports a compiled chunk. Companions are
149
+ // copied first so the Node graph sees the delivery runtime beside the
150
+ // client modules.
151
+ ensureRuntimeCompanions(artifactDir, resolveCoreRuntimeDist());
152
+ packSsrFromDeploymentIr(artifactDir, { projectRoot: project });
153
+ }
154
+ catch (err) {
155
+ return {
156
+ ok: false,
157
+ outDirRoot: root,
158
+ artifactDir,
159
+ deliveryName,
160
+ diagnostics,
161
+ error: `node pack failed: ${err instanceof Error ? err.message : String(err)}`,
162
+ };
163
+ }
144
164
  if (projectHasDocuments(project)) {
145
- const docs = await buildIntegratedDocuments({ projectRoot: project, outDir: artifactDir });
165
+ const docs = await buildIntegratedDocuments({ projectRoot: project, outDir: artifactDir, engines: cfg.engines });
146
166
  if (!docs.ok) {
147
167
  return {
148
168
  ok: false,
@@ -156,11 +176,13 @@ export async function buildProjectToOutDirRoot(project, outDirRoot, opts = {}) {
156
176
  }
157
177
  let pack;
158
178
  try {
179
+ // Browser pack entries are the static bootstrap modules; static emit
180
+ // runs after pack (HTML resolves `_vmz/pack/`), so write them first.
181
+ emitStaticBootstrapEntries(artifactDir);
159
182
  pack = packFromDeploymentIr(artifactDir, {
160
183
  release: Boolean(opts.release),
161
184
  profileId,
162
185
  assembly,
163
- coreDist: resolveCoreRuntimeDist(),
164
186
  projectRoot: project,
165
187
  });
166
188
  }
@@ -96,7 +96,7 @@ export { registerApplicationCommands, runCheck as runApplicationCheck } from '..
96
96
  export { registerArtifactCommands, runArtifact } from '../cli/release-cmd.js';
97
97
  export { ARTIFACT_DIFF_SCHEMA, DELIVERY_ARTIFACT_MANIFEST_SCHEMA, RELEASE_ENVELOPE_SCHEMA, ROUTE_REALIZATION_TABLE_SCHEMA, atomicWritePointer, canonicalJson, diffArtifacts, loadReleaseEnvelope, packRelease, publishRelease, readPointer, rollbackRelease, sha256File, sha256Hex, } from './release-pack.js';
98
98
  export { STATIC_DELIVERY_MANIFEST_SCHEMA, emitWebStatic } from './static-emit.js';
99
- export { CONTENT_ADDRESSED_ASSETS_SCHEMA, emitContentAddressedAssets, resolveAssetByDigest, assertSharedAssetPath, contentAddressedAssetsDigest, rewriteCssImports, rewriteJsEntryRelativeImports, } from './content-addressed-assets.js';
99
+ export { CONTENT_ADDRESSED_ASSETS_SCHEMA, emitContentAddressedAssets, resolveAssetByDigest, assertSharedAssetPath, contentAddressedAssetsDigest, rewriteCssImports, } from './content-addressed-assets.js';
100
100
  export { SITE_FAVICON_SCHEMA, emitSiteFavicon, readSiteFaviconHeadHtml, packPngsIntoIco } from './site-favicon.js';
101
101
  export { PUBLIC_STATIC_ASSETS_SCHEMA, emitPublicStaticAssets } from './public-static-assets.js';
102
102
  export { CDN_POLICY_MANIFEST_SCHEMA, CDN_ADAPTER_PROJECTION_SCHEMA, CACHE_HTML, CACHE_ASSET_IMMUTABLE, CACHE_META, buildCdnPolicyManifest, emitCdnPolicy, projectCdnAdapter, createLocalStaticHandler, listenLocalStaticHost, matchGlob, } from './cdn-policy.js';
@@ -105,7 +105,9 @@ export { EMBEDDED_RESOURCE_INDEX_SCHEMA, emitEmbeddedPackaging } from './embedde
105
105
  export { SERVER_LANG_IDS, SERVER_LANG_ALIASES, SERVER_LANGUAGE_BACKENDS, resolveServerLanguage, assertLangRuntimePair, } from './server-language-backend.js';
106
106
  export { WECHAT_PACKAGING_SCHEMA, WECHAT_PACKAGING_REL, wechatPackagingFromDelivery, materializeWechatPackaging, prepareWechatPackaging, } from './wechat-packaging.js';
107
107
  export { DELIVERY_PROFILE_AUTHORING_SCHEMA, BUILD_PROFILE_SELECTION_SCHEMA, ASSEMBLIES, SERVER_RUNTIMES, BUILTIN_PROFILES, pickSiteAuthoring, pickDeliveryPackaging, normalizeProfileArtifactName, resolveProfileArtifactDir, normalizeDeliveryAuthoring, selectBuildProfile, semanticIdsForAssembly, } from '../delivery/delivery-profile.js';
108
- export { PACK_MANIFEST_SCHEMA, packFromDeploymentIr, ensureRuntimeCompanions } from './pack.js';
108
+ export { PACK_MANIFEST_SCHEMA, PACK_OUTPUT_DIR_REL, PACK_PUBLIC_BASE, PACK_NODE_MANIFEST_SCHEMA, PACK_NODE_OUTPUT_DIR_REL, PACK_NODE_MANIFEST_REL, packFromDeploymentIr, packSsrFromDeploymentIr, packEntryHref, packNodeEntryFile, ensureRuntimeCompanions, } from './pack.js';
109
+ export { buildPackRequest, runPackPlan, runPackWrite, PackNativeError } from './pack-native.js';
110
+ export type { PackNativeBinding, PackPlan, PackPlanResponse, PackDiagnostic, PackRequest, PackRequestEntry, PackRequestUse, PackTarget, } from './pack-native.js';
109
111
  export { SERVER_ARTIFACT_SCHEMA, HTTP_CONTRACT_SCHEMA, SERVER_RUNTIME_ADAPTER_SCHEMA, emitServerArtifact, projectServerRuntimeAdapter, } from './server-artifact.js';
110
112
  export { BUILD_PROOF_SCHEMA, ASSEMBLE_MANIFEST_SCHEMA, assembleDelivery, emitBuildProof } from './build-assemble.js';
111
113
  export { buildProjectToOutDirRoot, type BuildProjectOptions, type BuildProjectResult } from './project-build.js';
@@ -116,7 +118,9 @@ export { absoluteUrl, assertLocaleCacheKey, buildLocalePageMeta, buildLocaleRout
116
118
  export { LOCALE_ROUTE_REALIZATION_ARTIFACT_SCHEMA, emitLocaleRouteRealization } from '../locale/locale-route-emit.js';
117
119
  export { assertHostMessageInvariant, assertServerErrorEnvelope, assertServerFormatContext, buildLocaleDeliveryResolution, checkLocaleDelivery, fallbackDigest, messageCatalogHash, proveMiniPackageMessages, validateNativeLocalePack, } from '../locale/locale-delivery.js';
118
120
  export { checkLocaleConformance, diffLocaleCatalogs, explainLocaleMessage, extractHardcodedText, pseudoLocalizeCatalog, } from '../locale/locale-tooling.js';
119
- export { applyPlugins, contentHash, defineConfig, definePlugin, loadVmzConfig } from './plugin-host.js';
121
+ export { applyPlugins, contentHash, definePlugin, loadVmzConfig } from './plugin-host.js';
122
+ export { defineConfig } from './user-config.js';
123
+ export type { DeliveryAssembly, DeliveryAuthoring, DeliveryPackagingAuthoring, DeliveryProfileAuthoring, DeliveryServerRuntime, SiteDeliveryAuthoring, VmzFormatConfig, VmzUserConfig, WechatPackagingAuthoring, } from './user-config.js';
120
124
  export { createRolldownPluginVmzAdapter, createVitePluginVmzAdapter, loadDeploymentIr, planAffectedBundleInputs, planBundleInputs, } from './bundler-adapter.js';
121
125
  declare const _default: {
122
126
  HOST_PROTOCOL: string;
@@ -284,7 +284,7 @@ export { ARTIFACT_DIFF_SCHEMA, DELIVERY_ARTIFACT_MANIFEST_SCHEMA, RELEASE_ENVELO
284
284
  // APPLICATION_ARTIFACT_SCHEMA lives in @vmz/protocol (already re-exported above);
285
285
  // release-pack keeps a local constant for envelope writes — do not dual-export the name.
286
286
  export { STATIC_DELIVERY_MANIFEST_SCHEMA, emitWebStatic } from './static-emit.js';
287
- export { CONTENT_ADDRESSED_ASSETS_SCHEMA, emitContentAddressedAssets, resolveAssetByDigest, assertSharedAssetPath, contentAddressedAssetsDigest, rewriteCssImports, rewriteJsEntryRelativeImports, } from './content-addressed-assets.js';
287
+ export { CONTENT_ADDRESSED_ASSETS_SCHEMA, emitContentAddressedAssets, resolveAssetByDigest, assertSharedAssetPath, contentAddressedAssetsDigest, rewriteCssImports, } from './content-addressed-assets.js';
288
288
  export { SITE_FAVICON_SCHEMA, emitSiteFavicon, readSiteFaviconHeadHtml, packPngsIntoIco } from './site-favicon.js';
289
289
  export { PUBLIC_STATIC_ASSETS_SCHEMA, emitPublicStaticAssets } from './public-static-assets.js';
290
290
  export { CDN_POLICY_MANIFEST_SCHEMA, CDN_ADAPTER_PROJECTION_SCHEMA, CACHE_HTML, CACHE_ASSET_IMMUTABLE, CACHE_META, buildCdnPolicyManifest, emitCdnPolicy, projectCdnAdapter, createLocalStaticHandler, listenLocalStaticHost, matchGlob, } from './cdn-policy.js';
@@ -293,7 +293,8 @@ export { EMBEDDED_RESOURCE_INDEX_SCHEMA, emitEmbeddedPackaging } from './embedde
293
293
  export { SERVER_LANG_IDS, SERVER_LANG_ALIASES, SERVER_LANGUAGE_BACKENDS, resolveServerLanguage, assertLangRuntimePair, } from './server-language-backend.js';
294
294
  export { WECHAT_PACKAGING_SCHEMA, WECHAT_PACKAGING_REL, wechatPackagingFromDelivery, materializeWechatPackaging, prepareWechatPackaging, } from './wechat-packaging.js';
295
295
  export { DELIVERY_PROFILE_AUTHORING_SCHEMA, BUILD_PROFILE_SELECTION_SCHEMA, ASSEMBLIES, SERVER_RUNTIMES, BUILTIN_PROFILES, pickSiteAuthoring, pickDeliveryPackaging, normalizeProfileArtifactName, resolveProfileArtifactDir, normalizeDeliveryAuthoring, selectBuildProfile, semanticIdsForAssembly, } from '../delivery/delivery-profile.js';
296
- export { PACK_MANIFEST_SCHEMA, packFromDeploymentIr, ensureRuntimeCompanions } from './pack.js';
296
+ export { PACK_MANIFEST_SCHEMA, PACK_OUTPUT_DIR_REL, PACK_PUBLIC_BASE, PACK_NODE_MANIFEST_SCHEMA, PACK_NODE_OUTPUT_DIR_REL, PACK_NODE_MANIFEST_REL, packFromDeploymentIr, packSsrFromDeploymentIr, packEntryHref, packNodeEntryFile, ensureRuntimeCompanions, } from './pack.js';
297
+ export { buildPackRequest, runPackPlan, runPackWrite, PackNativeError } from './pack-native.js';
297
298
  export { SERVER_ARTIFACT_SCHEMA, HTTP_CONTRACT_SCHEMA, SERVER_RUNTIME_ADAPTER_SCHEMA, emitServerArtifact, projectServerRuntimeAdapter, } from './server-artifact.js';
298
299
  export { BUILD_PROOF_SCHEMA, ASSEMBLE_MANIFEST_SCHEMA, assembleDelivery, emitBuildProof } from './build-assemble.js';
299
300
  export { buildProjectToOutDirRoot } from './project-build.js';
@@ -304,7 +305,8 @@ export { absoluteUrl, assertLocaleCacheKey, buildLocalePageMeta, buildLocaleRout
304
305
  export { LOCALE_ROUTE_REALIZATION_ARTIFACT_SCHEMA, emitLocaleRouteRealization } from '../locale/locale-route-emit.js';
305
306
  export { assertHostMessageInvariant, assertServerErrorEnvelope, assertServerFormatContext, buildLocaleDeliveryResolution, checkLocaleDelivery, fallbackDigest, messageCatalogHash, proveMiniPackageMessages, validateNativeLocalePack, } from '../locale/locale-delivery.js';
306
307
  export { checkLocaleConformance, diffLocaleCatalogs, explainLocaleMessage, extractHardcodedText, pseudoLocalizeCatalog, } from '../locale/locale-tooling.js';
307
- export { applyPlugins, contentHash, defineConfig, definePlugin, loadVmzConfig } from './plugin-host.js';
308
+ export { applyPlugins, contentHash, definePlugin, loadVmzConfig } from './plugin-host.js';
309
+ export { defineConfig } from './user-config.js';
308
310
  export { createRolldownPluginVmzAdapter, createVitePluginVmzAdapter, loadDeploymentIr, planAffectedBundleInputs, planBundleInputs, } from './bundler-adapter.js';
309
311
  export default {
310
312
  HOST_PROTOCOL,