@vmz/vmz 0.1.13 → 0.1.15

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 (56) hide show
  1. package/dist/author-input.d.ts +19 -0
  2. package/dist/author-input.js +52 -0
  3. package/dist/build-assemble.js +18 -7
  4. package/dist/cdn-policy.js +5 -4
  5. package/dist/cli.js +106 -38
  6. package/dist/content-addressed-assets.d.ts +23 -2
  7. package/dist/content-addressed-assets.js +178 -32
  8. package/dist/delivery-profile.d.ts +30 -2
  9. package/dist/delivery-profile.js +139 -5
  10. package/dist/dev-session.d.ts +1 -0
  11. package/dist/dev-session.js +269 -65
  12. package/dist/dev-watch-roots.d.ts +80 -0
  13. package/dist/dev-watch-roots.js +245 -0
  14. package/dist/document-build.d.ts +4 -3
  15. package/dist/document-build.js +104 -82
  16. package/dist/document-check.d.ts +24 -5
  17. package/dist/document-check.js +64 -101
  18. package/dist/document-cmd.js +2 -9
  19. package/dist/document-enrich.d.ts +2 -1
  20. package/dist/document-enrich.js +15 -4
  21. package/dist/document-integrate.js +29 -21
  22. package/dist/document-layout-render.d.ts +20 -0
  23. package/dist/document-layout-render.js +87 -0
  24. package/dist/document-routing-config.d.ts +17 -0
  25. package/dist/document-routing-config.js +31 -0
  26. package/dist/document-schema.js +8 -1
  27. package/dist/embedded-packaging.js +2 -1
  28. package/dist/index.d.ts +18 -2
  29. package/dist/index.js +63 -4
  30. package/dist/locale-check.js +81 -183
  31. package/dist/locale-cmd.js +13 -45
  32. package/dist/locale-route-emit.d.ts +4 -1
  33. package/dist/locale-route-emit.js +7 -32
  34. package/dist/locale-router.js +26 -8
  35. package/dist/native-addon.d.ts +9 -0
  36. package/dist/native-addon.js +84 -0
  37. package/dist/pack.js +3 -2
  38. package/dist/pretty-json.d.ts +19 -0
  39. package/dist/pretty-json.js +43 -0
  40. package/dist/production-observability.js +3 -2
  41. package/dist/production-test-pack.js +4 -3
  42. package/dist/public-static-assets.d.ts +24 -0
  43. package/dist/public-static-assets.js +140 -0
  44. package/dist/release-pack.js +5 -18
  45. package/dist/route-path.d.ts +35 -0
  46. package/dist/route-path.js +77 -0
  47. package/dist/server-artifact.js +4 -3
  48. package/dist/site-delivery.js +4 -3
  49. package/dist/site-favicon.d.ts +31 -0
  50. package/dist/site-favicon.js +140 -0
  51. package/dist/static-emit.d.ts +21 -0
  52. package/dist/static-emit.js +134 -97
  53. package/dist/test-cmd.js +2 -1
  54. package/dist/wechat-packaging.d.ts +22 -0
  55. package/dist/wechat-packaging.js +59 -0
  56. package/package.json +13 -14
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Degrade author JSON5/JSON text to a plain object via Rust (not a semantic plan API).
3
+ * @param {string} source
4
+ * @returns {any}
5
+ */
6
+ export declare function parseAuthorInput(source: any): any;
7
+ /**
8
+ * @param {string} projectRoot
9
+ */
10
+ export declare function loadLocalePlan(projectRoot: any): any;
11
+ /**
12
+ * @param {string} projectRoot
13
+ */
14
+ export declare function loadDocumentRoutePlan(projectRoot: any): any;
15
+ /**
16
+ * Map Rust ReportedDiagnostic rows into host `{ code, severity, message, path }` rows.
17
+ * @param {Array<{ code?: string, severity?: string, message?: string, path?: string }>} rows
18
+ */
19
+ export declare function mapPlanDiagnostics(rows: any): any;
@@ -0,0 +1,52 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Author declaration input via Rust N-API (no JS JSON5 package).
4
+ *
5
+ * Locale/document policy: loadLocalePlan / loadDocumentRoutePlan.
6
+ * Catalogs / transitional tables: parseAuthorInput → Rust degrade → JSON.parse.
7
+ */
8
+ import { requireNativeAddon } from './native-addon.js';
9
+ /**
10
+ * Degrade author JSON5/JSON text to a plain object via Rust (not a semantic plan API).
11
+ * @param {string} source
12
+ * @returns {any}
13
+ */
14
+ export function parseAuthorInput(source) {
15
+ const native = requireNativeAddon();
16
+ if (typeof native.authorJson5ToCanonicalJson !== 'function') {
17
+ throw new Error('native missing authorJson5ToCanonicalJson — run `pnpm napi:build`');
18
+ }
19
+ return JSON.parse(native.authorJson5ToCanonicalJson(String(source)));
20
+ }
21
+ /**
22
+ * @param {string} projectRoot
23
+ */
24
+ export function loadLocalePlan(projectRoot) {
25
+ const native = requireNativeAddon();
26
+ if (typeof native.loadLocalePlan !== 'function') {
27
+ throw new Error('native missing loadLocalePlan — run `pnpm napi:build`');
28
+ }
29
+ return JSON.parse(native.loadLocalePlan(String(projectRoot)));
30
+ }
31
+ /**
32
+ * @param {string} projectRoot
33
+ */
34
+ export function loadDocumentRoutePlan(projectRoot) {
35
+ const native = requireNativeAddon();
36
+ if (typeof native.loadDocumentRoutePlan !== 'function') {
37
+ throw new Error('native missing loadDocumentRoutePlan — run `pnpm napi:build`');
38
+ }
39
+ return JSON.parse(native.loadDocumentRoutePlan(String(projectRoot)));
40
+ }
41
+ /**
42
+ * Map Rust ReportedDiagnostic rows into host `{ code, severity, message, path }` rows.
43
+ * @param {Array<{ code?: string, severity?: string, message?: string, path?: string }>} rows
44
+ */
45
+ export function mapPlanDiagnostics(rows) {
46
+ return (rows || []).map((d) => ({
47
+ code: d.code || 'vmz::unknown',
48
+ severity: d.severity === 'advice' ? 'warning' : d.severity || 'error',
49
+ message: d.message || '',
50
+ path: d.path || undefined,
51
+ }));
52
+ }
@@ -2,13 +2,16 @@
2
2
  * B5 Assemble dispatch + B6 build-proof (per-build semantic id slots).
3
3
  */
4
4
  // @ts-nocheck
5
- import { mkdirSync, writeFileSync } from 'node:fs';
5
+ import { mkdirSync } from 'node:fs';
6
6
  import path from 'node:path';
7
7
  import { semanticIdsForAssembly, sha256Hex, canonicalJson } from './delivery-profile.js';
8
8
  import { emitServerArtifact } from './server-artifact.js';
9
9
  import { emitEmbeddedPackaging } from './embedded-packaging.js';
10
10
  import { emitSiteDelivery } from './site-delivery.js';
11
+ import { emitSiteFavicon } from './site-favicon.js';
12
+ import { emitPublicStaticAssets } from './public-static-assets.js';
11
13
  import { emitWebStatic } from './static-emit.js';
14
+ import { writePrettyJsonFile } from './pretty-json.js';
12
15
  export const BUILD_PROOF_SCHEMA = 'vmz.build.proof.v0';
13
16
  export const ASSEMBLE_MANIFEST_SCHEMA = 'vmz.assemble.manifest.v0';
14
17
  /**
@@ -25,10 +28,13 @@ export async function assembleDelivery(outDir, ctx) {
25
28
  serverRuntime: selection.serverRuntime || null,
26
29
  steps: [],
27
30
  };
28
- if (assembly === 'static-cdn' || assembly === 'cdn+server') {
29
- const staticResult = await emitWebStatic(outDir, { origin: ctx.origin });
31
+ if (assembly === 'web-static' || assembly === 'cdn+server') {
32
+ const staticResult = await emitWebStatic(outDir, {
33
+ origin: ctx.origin,
34
+ projectRoot: ctx.projectRoot,
35
+ });
30
36
  result.steps.push({
31
- kind: 'static-cdn',
37
+ kind: 'web-static',
32
38
  digest: staticResult.digest,
33
39
  htmlFiles: staticResult.htmlFiles?.length ?? 0,
34
40
  skipped: staticResult.skipped?.length ?? 0,
@@ -39,6 +45,11 @@ export async function assembleDelivery(outDir, ctx) {
39
45
  skipped: staticResult.skipped,
40
46
  };
41
47
  }
48
+ else if (assembly === 'server-host' || assembly === 'local-static') {
49
+ // SSR / local packs: favicon + opaque public/ for serve-host.
50
+ emitSiteFavicon(outDir, { projectRoot: ctx.projectRoot });
51
+ emitPublicStaticAssets(outDir, { projectRoot: ctx.projectRoot });
52
+ }
42
53
  if (assembly === 'local-static') {
43
54
  result.steps.push({
44
55
  kind: 'local-static',
@@ -106,7 +117,7 @@ export async function assembleDelivery(outDir, ctx) {
106
117
  const vmzDir = path.join(outDir, '_vmz');
107
118
  mkdirSync(vmzDir, { recursive: true });
108
119
  const file = path.join(vmzDir, 'assemble-manifest.json');
109
- writeFileSync(file, `${JSON.stringify(result, null, 2)}\n`, 'utf8');
120
+ writePrettyJsonFile(file, result);
110
121
  return { manifest: result, path: file };
111
122
  }
112
123
  /**
@@ -123,7 +134,7 @@ export function emitBuildProof(outDir, ctx) {
123
134
  };
124
135
  for (const id of semanticIds) {
125
136
  if (id === 'static-delivery') {
126
- const step = (ctx.assemble?.steps || []).find((s) => s.kind === 'static-cdn');
137
+ const step = (ctx.assemble?.steps || []).find((s) => s.kind === 'web-static');
127
138
  slots[id] = step
128
139
  ? { status: 'emitted', detail: `digest=${String(step.digest).slice(0, 12)}` }
129
140
  : { status: 'pending', detail: 'assembly requires static emit' };
@@ -186,6 +197,6 @@ export function emitBuildProof(outDir, ctx) {
186
197
  const vmzDir = path.join(outDir, '_vmz');
187
198
  mkdirSync(vmzDir, { recursive: true });
188
199
  const file = path.join(vmzDir, 'build-proof.json');
189
- writeFileSync(file, `${JSON.stringify(body, null, 2)}\n`, 'utf8');
200
+ writePrettyJsonFile(file, body);
190
201
  return { proof: body, path: file };
191
202
  }
@@ -8,6 +8,7 @@ import fs from 'node:fs';
8
8
  import http from 'node:http';
9
9
  import path from 'node:path';
10
10
  import { assertLocaleCacheKey, localeAwareCacheKey } from './locale-router.js';
11
+ import { writePrettyJsonFile } from './pretty-json.js';
11
12
  export const CDN_POLICY_MANIFEST_SCHEMA = 'vmz.cdn.policy_manifest.v0';
12
13
  export const CDN_ADAPTER_PROJECTION_SCHEMA = 'vmz.cdn.adapter_projection.v0';
13
14
  /** HTML: revalidate. Hashed/static assets: long immutable. */
@@ -85,7 +86,7 @@ export function buildCdnPolicyManifest(staticManifest, opts = {}) {
85
86
  const body = {
86
87
  schema: CDN_POLICY_MANIFEST_SCHEMA,
87
88
  applicationId: staticManifest.applicationId || null,
88
- deliveryProfile: 'web-static',
89
+ deliveryProfile: 'static',
89
90
  origin,
90
91
  spaFallback: false,
91
92
  staticManifestDigest: staticManifest.manifestDigest || null,
@@ -145,14 +146,14 @@ export function emitCdnPolicy(distDir, staticManifest, opts = {}) {
145
146
  const policy = buildCdnPolicyManifest(staticManifest, { ...opts, localeArtifact });
146
147
  const vmzDir = path.join(distDir, '_vmz');
147
148
  fs.mkdirSync(vmzDir, { recursive: true });
148
- fs.writeFileSync(path.join(vmzDir, 'cdn-policy-manifest.json'), `${JSON.stringify(policy, null, 2)}\n`, 'utf8');
149
+ writePrettyJsonFile(path.join(vmzDir, 'cdn-policy-manifest.json'), policy);
149
150
  const local = projectCdnAdapter(policy, 'local-static');
150
151
  const netlify = projectCdnAdapter(policy, 'netlify');
151
152
  const adaptersDir = path.join(vmzDir, 'adapters');
152
153
  fs.mkdirSync(path.join(adaptersDir, 'local-static'), { recursive: true });
153
154
  fs.mkdirSync(path.join(adaptersDir, 'netlify'), { recursive: true });
154
- fs.writeFileSync(path.join(adaptersDir, 'local-static', 'projection.json'), `${JSON.stringify(local, null, 2)}\n`, 'utf8');
155
- fs.writeFileSync(path.join(adaptersDir, 'netlify', 'projection.json'), `${JSON.stringify(netlify, null, 2)}\n`, 'utf8');
155
+ writePrettyJsonFile(path.join(adaptersDir, 'local-static', 'projection.json'), local);
156
+ writePrettyJsonFile(path.join(adaptersDir, 'netlify', 'projection.json'), netlify);
156
157
  fs.writeFileSync(path.join(adaptersDir, 'netlify', '_headers'), String(netlify.files['_headers'] || ''), 'utf8');
157
158
  fs.writeFileSync(path.join(adaptersDir, 'netlify', '_redirects'), String(netlify.files['_redirects'] || ''), 'utf8');
158
159
  return { policy, adapters: { 'local-static': local, netlify } };
package/dist/cli.js CHANGED
@@ -3,9 +3,9 @@
3
3
  * Node CLI command implementations .
4
4
  */
5
5
  import { spawn } from 'node:child_process';
6
- import { copyFileSync, existsSync } from 'node:fs';
6
+ import { existsSync } from 'node:fs';
7
7
  import path from 'node:path';
8
- import { HOST_PROTOCOL, createWorkspace, getProtocolVersions, resolveCoreRuntimeDist } from './index.js';
8
+ import { HOST_PROTOCOL, createWorkspace, getProtocolVersions, materializeServeHostRuntime, resolveCoreRuntimeDist, resolveNativePath, } from './index.js';
9
9
  import { createDevSession } from './dev-session.js';
10
10
  import { gateGlobalProjectCommand, getInvocationContext, isGlobalAllowedCommand } from './invocation.js';
11
11
  import { log } from './log.js';
@@ -23,7 +23,7 @@ import { cmdRefactor } from './refactor-cmd.js';
23
23
  import { cmdExplain } from './explain-cmd.js';
24
24
  import { resolveNativeVmzCli } from './resolve-native-cli.js';
25
25
  import { loadVmzConfig } from './plugin-host.js';
26
- import { normalizeDeliveryAuthoring, selectBuildProfile } from './delivery-profile.js';
26
+ import { normalizeDeliveryAuthoring, resolveProfileArtifactDir, selectBuildProfile } from './delivery-profile.js';
27
27
  import { packFromDeploymentIr } from './pack.js';
28
28
  import { assembleDelivery, emitBuildProof } from './build-assemble.js';
29
29
  /**
@@ -104,10 +104,10 @@ export function printProjectHelp() {
104
104
  Usage:
105
105
  vmz new|init <dir> Scaffold a minimal app (native CLI)
106
106
  vmz check [path] Check project via Workspace
107
- vmz build [path] [options] Build project via Workspace
107
+ vmz build [path] [options] Build project via Workspace; --target mini-program-wechat packs dist/wechat
108
108
  vmz serve [path] [options] Serve dist (optional --build)
109
- vmz dev [path] [options] Long-lived rebuild session (no CLI spawn)
110
- vmz format [path] [--check] Format .vmz via N-API (oxc codegen)
109
+ vmz dev [path] [options] Rebuild session; --target mini-program-wechat packs dist/wechat
110
+ vmz format [path] [--check] Format .vmz via N-API (oxc formatter + EditorConfig)
111
111
  vmz lint [path] [--deny-warnings] Lint (= check) via N-API
112
112
  vmz test [path] [options] Native test discover / report
113
113
  vmz document|docs <cmd> Project /documents domain
@@ -121,10 +121,11 @@ Usage:
121
121
  vmz help Show this help
122
122
 
123
123
  Options:
124
- --out-dir, -o <dir> Output directory (default: dist)
124
+ --out-dir, -o <dir> Workspace output root (default: dist). Profile artifacts land in <out-dir>/<name> (name defaults to profile id; CDN: name:'cdn' → dist/cdn)
125
125
  --release Release build (omit serve-host; pack minify slot; proof)
126
- --profile <name> Delivery profile (default from config; builtins: web-ssr|web-static|web-client|web-hybrid)
127
- --origin <url> Site origin for static-cdn canonical/sitemap
126
+ --profile <name> Delivery profile (default from config; builtins: web-ssr|static|web-client|web-hybrid)
127
+ --target <id> browser (default) | mini-program-wechat (pack dist/wechat for WeChat DevTools; build+dev)
128
+ --origin <url> Site origin for web-static canonical/sitemap
128
129
  --host <host> Listen host (default: 127.0.0.1)
129
130
  --port <port> Listen port (dev: omit = auto from 5173; set = lock)
130
131
  --poll-ms <ms> Dev watch poll interval (default: 300)
@@ -332,28 +333,35 @@ async function runWithPlugins(ws, project, outDir, fn) {
332
333
  */
333
334
  async function cmdBuild(args) {
334
335
  const pathArg = args._[0] ?? '.';
335
- const { project, outDir } = resolveWorkspaceDirs({
336
+ const targetRaw = typeof args.target === 'string' ? args.target : 'browser';
337
+ if (targetRaw !== 'browser' && targetRaw !== 'mini-program-wechat') {
338
+ log.error(`unknown --target ${targetRaw} (browser | mini-program-wechat)`);
339
+ return 1;
340
+ }
341
+ const wechatPack = targetRaw === 'mini-program-wechat';
342
+ const { project, outDir: outDirRoot } = resolveWorkspaceDirs({
336
343
  path: pathArg,
337
344
  outDir: typeof args['out-dir'] === 'string' ? args['out-dir'] : undefined,
338
345
  });
339
- log.info(`build ${project} ${outDir}`);
346
+ const cfg = await loadVmzConfig(project);
347
+ const cliProfile = typeof args.profile === 'string' ? args.profile : '';
348
+ const norm = normalizeDeliveryAuthoring(cfg.delivery ?? null);
349
+ if (!norm.ok) {
350
+ log.diagnostics(norm.diagnostics ?? []);
351
+ log.error('delivery authoring invalid');
352
+ return 1;
353
+ }
354
+ const selected = selectBuildProfile(norm.table, cliProfile);
355
+ if (!selected.ok) {
356
+ log.diagnostics(selected.diagnostics ?? []);
357
+ log.error(`unknown build --profile ${cliProfile || norm.table.default}`);
358
+ return 1;
359
+ }
360
+ const outDir = resolveProfileArtifactDir(outDirRoot, selected.profile);
361
+ log.info(`build ${project} → ${outDir}${wechatPack ? ' (target=mini-program-wechat)' : ''} (out-dir=${outDirRoot}, name=${selected.profile.name})`);
340
362
  const ws = createWorkspace({ root: project, outDir });
341
363
  try {
342
- const cfg = await loadVmzConfig(project);
343
- const cliProfile = typeof args.profile === 'string' ? args.profile : '';
344
- const norm = normalizeDeliveryAuthoring(cfg.delivery ?? null);
345
- if (!norm.ok) {
346
- log.diagnostics(norm.diagnostics ?? []);
347
- log.error('delivery authoring invalid');
348
- return 1;
349
- }
350
- const selected = selectBuildProfile(norm.table, cliProfile);
351
- if (!selected.ok) {
352
- log.diagnostics(selected.diagnostics ?? []);
353
- log.error(`unknown build --profile ${cliProfile || norm.table.default}`);
354
- return 1;
355
- }
356
- log.info(`delivery profile ${selected.selection.profileId} (assembly=${selected.selection.assembly})`);
364
+ log.info(`delivery profile ${selected.selection.profileId} (assembly=${selected.selection.assembly}, name=${selected.profile.name})`);
357
365
  const code = await runWithPlugins(ws, project, outDir, () => {
358
366
  const report = ws.build(Boolean(args.release));
359
367
  const errors = log.diagnostics(report.diagnostics ?? []);
@@ -394,6 +402,29 @@ async function cmdBuild(args) {
394
402
  if (!docs.ok)
395
403
  return 1;
396
404
  }
405
+ if (wechatPack) {
406
+ if (typeof ws.lowerMiniprogramWechatPackaging !== 'function') {
407
+ log.error('wechat pack: workspace missing lowerMiniprogramWechatPackaging');
408
+ return 1;
409
+ }
410
+ let report;
411
+ try {
412
+ const raw = ws.lowerMiniprogramWechatPackaging();
413
+ report = typeof raw === 'string' ? JSON.parse(raw) : raw;
414
+ }
415
+ catch (err) {
416
+ log.error(`wechat pack failed: ${err instanceof Error ? err.message : String(err)}`);
417
+ return 1;
418
+ }
419
+ log.diagnostics(report.diagnostics ?? []);
420
+ if (report.status !== 'ready') {
421
+ log.error(`wechat pack ${report.status || 'failed'}`);
422
+ return 1;
423
+ }
424
+ const packRoot = report.packRoot || 'dist/wechat';
425
+ log.info(`wechat pack ok → ${path.join(project, packRoot)} (open in WeChat DevTools)`);
426
+ return 0;
427
+ }
397
428
  let pack = null;
398
429
  try {
399
430
  pack = packFromDeploymentIr(outDir, {
@@ -419,8 +450,8 @@ async function cmdBuild(args) {
419
450
  const origin = typeof args.origin === 'string' ? args.origin : undefined;
420
451
  let assemble = null;
421
452
  try {
422
- if (selected.selection.assembly === 'static-cdn') {
423
- log.info(`web-static emit ${outDir}`);
453
+ if (selected.selection.assembly === 'web-static') {
454
+ log.info(`static emit ${outDir}`);
424
455
  }
425
456
  assemble = await assembleDelivery(outDir, {
426
457
  selection: selected.selection,
@@ -431,10 +462,11 @@ async function cmdBuild(args) {
431
462
  siteId: cfg.application?.id || undefined,
432
463
  origin,
433
464
  pack: pack.manifest,
465
+ projectRoot: project,
434
466
  });
435
467
  for (const step of assemble.manifest.steps || []) {
436
- if (step.kind === 'static-cdn') {
437
- log.info(`web-static ok (${step.htmlFiles} html, ${step.skipped} skipped, digest=${String(step.digest).slice(0, 12)}…)`);
468
+ if (step.kind === 'web-static') {
469
+ log.info(`static ok (${step.htmlFiles} html, ${step.skipped} skipped, digest=${String(step.digest).slice(0, 12)}…)`);
438
470
  }
439
471
  else if (step.kind === 'site-delivery' && step.digest) {
440
472
  log.info(`site-delivery ok (digest=${String(step.digest).slice(0, 12)}…)`);
@@ -465,20 +497,33 @@ async function cmdServe(args) {
465
497
  if (code !== 0)
466
498
  return code;
467
499
  }
468
- const { project, outDir } = resolveWorkspaceDirs({
500
+ const { project, outDir: outDirRoot } = resolveWorkspaceDirs({
469
501
  path: pathArg,
470
502
  outDir: typeof args['out-dir'] === 'string' ? args['out-dir'] : undefined,
471
503
  });
504
+ const cfg = await loadVmzConfig(project);
505
+ const cliProfile = typeof args.profile === 'string' ? args.profile : '';
506
+ const norm = normalizeDeliveryAuthoring(cfg.delivery ?? null);
507
+ if (!norm.ok) {
508
+ log.diagnostics(norm.diagnostics ?? []);
509
+ log.error('delivery authoring invalid');
510
+ return 1;
511
+ }
512
+ const selected = selectBuildProfile(norm.table, cliProfile);
513
+ if (!selected.ok) {
514
+ log.diagnostics(selected.diagnostics ?? []);
515
+ log.error(`unknown build --profile ${cliProfile || norm.table.default}`);
516
+ return 1;
517
+ }
518
+ const outDir = resolveProfileArtifactDir(outDirRoot, selected.profile);
472
519
  const hostJs = path.join(outDir, 'vmz-serve-host.mjs');
473
520
  if (!existsSync(hostJs)) {
474
- const coreDist = resolveCoreRuntimeDist();
475
- const src = coreDist ? path.join(coreDist, 'serve-host.mjs') : null;
476
- if (src && existsSync(src)) {
477
- copyFileSync(src, hostJs);
521
+ try {
522
+ materializeServeHostRuntime(outDir);
478
523
  log.info(`materialized ${hostJs} from @vmz/core (release builds omit it)`);
479
524
  }
480
- else {
481
- log.error(`missing ${hostJs} — run \`vmz build\` (without --release) or ensure @vmz/core is installed`);
525
+ catch (err) {
526
+ log.error(`missing ${hostJs} — run \`vmz build\` (without --release) or ensure @vmz/core is installed (${err instanceof Error ? err.message : err})`);
482
527
  return 1;
483
528
  }
484
529
  }
@@ -493,6 +538,8 @@ async function cmdServe(args) {
493
538
  VMZ_DIST: outDir,
494
539
  VMZ_PORT: String(port),
495
540
  VMZ_HOST: host,
541
+ VMZ_PROJECT_ROOT: project,
542
+ VMZ_NATIVE_NODE: resolveNativePath(),
496
543
  },
497
544
  stdio: 'inherit',
498
545
  });
@@ -517,10 +564,25 @@ async function cmdServe(args) {
517
564
  */
518
565
  async function cmdDev(args) {
519
566
  const pathArg = args._[0] ?? '.';
520
- const { project, outDir } = resolveWorkspaceDirs({
567
+ const { project, outDir: outDirRoot } = resolveWorkspaceDirs({
521
568
  path: pathArg,
522
569
  outDir: typeof args['out-dir'] === 'string' ? args['out-dir'] : undefined,
523
570
  });
571
+ const cfg = await loadVmzConfig(project);
572
+ const cliProfile = typeof args.profile === 'string' ? args.profile : '';
573
+ const norm = normalizeDeliveryAuthoring(cfg.delivery ?? null);
574
+ if (!norm.ok) {
575
+ log.diagnostics(norm.diagnostics ?? []);
576
+ log.error('delivery authoring invalid');
577
+ return 1;
578
+ }
579
+ const selected = selectBuildProfile(norm.table, cliProfile);
580
+ if (!selected.ok) {
581
+ log.diagnostics(selected.diagnostics ?? []);
582
+ log.error(`unknown build --profile ${cliProfile || norm.table.default}`);
583
+ return 1;
584
+ }
585
+ const outDir = resolveProfileArtifactDir(outDirRoot, selected.profile);
524
586
  const host = typeof args.host === 'string' ? args.host : '127.0.0.1';
525
587
  const portLocked = Object.prototype.hasOwnProperty.call(args, 'port');
526
588
  let port;
@@ -539,6 +601,11 @@ async function cmdDev(args) {
539
601
  }
540
602
  }
541
603
  const pollMs = Number(args['poll-ms'] ?? 300);
604
+ const targetRaw = typeof args.target === 'string' ? args.target : 'browser';
605
+ if (targetRaw !== 'browser' && targetRaw !== 'mini-program-wechat') {
606
+ log.error(`unknown --target ${targetRaw} (browser | mini-program-wechat)`);
607
+ return 1;
608
+ }
542
609
  const ac = new AbortController();
543
610
  const onSig = () => {
544
611
  log.info('shutting down…');
@@ -552,6 +619,7 @@ async function cmdDev(args) {
552
619
  host,
553
620
  port,
554
621
  pollMs,
622
+ target: targetRaw,
555
623
  signal: ac.signal,
556
624
  });
557
625
  try {
@@ -1,9 +1,30 @@
1
1
  /**
2
2
  * A3: content-addressed assets/<hash> layout for immutable CDN objects.
3
- * Logical paths stay available for serve/dev; web-static HTML rewrites to hashed URLs.
4
- * Identical bytes identical asset path (cross-release / cross-source reuse by digest).
3
+ * Logical paths stay available for serve/dev; static HTML rewrites to hashed URLs.
4
+ * CSS aggregators (vmz.css) rewrite `@import` to hashed sibling paths under assets/.
5
+ * JS under assets/ always rewrites ESM `./` → `../` so barrels (vmz-dom → dom-core)
6
+ * resolve at dist root — never prefer hashed siblings for JS (second-hop 404).
5
7
  */
6
8
  export declare const CONTENT_ADDRESSED_ASSETS_SCHEMA = "vmz.content_addressed_assets.v0";
9
+ /**
10
+ * Rewrite relative `@import "./foo.css"` to hashed paths under assets/.
11
+ * @param {string} cssText
12
+ * @param {Record<string, string>} rewrites logical (no leading slash) or `/logical` → `assets/hash.ext`
13
+ */
14
+ export declare function rewriteCssImports(cssText: any, rewrites: any): any;
15
+ /**
16
+ * Rewrite relative ESM so a file served from `/assets/<hash>.js` resolves against
17
+ * dist root (static `from "./x"` / `export * from "./x"` + dynamic `import("./"+…)`).
18
+ *
19
+ * Always use `../…` (Fix A). Do **not** prefer hashed siblings under `assets/`:
20
+ * barrels like `vmz-dom.js` (`export * from './dom-core.js'`) would then resolve
21
+ * as `/assets/dom-core.js` and 404. `rewrites` is accepted for API parity with CSS
22
+ * but intentionally ignored for JS path choice.
23
+ *
24
+ * @param {string} jsText
25
+ * @param {Record<string, string>} [_rewrites]
26
+ */
27
+ export declare function rewriteJsEntryRelativeImports(jsText: any, _rewrites?: {}): string;
7
28
  /**
8
29
  * Emit `assets/<sha256>.<ext>` copies and rewrite HTML href/src to hashed URLs.
9
30
  * @param {string} distDir