@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.
Files changed (55) hide show
  1. package/dist/build-assemble.d.ts +52 -0
  2. package/dist/build-assemble.js +192 -0
  3. package/dist/cdn-policy.d.ts +22 -4
  4. package/dist/cdn-policy.js +109 -13
  5. package/dist/cli.js +122 -27
  6. package/dist/content-addressed-assets.js +2 -1
  7. package/dist/delivery-profile.d.ts +84 -0
  8. package/dist/delivery-profile.js +348 -0
  9. package/dist/dev-session.d.ts +6 -0
  10. package/dist/dev-session.js +167 -40
  11. package/dist/document-build.js +33 -32
  12. package/dist/document-cmd.js +2 -9
  13. package/dist/document-enrich.js +8 -0
  14. package/dist/document-integrate.js +10 -17
  15. package/dist/embedded-packaging.d.ts +22 -0
  16. package/dist/embedded-packaging.js +109 -0
  17. package/dist/index.d.ts +26 -1
  18. package/dist/index.js +69 -2
  19. package/dist/locale-check.js +39 -66
  20. package/dist/locale-cmd.js +12 -44
  21. package/dist/locale-route-emit.d.ts +37 -0
  22. package/dist/locale-route-emit.js +109 -0
  23. package/dist/locale-router.d.ts +20 -0
  24. package/dist/locale-router.js +68 -0
  25. package/dist/log.d.ts +2 -2
  26. package/dist/log.js +11 -3
  27. package/dist/mini-host.d.ts +47 -0
  28. package/dist/mini-host.js +202 -0
  29. package/dist/native-addon.d.ts +9 -0
  30. package/dist/native-addon.js +84 -0
  31. package/dist/pack-client-packages.d.ts +25 -0
  32. package/dist/pack-client-packages.js +399 -0
  33. package/dist/pack.d.ts +58 -0
  34. package/dist/pack.js +123 -0
  35. package/dist/plugin-host.d.ts +1 -1
  36. package/dist/plugin-host.js +2 -2
  37. package/dist/pretty-json.d.ts +19 -0
  38. package/dist/pretty-json.js +43 -0
  39. package/dist/production-observability.js +3 -2
  40. package/dist/production-test-pack.d.ts +0 -14
  41. package/dist/production-test-pack.js +27 -31
  42. package/dist/release-pack.js +17 -21
  43. package/dist/route-path.d.ts +35 -0
  44. package/dist/route-path.js +77 -0
  45. package/dist/server-artifact.d.ts +140 -0
  46. package/dist/server-artifact.js +204 -0
  47. package/dist/server-language-backend.d.ts +89 -0
  48. package/dist/server-language-backend.js +121 -0
  49. package/dist/site-delivery.js +3 -2
  50. package/dist/static-emit.d.ts +10 -1
  51. package/dist/static-emit.js +192 -97
  52. package/dist/test-cmd.js +2 -1
  53. package/dist/wechat-packaging.d.ts +22 -0
  54. package/dist/wechat-packaging.js +59 -0
  55. package/package.json +12 -12
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@
5
5
  import { spawn } from 'node:child_process';
6
6
  import { copyFileSync, 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, 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';
@@ -16,14 +16,16 @@ import { cmdDocument } from './document-cmd.js';
16
16
  import { buildIntegratedDocuments, projectHasDocuments } from './document-integrate.js';
17
17
  import { cmdLocale } from './locale-cmd.js';
18
18
  import { emitLocaleRuntimeModules, localeHasErrors } from './locale-check.js';
19
+ import { emitLocaleRouteRealization } from './locale-route-emit.js';
19
20
  import { cmdApplication } from './application-cmd.js';
20
21
  import { cmdArtifact } from './release-cmd.js';
21
22
  import { cmdRefactor } from './refactor-cmd.js';
22
23
  import { cmdExplain } from './explain-cmd.js';
23
24
  import { resolveNativeVmzCli } from './resolve-native-cli.js';
24
- import { emitWebStatic } from './static-emit.js';
25
- import { emitSiteDelivery } from './site-delivery.js';
26
25
  import { loadVmzConfig } from './plugin-host.js';
26
+ import { normalizeDeliveryAuthoring, selectBuildProfile } from './delivery-profile.js';
27
+ import { packFromDeploymentIr } from './pack.js';
28
+ import { assembleDelivery, emitBuildProof } from './build-assemble.js';
27
29
  /**
28
30
  * @param {string[]} argv
29
31
  */
@@ -104,8 +106,8 @@ Usage:
104
106
  vmz check [path] Check project via Workspace
105
107
  vmz build [path] [options] Build project via Workspace
106
108
  vmz serve [path] [options] Serve dist (optional --build)
107
- vmz dev [path] [options] Long-lived rebuild session (no CLI spawn)
108
- 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)
109
111
  vmz lint [path] [--deny-warnings] Lint (= check) via N-API
110
112
  vmz test [path] [options] Native test discover / report
111
113
  vmz document|docs <cmd> Project /documents domain
@@ -120,9 +122,10 @@ Usage:
120
122
 
121
123
  Options:
122
124
  --out-dir, -o <dir> Output directory (default: dist)
123
- --release Release build (build only)
124
- --profile <name> Delivery profile after build (web-static)
125
- --origin <url> Site origin for web-static canonical/sitemap
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
+ --target <id> Dev preview: browser (default) | mini-program-wechat (pack dist/wechat; WeChat DevTools)
128
+ --origin <url> Site origin for static-cdn canonical/sitemap
126
129
  --host <host> Listen host (default: 127.0.0.1)
127
130
  --port <port> Listen port (dev: omit = auto from 5173; set = lock)
128
131
  --poll-ms <ms> Dev watch poll interval (default: 300)
@@ -281,7 +284,11 @@ function cmdCheck(args) {
281
284
  try {
282
285
  return runWithPlugins(ws, project, outDir, async () => {
283
286
  const report = ws.check();
284
- const errors = log.diagnostics(report.diagnostics ?? []);
287
+ const { checkLocales, localeHasErrors } = await import('./locale-check.js');
288
+ const localeReport = checkLocales({ projectRoot: project, checkUnused: false });
289
+ // Locale policy is first-class: missing /locales is warning (not silent), hard errors still fail.
290
+ const errors = log.diagnostics([...(report.diagnostics ?? []), ...(localeReport.diagnostics ?? [])]) ||
291
+ (localeHasErrors(localeReport) ? 1 : 0);
285
292
  log.info(`checked ${report.filesChecked} file(s)`);
286
293
  return errors ? 1 : 0;
287
294
  });
@@ -290,6 +297,23 @@ function cmdCheck(args) {
290
297
  ws.dispose();
291
298
  }
292
299
  }
300
+ /**
301
+ * Dedupe locale/build diagnostics by code+path+message (runtime + route emit both report missing manifest).
302
+ * @param {Array<{ code?: string, path?: string, message?: string, severity?: string }>} list
303
+ */
304
+ function dedupeDiagnostics(list) {
305
+ const seen = new Set();
306
+ /** @type {typeof list} */
307
+ const out = [];
308
+ for (const d of list || []) {
309
+ const key = `${d.code || ''}\0${d.path || ''}\0${d.message || ''}\0${d.severity || ''}`;
310
+ if (seen.has(key))
311
+ continue;
312
+ seen.add(key);
313
+ out.push(d);
314
+ }
315
+ return out;
316
+ }
293
317
  /**
294
318
  * @param {import('./index.js').Workspace} ws
295
319
  * @param {string} project
@@ -316,6 +340,21 @@ async function cmdBuild(args) {
316
340
  log.info(`build ${project} → ${outDir}`);
317
341
  const ws = createWorkspace({ root: project, outDir });
318
342
  try {
343
+ const cfg = await loadVmzConfig(project);
344
+ const cliProfile = typeof args.profile === 'string' ? args.profile : '';
345
+ const norm = normalizeDeliveryAuthoring(cfg.delivery ?? null);
346
+ if (!norm.ok) {
347
+ log.diagnostics(norm.diagnostics ?? []);
348
+ log.error('delivery authoring invalid');
349
+ return 1;
350
+ }
351
+ const selected = selectBuildProfile(norm.table, cliProfile);
352
+ if (!selected.ok) {
353
+ log.diagnostics(selected.diagnostics ?? []);
354
+ log.error(`unknown build --profile ${cliProfile || norm.table.default}`);
355
+ return 1;
356
+ }
357
+ log.info(`delivery profile ${selected.selection.profileId} (assembly=${selected.selection.assembly})`);
319
358
  const code = await runWithPlugins(ws, project, outDir, () => {
320
359
  const report = ws.build(Boolean(args.release));
321
360
  const errors = log.diagnostics(report.diagnostics ?? []);
@@ -330,47 +369,96 @@ async function cmdBuild(args) {
330
369
  if (code !== 0)
331
370
  return code;
332
371
  const localeEmit = emitLocaleRuntimeModules(project, outDir);
372
+ const localeRoutes = emitLocaleRouteRealization(project, outDir, {
373
+ origin: typeof args.origin === 'string' ? args.origin : undefined,
374
+ });
375
+ // Always surface locale diagnostics (warnings included) — missing /locales must not be silent.
376
+ // Dedupe: runtime emit + route realization both report the same missing-manifest warning.
377
+ const localeDiags = dedupeDiagnostics([...(localeEmit.diagnostics ?? []), ...(localeRoutes.diagnostics ?? [])]);
378
+ log.diagnostics(localeDiags);
333
379
  if (!localeEmit.ok || localeHasErrors({ diagnostics: localeEmit.diagnostics })) {
334
- log.diagnostics(localeEmit.diagnostics ?? []);
335
380
  log.error('locale runtime emit failed');
336
381
  return 1;
337
382
  }
338
383
  if (localeEmit.written.length) {
339
384
  log.info(`locale runtime emit (${localeEmit.written.length} module(s))`);
340
385
  }
386
+ if (!localeRoutes.ok) {
387
+ log.error('locale route realization emit failed');
388
+ return 1;
389
+ }
390
+ if (localeRoutes.written.length) {
391
+ log.info(`locale route realization (${localeRoutes.written.length} artifact(s))`);
392
+ }
341
393
  if (projectHasDocuments(project)) {
342
394
  const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
343
395
  if (!docs.ok)
344
396
  return 1;
345
397
  }
346
- const cfg = await loadVmzConfig(project);
347
- if (cfg.delivery) {
348
- log.info(`site-delivery emit ${outDir}`);
349
- const site = emitSiteDelivery(outDir, cfg.delivery, {
350
- siteId: cfg.application?.id || undefined,
398
+ let pack = null;
399
+ try {
400
+ pack = packFromDeploymentIr(outDir, {
401
+ release: Boolean(args.release),
402
+ profileId: selected.selection.profileId,
403
+ assembly: selected.selection.assembly,
404
+ coreDist: resolveCoreRuntimeDist(),
405
+ projectRoot: project,
351
406
  });
352
- log.info(`site-delivery ok (digest=${String(site.contract.contractDigest).slice(0, 12)}…)`);
407
+ const lower = pack.manifest?.clientPackageLowering;
408
+ if (lower?.bareSpecs?.length) {
409
+ log.info(`pack client packages (${lower.bareSpecs.length} bare → vendor; rewritten=${lower.rewrittenFiles})`);
410
+ }
411
+ if (lower?.remainingBareSpecs?.length) {
412
+ log.warn(`pack client packages: ${lower.remainingBareSpecs.length} bare specifier(s) still unresolved for browser: ${lower.remainingBareSpecs.slice(0, 8).join(', ')}${lower.remainingBareSpecs.length > 8 ? '…' : ''}`);
413
+ }
414
+ log.info(`pack ok (units=${pack.manifest.unitCount}, digest=${String(pack.manifest.packDigest).slice(0, 12)}…)`);
353
415
  }
354
- const profile = typeof args.profile === 'string' ? args.profile : '';
355
- if (profile === 'web-static') {
356
- const origin = typeof args.origin === 'string' ? args.origin : undefined;
357
- log.info(`web-static emit ${outDir}`);
358
- const result = await emitWebStatic(outDir, { origin });
359
- log.info(`web-static ok (${result.htmlFiles.length} html, ${result.skipped.length} skipped, digest=${result.digest.slice(0, 12)}…)`);
416
+ catch (err) {
417
+ log.error(`pack failed: ${err instanceof Error ? err.message : String(err)}`);
418
+ return 1;
419
+ }
420
+ const origin = typeof args.origin === 'string' ? args.origin : undefined;
421
+ let assemble = null;
422
+ try {
423
+ if (selected.selection.assembly === 'static-cdn') {
424
+ log.info(`web-static emit ${outDir}`);
425
+ }
426
+ assemble = await assembleDelivery(outDir, {
427
+ selection: selected.selection,
428
+ profile: {
429
+ ...selected.profile,
430
+ sources: selected.profile.sources || (norm.table.sugar ? norm.table.profiles[norm.table.default]?.sources : null),
431
+ },
432
+ siteId: cfg.application?.id || undefined,
433
+ origin,
434
+ pack: pack.manifest,
435
+ });
436
+ for (const step of assemble.manifest.steps || []) {
437
+ if (step.kind === 'static-cdn') {
438
+ log.info(`web-static ok (${step.htmlFiles} html, ${step.skipped} skipped, digest=${String(step.digest).slice(0, 12)}…)`);
439
+ }
440
+ else if (step.kind === 'site-delivery' && step.digest) {
441
+ log.info(`site-delivery ok (digest=${String(step.digest).slice(0, 12)}…)`);
442
+ }
443
+ }
360
444
  }
361
- else if (profile) {
362
- log.error(`unknown build --profile ${profile} (supported: web-static)`);
445
+ catch (err) {
446
+ log.error(`assemble failed: ${err instanceof Error ? err.message : String(err)}`);
363
447
  return 1;
364
448
  }
449
+ const proof = emitBuildProof(outDir, {
450
+ selection: selected.selection,
451
+ pack: pack.manifest,
452
+ assemble: assemble.manifest,
453
+ release: Boolean(args.release),
454
+ });
455
+ log.info(`build-proof ok (profile=${proof.proof.profileId}, slots=${proof.proof.semanticIds.join(',')})`);
365
456
  return 0;
366
457
  }
367
458
  finally {
368
459
  ws.dispose();
369
460
  }
370
461
  }
371
- /**
372
- * @param {Record<string, string | boolean> & { _: string[] }} args
373
- */
374
462
  async function cmdServe(args) {
375
463
  const pathArg = args._[0] ?? '.';
376
464
  if (args.build) {
@@ -406,6 +494,7 @@ async function cmdServe(args) {
406
494
  VMZ_DIST: outDir,
407
495
  VMZ_PORT: String(port),
408
496
  VMZ_HOST: host,
497
+ VMZ_NATIVE_NODE: resolveNativePath(),
409
498
  },
410
499
  stdio: 'inherit',
411
500
  });
@@ -452,6 +541,11 @@ async function cmdDev(args) {
452
541
  }
453
542
  }
454
543
  const pollMs = Number(args['poll-ms'] ?? 300);
544
+ const targetRaw = typeof args.target === 'string' ? args.target : 'browser';
545
+ if (targetRaw !== 'browser' && targetRaw !== 'mini-program-wechat') {
546
+ log.error(`unknown --target ${targetRaw} (browser | mini-program-wechat)`);
547
+ return 1;
548
+ }
455
549
  const ac = new AbortController();
456
550
  const onSig = () => {
457
551
  log.info('shutting down…');
@@ -465,6 +559,7 @@ async function cmdDev(args) {
465
559
  host,
466
560
  port,
467
561
  pollMs,
562
+ target: targetRaw,
468
563
  signal: ac.signal,
469
564
  });
470
565
  try {
@@ -8,6 +8,7 @@ import crypto from 'node:crypto';
8
8
  import fs from 'node:fs';
9
9
  import path from 'node:path';
10
10
  import { canonicalJson, sha256Hex } from './release-pack.js';
11
+ import { writePrettyJsonFile } from './pretty-json.js';
11
12
  export const CONTENT_ADDRESSED_ASSETS_SCHEMA = 'vmz.content_addressed_assets.v0';
12
13
  /** Immutable delivery candidates (client-facing bytes). */
13
14
  const DEFAULT_CANDIDATES = [
@@ -85,7 +86,7 @@ export function emitContentAddressedAssets(distDir, opts = {}) {
85
86
  const vmzDir = path.join(abs, '_vmz');
86
87
  fs.mkdirSync(vmzDir, { recursive: true });
87
88
  const outPath = path.join(vmzDir, 'content-addressed-assets.json');
88
- fs.writeFileSync(outPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
89
+ writePrettyJsonFile(outPath, manifest);
89
90
  return { manifest, assetsDir, rewrites, manifestPath: outPath };
90
91
  }
91
92
  /**
@@ -0,0 +1,84 @@
1
+ /**
2
+ * B0 — Delivery profile authoring normalize + CLI --profile resolve.
3
+ * Pure data only; expands legacy site-delivery sugar into profiles[default].
4
+ */
5
+ export declare const DELIVERY_PROFILE_AUTHORING_SCHEMA = "vmz.delivery.authoring.v0";
6
+ export declare const BUILD_PROFILE_SELECTION_SCHEMA = "vmz.build.profile_selection.v0";
7
+ /** Browser-era assembly kinds (04 B5). */
8
+ export declare const ASSEMBLIES: readonly string[];
9
+ export declare const SERVER_RUNTIMES: readonly string[];
10
+ /** Official built-in aliases when not overridden in config. */
11
+ export declare const BUILTIN_PROFILES: Readonly<{
12
+ 'web-client': {
13
+ host: string;
14
+ assembly: string;
15
+ };
16
+ 'web-static': {
17
+ host: string;
18
+ assembly: string;
19
+ };
20
+ 'web-ssr': {
21
+ host: string;
22
+ assembly: string;
23
+ serverRuntime: string;
24
+ };
25
+ 'web-hybrid': {
26
+ host: string;
27
+ assembly: string;
28
+ serverRuntime: string;
29
+ };
30
+ }>;
31
+ export declare function pickSiteAuthoring(raw: any): {
32
+ artifact: string;
33
+ sources: any;
34
+ };
35
+ /**
36
+ * `delivery.packaging.wechat` — vendor identity, not WeChat JSON / wx APIs.
37
+ * @param {unknown} raw
38
+ * @param {Array<{ code: string, message: string }>} diagnostics
39
+ */
40
+ export declare function pickDeliveryPackaging(raw: any, diagnostics: any): {
41
+ wechat?: undefined;
42
+ } | {
43
+ wechat: {};
44
+ };
45
+ export declare function normalizeDeliveryAuthoring(raw: any): {
46
+ ok: boolean;
47
+ table: {
48
+ schema: string;
49
+ default: string;
50
+ profiles: {};
51
+ sugar: boolean;
52
+ };
53
+ diagnostics?: undefined;
54
+ } | {
55
+ ok: boolean;
56
+ diagnostics: any[];
57
+ table?: undefined;
58
+ };
59
+ export declare function selectBuildProfile(table: any, cliProfile?: string): {
60
+ ok: boolean;
61
+ diagnostics: {
62
+ code: string;
63
+ message: string;
64
+ }[];
65
+ selection?: undefined;
66
+ profile?: undefined;
67
+ } | {
68
+ ok: boolean;
69
+ selection: {
70
+ schema: string;
71
+ profileId: any;
72
+ host: any;
73
+ assembly: any;
74
+ serverRuntime: any;
75
+ hasSiteSources: boolean;
76
+ authoringDigest: any;
77
+ fromCli: boolean;
78
+ };
79
+ profile: any;
80
+ diagnostics?: undefined;
81
+ };
82
+ export declare function semanticIdsForAssembly(assembly: any): string[];
83
+ export declare function canonicalJson(value: any): string;
84
+ export declare function sha256Hex(text: any): string;