@vmz/vmz 0.0.2 → 0.0.4

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 (83) hide show
  1. package/README.md +19 -13
  2. package/dist/application-cmd.d.ts +1 -2
  3. package/dist/application-cmd.js +9 -10
  4. package/dist/bundler-adapter.d.ts +2 -3
  5. package/dist/bundler-adapter.js +2 -3
  6. package/dist/cdn-policy.d.ts +178 -0
  7. package/dist/cdn-policy.js +344 -0
  8. package/dist/cli.d.ts +10 -2
  9. package/dist/cli.js +179 -16
  10. package/dist/content-addressed-assets.d.ts +69 -0
  11. package/dist/content-addressed-assets.js +206 -0
  12. package/dist/dev-session.d.ts +2 -2
  13. package/dist/dev-session.js +51 -16
  14. package/dist/document-build.js +1 -2
  15. package/dist/document-check.d.ts +1 -1
  16. package/dist/document-check.js +4 -4
  17. package/dist/document-cmd.d.ts +1 -2
  18. package/dist/document-cmd.js +2 -3
  19. package/dist/document-designs.js +31 -3
  20. package/dist/document-enrich.js +2 -3
  21. package/dist/document-evidence.d.ts +4 -4
  22. package/dist/document-evidence.js +20 -12
  23. package/dist/document-integrate.d.ts +0 -1
  24. package/dist/document-integrate.js +0 -1
  25. package/dist/document-interactive.d.ts +11 -11
  26. package/dist/document-interactive.js +12 -13
  27. package/dist/document-locale.d.ts +1 -1
  28. package/dist/document-locale.js +1 -1
  29. package/dist/document-markdown.d.ts +1 -2
  30. package/dist/document-markdown.js +13 -7
  31. package/dist/document-scan.d.ts +4 -4
  32. package/dist/document-scan.js +4 -4
  33. package/dist/document-schema.d.ts +28 -29
  34. package/dist/document-schema.js +28 -29
  35. package/dist/explain-cmd.js +3 -3
  36. package/dist/index.d.ts +349 -789
  37. package/dist/index.js +109 -85
  38. package/dist/invocation.d.ts +68 -0
  39. package/dist/invocation.js +169 -0
  40. package/dist/locale-check.d.ts +19 -3
  41. package/dist/locale-check.js +130 -6
  42. package/dist/locale-cmd.js +6 -7
  43. package/dist/locale-delivery.d.ts +38 -38
  44. package/dist/locale-delivery.js +39 -40
  45. package/dist/locale-router.d.ts +39 -40
  46. package/dist/locale-router.js +39 -40
  47. package/dist/locale-runtime.d.ts +39 -39
  48. package/dist/locale-runtime.js +44 -45
  49. package/dist/locale-schema.d.ts +1 -2
  50. package/dist/locale-schema.js +1 -2
  51. package/dist/locale-tooling.d.ts +13 -13
  52. package/dist/locale-tooling.js +14 -15
  53. package/dist/log.d.ts +1 -1
  54. package/dist/log.js +1 -1
  55. package/dist/packages.d.ts +1 -2
  56. package/dist/packages.js +1 -2
  57. package/dist/plugin-host.d.ts +11 -3
  58. package/dist/plugin-host.js +21 -5
  59. package/dist/port.d.ts +10 -0
  60. package/dist/port.js +46 -0
  61. package/dist/production-observability.d.ts +286 -0
  62. package/dist/production-observability.js +469 -0
  63. package/dist/production-test-pack.d.ts +158 -0
  64. package/dist/production-test-pack.js +452 -0
  65. package/dist/refactor-cmd.d.ts +1 -1
  66. package/dist/refactor-cmd.js +6 -6
  67. package/dist/release-cmd.d.ts +8 -0
  68. package/dist/release-cmd.js +126 -0
  69. package/dist/release-pack.d.ts +96 -0
  70. package/dist/release-pack.js +337 -0
  71. package/dist/resolve-native-cli.d.ts +14 -0
  72. package/dist/resolve-native-cli.js +84 -0
  73. package/dist/resolve.d.ts +1 -2
  74. package/dist/resolve.js +1 -2
  75. package/dist/site-delivery.d.ts +134 -0
  76. package/dist/site-delivery.js +345 -0
  77. package/dist/static-emit.d.ts +136 -0
  78. package/dist/static-emit.js +463 -0
  79. package/dist/test-cmd.d.ts +2 -2
  80. package/dist/test-cmd.js +37 -17
  81. package/dist/watch-diff.d.ts +1 -1
  82. package/dist/watch-diff.js +1 -1
  83. package/package.json +32 -17
@@ -1,15 +1,16 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Long-lived Node dev session (N2/N4).
3
+ * Long-lived Node dev session (/session).
4
4
  *
5
5
  * Rebuilds go through the N-API `Workspace` — never spawn `cargo` / `vmz-tools`.
6
- * N4: only dirty leaves are marked; Workspace emits affected deployment units.
6
+ * session: only dirty leaves are marked; Workspace emits affected deployment units.
7
7
  */
8
8
  import { spawn } from 'node:child_process';
9
9
  import { existsSync } from 'node:fs';
10
10
  import path from 'node:path';
11
11
  import { buildIntegratedDocuments, projectHasDocuments } from './document-integrate.js';
12
12
  import { createWorkspace } from './index.js';
13
+ import { emitLocaleRuntimeModules, localeHasErrors } from './locale-check.js';
13
14
  import { log } from './log.js';
14
15
  import { diffFingerprints, fileFingerprintMap } from './watch-diff.js';
15
16
  /**
@@ -48,12 +49,23 @@ export function createDevSession(options) {
48
49
  ws.updateFiles(changes);
49
50
  return ws.build();
50
51
  }
52
+ function emitLocales() {
53
+ const localeEmit = emitLocaleRuntimeModules(project, outDir);
54
+ if (!localeEmit.ok || localeHasErrors({ diagnostics: localeEmit.diagnostics })) {
55
+ log.diagnostics(localeEmit.diagnostics ?? []);
56
+ log.error('locale runtime emit failed');
57
+ return false;
58
+ }
59
+ return true;
60
+ }
51
61
  function printReport(report, label) {
52
62
  const errors = log.diagnostics(report.diagnostics ?? []);
53
63
  if (errors) {
54
64
  log.error(`${label} failed (${errors} error(s))`);
55
65
  return false;
56
66
  }
67
+ if (!emitLocales())
68
+ return false;
57
69
  const mode = report.full ? 'full' : 'affected';
58
70
  const chunks = (report.affectedChunks || []).join(', ') || '(none)';
59
71
  log.info(`${label} ok (${mode}; chunks=[${chunks}]; ${(report.emitted ?? []).length} emitted)`);
@@ -65,7 +77,7 @@ export function createDevSession(options) {
65
77
  throw new Error(`vmz dev: missing src/ under ${project}`);
66
78
  }
67
79
  log.info('initial build (N-API workspace, full)…');
68
- // Empty dirty → full project build (N4).
80
+ // Empty dirty → full project build (session).
69
81
  const initial = rebuild();
70
82
  if (!printReport(initial, 'build')) {
71
83
  throw new Error('vmz dev: initial build failed');
@@ -82,7 +94,8 @@ export function createDevSession(options) {
82
94
  }
83
95
  child = spawnHost({ project, outDir, host, port });
84
96
  const docsRoot = path.join(project, 'documents');
85
- const watchRoots = [src].concat(existsSync(docsRoot) ? [docsRoot] : []);
97
+ const localesRoot = path.join(project, 'locales');
98
+ const watchRoots = [src].concat(existsSync(docsRoot) ? [docsRoot] : []).concat(existsSync(localesRoot) ? [localesRoot] : []);
86
99
  log.info(`dev → http://${host}:${port} (watching ${watchRoots.join(', ')})`);
87
100
  /** @type {Map<string, Map<string, string>>} */
88
101
  let fingerprints = new Map();
@@ -100,10 +113,12 @@ export function createDevSession(options) {
100
113
  if (stopped || signal?.aborted)
101
114
  break;
102
115
  if (child && child.exitCode != null) {
103
- throw new Error(`vmz serve-host exited: ${child.exitCode}`);
116
+ log.warn(`serve-host exited (${child.exitCode}) — respawning…`);
117
+ child = spawnHost({ project, outDir, host, port });
118
+ continue;
104
119
  }
105
- /** @type {{ srcChanged: string[], srcDeleted: string[], docsDirty: boolean }} */
106
- let batch = { srcChanged: [], srcDeleted: [], docsDirty: false };
120
+ /** @type {{ srcChanged: string[], srcDeleted: string[], docsDirty: boolean, localesDirty: boolean }} */
121
+ let batch = { srcChanged: [], srcDeleted: [], docsDirty: false, localesDirty: false };
107
122
  try {
108
123
  // Probe only — keep prior fingerprints until debounce resample
109
124
  // (same contract as pre-docs watcher: empty second pass would miss soft reload).
@@ -115,6 +130,10 @@ export function createDevSession(options) {
115
130
  batch.srcChanged = diff.changed;
116
131
  batch.srcDeleted = diff.deleted;
117
132
  }
133
+ else if (root === localesRoot) {
134
+ if (diff.changed.length || diff.deleted.length)
135
+ batch.localesDirty = true;
136
+ }
118
137
  else if (diff.changed.length || diff.deleted.length) {
119
138
  batch.docsDirty = true;
120
139
  }
@@ -124,11 +143,11 @@ export function createDevSession(options) {
124
143
  log.warn('watch error:', err);
125
144
  continue;
126
145
  }
127
- if (!batch.srcChanged.length && !batch.srcDeleted.length && !batch.docsDirty)
146
+ if (!batch.srcChanged.length && !batch.srcDeleted.length && !batch.docsDirty && !batch.localesDirty)
128
147
  continue;
129
148
  await sleep(200);
130
149
  // Resample against the same prior fingerprints, then commit.
131
- batch = { srcChanged: [], srcDeleted: [], docsDirty: false };
150
+ batch = { srcChanged: [], srcDeleted: [], docsDirty: false, localesDirty: false };
132
151
  for (const root of watchRoots) {
133
152
  const prev = fingerprints.get(root) || new Map();
134
153
  const next = fileFingerprintMap(root);
@@ -138,11 +157,15 @@ export function createDevSession(options) {
138
157
  batch.srcChanged = diff.changed;
139
158
  batch.srcDeleted = diff.deleted;
140
159
  }
160
+ else if (root === localesRoot) {
161
+ if (diff.changed.length || diff.deleted.length)
162
+ batch.localesDirty = true;
163
+ }
141
164
  else if (diff.changed.length || diff.deleted.length) {
142
165
  batch.docsDirty = true;
143
166
  }
144
167
  }
145
- if (!batch.srcChanged.length && !batch.srcDeleted.length && !batch.docsDirty)
168
+ if (!batch.srcChanged.length && !batch.srcDeleted.length && !batch.docsDirty && !batch.localesDirty)
146
169
  continue;
147
170
  let needFullReload = batch.docsDirty;
148
171
  if (batch.srcChanged.length || batch.srcDeleted.length) {
@@ -180,12 +203,26 @@ export function createDevSession(options) {
180
203
  : 'soft reload ok (full page)');
181
204
  }
182
205
  catch (err) {
183
- log.warn(`soft reload failed (${err}) — restarting serve-host…`);
184
- killChild(child);
185
- child = spawnHost({ project, outDir, host, port });
206
+ log.warn(`soft reload failed (${err}) — keeping previous serve-host (fix and save)`);
186
207
  }
187
208
  continue;
188
209
  }
210
+ if (batch.localesDirty) {
211
+ log.info('locales change detected — re-emitting #locales runtime…');
212
+ if (!emitLocales()) {
213
+ log.warn('locale runtime emit failed — keeping previous modules');
214
+ continue;
215
+ }
216
+ try {
217
+ await softReload(host, port, { full: true, islandHmr: false });
218
+ log.info('soft reload ok (full page; locales)');
219
+ }
220
+ catch (err) {
221
+ log.warn(`soft reload failed (${err}) — keeping previous serve-host (fix and save)`);
222
+ }
223
+ if (!batch.docsDirty)
224
+ continue;
225
+ }
189
226
  if (batch.docsDirty) {
190
227
  log.info('documents change detected — rebuilding document mount…');
191
228
  const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
@@ -198,9 +235,7 @@ export function createDevSession(options) {
198
235
  log.info('soft reload ok (full page; docs)');
199
236
  }
200
237
  catch (err) {
201
- log.warn(`soft reload failed (${err}) — restarting serve-host…`);
202
- killChild(child);
203
- child = spawnHost({ project, outDir, host, port });
238
+ log.warn(`soft reload failed (${err}) — keeping previous serve-host (fix and save)`);
204
239
  }
205
240
  }
206
241
  }
@@ -1,7 +1,6 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Document D1 Static + D3 Interactive artifacts.
4
- * Design: 规划设计/vmz/19 §5–6 · D1/D3
3
+ * Document Static + Interactive artifacts.
5
4
  */
6
5
  import fs from 'node:fs';
7
6
  import path from 'node:path';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Build DocumentManifest + run D0 / --strict checks.
2
+ * Build DocumentManifest + run / --strict checks.
3
3
  */
4
4
  /**
5
5
  * Resolve project documents root.
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Build DocumentManifest + run D0 / --strict checks.
3
+ * Build DocumentManifest + run / --strict checks.
4
4
  */
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
@@ -44,7 +44,7 @@ export function loadDocumentsConfig(documentsRoot) {
44
44
  return { config: null, diagnostics, configPath: null };
45
45
  }
46
46
  /**
47
- * D0: only declaration objects — no arbitrary hooks.
47
+ *: only declaration objects — no arbitrary hooks.
48
48
  * @param {string} raw
49
49
  * @param {string} filename
50
50
  */
@@ -88,7 +88,7 @@ export function checkDocuments(opts) {
88
88
  diagnostics.push({
89
89
  code: DIAG.CONFIG_MISSING,
90
90
  severity: strict ? 'error' : 'warning',
91
- message: 'documents.config.json|ts missing; D0 requires defaultLocale + locales for strict coverage checks',
91
+ message: 'documents.config.json|ts missing; strict mode requires defaultLocale + locales for strict coverage checks',
92
92
  path: documentsRoot,
93
93
  });
94
94
  }
@@ -175,7 +175,7 @@ export function checkDocuments(opts) {
175
175
  });
176
176
  }
177
177
  }
178
- // D0: no silent whole-page fallback by default
178
+ // no silent whole-page fallback by default
179
179
  if (config && config.fallback === true) {
180
180
  diagnostics.push({
181
181
  code: DIAG.FALLBACK_SILENT,
@@ -1,6 +1,5 @@
1
1
  /**
2
- * `vmz document` / `vmz docs` CLI (D0 check · D1 build).
3
- * Design: 规划设计/vmz/19 §1 · §8
2
+ * `vmz document` / `vmz docs` CLI .
4
3
  */
5
4
  /**
6
5
  * @param {string[]} argv
@@ -1,7 +1,6 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * `vmz document` / `vmz docs` CLI (D0 check · D1 build).
4
- * Design: 规划设计/vmz/19 §1 · §8
3
+ * `vmz document` / `vmz docs` CLI .
5
4
  */
6
5
  import fs from 'node:fs';
7
6
  import path from 'node:path';
@@ -14,7 +13,7 @@ import { createWorkspace } from './index.js';
14
13
  import { log } from './log.js';
15
14
  import { parseArgs } from './cli.js';
16
15
  function printDocumentHelp() {
17
- console.log(`vmz document — project /documents domain (D0–D3)
16
+ console.log(`vmz document — project /documents domain
18
17
 
19
18
  Usage:
20
19
  vmz document check [project] Check locale tree + links/anchors + fence/API evidence
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Document D1 — wire project `/designs` into static document assets.
3
+ * Document — wire project `/designs` into static document assets.
4
4
  * Prefer compiled `vmz-designs.css` / `vmz.css`; else emit a minimal token CSS.
5
5
  */
6
6
  import fs from 'node:fs';
@@ -64,12 +64,17 @@ function emitMinimalDesignsCss(designsDir) {
64
64
  const vars = {};
65
65
  const tokenDir = path.join(designsDir, 'tokens');
66
66
  if (fs.existsSync(tokenDir)) {
67
- walkJson(tokenDir, (obj, prefix) => flattenTokens(obj, prefix, vars));
67
+ walkJson(tokenDir, (obj, prefix) => {
68
+ collectStyleThemeEntries(obj, vars);
69
+ flattenTokens(obj, prefix, vars);
70
+ });
68
71
  }
69
72
  const themeJson = path.join(designsDir, 'theme.json');
70
73
  if (fs.existsSync(themeJson)) {
71
74
  try {
72
- flattenTokens(JSON.parse(fs.readFileSync(themeJson, 'utf8')), '', vars);
75
+ const theme = JSON.parse(fs.readFileSync(themeJson, 'utf8'));
76
+ collectStyleThemeEntries(theme, vars);
77
+ flattenTokens(theme, '', vars);
73
78
  }
74
79
  catch {
75
80
  /* ignore */
@@ -89,10 +94,33 @@ function cssVar(key) {
89
94
  .replace(/^-|-$/g, '');
90
95
  return `--${name}`;
91
96
  }
97
+ /**
98
+ * Style Theme entries (`key.path` + `value`) → `vmz-*` CSS vars (same naming as application compile).
99
+ * @param {unknown} obj
100
+ * @param {Record<string, string>} out
101
+ */
102
+ function collectStyleThemeEntries(obj, out) {
103
+ if (obj == null || typeof obj !== 'object' || Array.isArray(obj))
104
+ return;
105
+ const entries = /** @type {{ key?: { path?: unknown }, value?: unknown }[]} */ ( /** @type {{ entries?: unknown }} */(obj).entries);
106
+ if (!Array.isArray(entries))
107
+ return;
108
+ for (const e of entries) {
109
+ const pathParts = e?.key?.path;
110
+ if (!Array.isArray(pathParts) || pathParts.length === 0)
111
+ continue;
112
+ if (typeof e.value !== 'string' && typeof e.value !== 'number')
113
+ continue;
114
+ const dotted = pathParts.map(String).join('-');
115
+ out[`vmz-${dotted}`] = String(e.value);
116
+ }
117
+ }
92
118
  function flattenTokens(obj, prefix, out) {
93
119
  if (obj == null || typeof obj !== 'object' || Array.isArray(obj))
94
120
  return;
95
121
  for (const [k, v] of Object.entries(obj)) {
122
+ if (k === 'entries')
123
+ continue;
96
124
  const key = prefix ? `${prefix}-${k}` : k;
97
125
  if (v != null && typeof v === 'object' && !Array.isArray(v)) {
98
126
  if ('value' in v && (typeof v.value === 'string' || typeof v.value === 'number')) {
@@ -1,7 +1,6 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Document D1 — enrich manifest with routes, anchors, nav; diagnose links.
4
- * Design: 规划设计/vmz/19 §5–6
3
+ * Document — enrich manifest with routes, anchors, nav; diagnose links.
5
4
  */
6
5
  import fs from 'node:fs';
7
6
  import path from 'node:path';
@@ -115,7 +114,7 @@ export function enrichDocumentContent(manifest, ctx) {
115
114
  if (!href || href.startsWith('mailto:') || href.startsWith('http://') || href.startsWith('https://')) {
116
115
  continue;
117
116
  }
118
- // D2 API refs are resolved by evidence, not as page links.
117
+ // API refs are resolved by evidence, not as page links.
119
118
  if (href.startsWith('vmz-api:') || href.startsWith('api:')) {
120
119
  continue;
121
120
  }
@@ -30,10 +30,10 @@ export declare function resolveApiSymbol(index: any, query: any): {
30
30
  /**
31
31
  * @param {import('./document-schema.js').DocumentManifest} manifest
32
32
  * @param {{
33
- * analyzeMarkdown: Function,
34
- * projectRoot: string,
35
- * createWorkspace?: Function,
36
- * ensureProgramGraph?: boolean,
33
+ * analyzeMarkdown: Function,
34
+ * projectRoot: string,
35
+ * createWorkspace?: Function,
36
+ * ensureProgramGraph?: boolean,
37
37
  * }} ctx
38
38
  */
39
39
  export declare function enrichDocumentEvidence(manifest: any, ctx: any): Promise<{
@@ -1,7 +1,6 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Document D2 Evidence — fence check + API refs from Program Graph.
4
- * Design: 规划设计/vmz/19 §4 · §8 D2
3
+ * Document Evidence — fence check + API refs from Program Graph.
5
4
  *
6
5
  * Not a Doc IR: filesystem/manifest projection + Workspace/Program Graph queries.
7
6
  */
@@ -117,10 +116,10 @@ export function resolveApiSymbol(index, query) {
117
116
  /**
118
117
  * @param {import('./document-schema.js').DocumentManifest} manifest
119
118
  * @param {{
120
- * analyzeMarkdown: Function,
121
- * projectRoot: string,
122
- * createWorkspace?: Function,
123
- * ensureProgramGraph?: boolean,
119
+ * analyzeMarkdown: Function,
120
+ * projectRoot: string,
121
+ * createWorkspace?: Function,
122
+ * ensureProgramGraph?: boolean,
124
123
  * }} ctx
125
124
  */
126
125
  export async function enrichDocumentEvidence(manifest, ctx) {
@@ -424,22 +423,31 @@ function checkScriptFence({ fence, meta, sourcePath, page }) {
424
423
  diagnostics.push({
425
424
  code: DIAG.FENCE_CHECK,
426
425
  severity: 'error',
427
- message: `${meta.lang} fence parse produced empty SourceFile`,
426
+ message: `${meta.lang} fence parse produced no SourceFile`,
428
427
  path: `${sourcePath}:${fence.lineStart}`,
429
428
  });
430
- return { record: { status: 'failed' }, diagnostics };
429
+ return { record: { status: 'failed', detail: 'parse' }, diagnostics };
431
430
  }
432
- return { record: { status: 'ok', detail: 'syntax' }, diagnostics };
431
+ return { record: { status: 'ok' }, diagnostics };
433
432
  }
434
433
  catch (e) {
434
+ const msg = e instanceof Error ? e.message : String(e);
435
+ if (/Cannot find module ['\"]typescript['\"]/i.test(msg) || e?.code === 'MODULE_NOT_FOUND') {
436
+ diagnostics.push({
437
+ code: DIAG.FENCE_CHECK,
438
+ severity: 'error',
439
+ message: 'TS/JS fence check needs `typescript` (optional peer of `@vmz/vmz`). Install: pnpm add -D typescript',
440
+ path: `${sourcePath}:${fence.lineStart}`,
441
+ });
442
+ return { record: { status: 'failed', detail: 'missing-typescript' }, diagnostics };
443
+ }
435
444
  diagnostics.push({
436
445
  code: DIAG.FENCE_CHECK,
437
446
  severity: 'error',
438
- message: `${meta.lang} fence check unavailable: ${e.message || e}`,
447
+ message: `${meta.lang} fence check failed: ${msg}`,
439
448
  path: `${sourcePath}:${fence.lineStart}`,
440
- pageKey: page?.identity?.pageKey,
441
449
  });
442
- return { record: { status: 'failed', detail: 'engine' }, diagnostics };
450
+ return { record: { status: 'failed', detail: 'exception' }, diagnostics };
443
451
  }
444
452
  }
445
453
  function walkFiles(dir, fn) {
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Integrated DocumentMount — build /documents into the host app dist so
3
3
  * routeBase (e.g. /d) is served as static HTML next to SSR pages.
4
- * Design: 规划设计/vmz/19 · Integrated DocumentMount (same project, no separate package).
5
4
  */
6
5
  /**
7
6
  * @param {string} projectRoot
@@ -2,7 +2,6 @@
2
2
  /**
3
3
  * Integrated DocumentMount — build /documents into the host app dist so
4
4
  * routeBase (e.g. /d) is served as static HTML next to SSR pages.
5
- * Design: 规划设计/vmz/19 · Integrated DocumentMount (same project, no separate package).
6
5
  */
7
6
  import fs from 'node:fs';
8
7
  import path from 'node:path';
@@ -5,10 +5,10 @@
5
5
  export declare function htmlToSearchText(html: any): string;
6
6
  /**
7
7
  * @param {{
8
- * manifest: any,
9
- * enriched: { byId: Map<string, any> },
10
- * evidence: any,
11
- * version?: string | null,
8
+ * manifest: any,
9
+ * enriched: { byId: Map<string, any> },
10
+ * evidence: any,
11
+ * version?: string | null,
12
12
  * }} opts
13
13
  */
14
14
  export declare function buildDocumentSearch(opts: any): {
@@ -20,9 +20,9 @@ export declare function buildDocumentSearch(opts: any): {
20
20
  /**
21
21
  * Island-only resume plan for document surfaces.
22
22
  * @param {{
23
- * evidence: any,
24
- * searchHref?: string,
25
- * fenceBodies?: Map<string, string>,
23
+ * evidence: any,
24
+ * searchHref?: string,
25
+ * fenceBodies?: Map<string, string>,
26
26
  * }} opts
27
27
  */
28
28
  export declare function buildDocumentIslands(opts: any): {
@@ -57,10 +57,10 @@ export declare function collectFenceBodies(analyzedByPageId: any, pages: any): M
57
57
  /**
58
58
  * Render SSR island shells (no script — resume later).
59
59
  * @param {{
60
- * islands: any,
61
- * searchIndexHref: string,
62
- * pageKey: string,
63
- * locale: string,
60
+ * islands: any,
61
+ * searchIndexHref: string,
62
+ * pageKey: string,
63
+ * locale: string,
64
64
  * }} opts
65
65
  */
66
66
  export declare function renderIslandShellsHtml(opts: any): {
@@ -1,7 +1,6 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Document D3 Interactive — search index + Island-only resume plan.
4
- * Design: 规划设计/vmz/19 §5–6 · §8 D3
3
+ * Document Interactive — search index + Island-only resume plan.
5
4
  *
6
5
  * Not a Doc IR: build artifacts + ResumeEntry-shaped island hosts.
7
6
  * Static HTML stays no-JS readable; islands resume later, never full-page hydrate.
@@ -27,10 +26,10 @@ export function htmlToSearchText(html) {
27
26
  }
28
27
  /**
29
28
  * @param {{
30
- * manifest: any,
31
- * enriched: { byId: Map<string, any> },
32
- * evidence: any,
33
- * version?: string | null,
29
+ * manifest: any,
30
+ * enriched: { byId: Map<string, any> },
31
+ * evidence: any,
32
+ * version?: string | null,
34
33
  * }} opts
35
34
  */
36
35
  export function buildDocumentSearch(opts) {
@@ -114,9 +113,9 @@ export function buildDocumentSearch(opts) {
114
113
  /**
115
114
  * Island-only resume plan for document surfaces.
116
115
  * @param {{
117
- * evidence: any,
118
- * searchHref?: string,
119
- * fenceBodies?: Map<string, string>,
116
+ * evidence: any,
117
+ * searchHref?: string,
118
+ * fenceBodies?: Map<string, string>,
120
119
  * }} opts
121
120
  */
122
121
  export function buildDocumentIslands(opts) {
@@ -216,10 +215,10 @@ export function collectFenceBodies(analyzedByPageId, pages) {
216
215
  /**
217
216
  * Render SSR island shells (no script — resume later).
218
217
  * @param {{
219
- * islands: any,
220
- * searchIndexHref: string,
221
- * pageKey: string,
222
- * locale: string,
218
+ * islands: any,
219
+ * searchIndexHref: string,
220
+ * pageKey: string,
221
+ * locale: string,
223
222
  * }} opts
224
223
  */
225
224
  export function renderIslandShellsHtml(opts) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Locale key validation & canonicalization (D0).
2
+ * Locale key validation & canonicalization .
3
3
  */
4
4
  /**
5
5
  * Soft-normalize for alias / conflict detection (does not validate).
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Locale key validation & canonicalization (D0).
3
+ * Locale key validation & canonicalization .
4
4
  */
5
5
  import { LOCALE_ALIASES } from './document-schema.js';
6
6
  /** Literal must be lowercase ASCII BCP 47-ish with `-` separators only. */
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Document D1 — resolve engines.markdown via the official plugin runtime.
3
- * Design: 规划设计/vmz/19 §3 · 23 §0(runtime.ts;与 plugin-host 同一 importMaybeTs).
2
+ * Document — resolve engines.markdown via the official plugin runtime.
4
3
  */
5
4
  /**
6
5
  * @param {{ engines?: { markdown?: string } }} [opts]
@@ -1,9 +1,9 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Document D1 — resolve engines.markdown via the official plugin runtime.
4
- * Design: 规划设计/vmz/19 §3 · 23 §0(runtime.ts;与 plugin-host 同一 importMaybeTs).
3
+ * Document — resolve engines.markdown via the official plugin runtime.
5
4
  */
6
5
  import { createRequire } from 'node:module';
6
+ import { existsSync } from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { importMaybeTs } from './plugin-host.js';
@@ -14,17 +14,23 @@ const require = createRequire(import.meta.url);
14
14
  export async function resolveMarkdownEngine(opts = {}) {
15
15
  const id = opts.engines?.markdown || 'markdown-it';
16
16
  if (id !== 'markdown-it') {
17
- throw new Error(`unsupported engines.markdown ${JSON.stringify(id)} (D1: markdown-it only)`);
17
+ throw new Error(`unsupported engines.markdown ${JSON.stringify(id)} (markdown-it only)`);
18
18
  }
19
- let runtimeFile;
19
+ let runtimeFile = null;
20
20
  try {
21
21
  const pkg = require.resolve('@vmz/plugin-markdown-it/package.json');
22
22
  runtimeFile = path.join(path.dirname(pkg), 'runtime.ts');
23
23
  }
24
- catch (e) {
25
- // Workspace fallback when package exports omit package.json.
24
+ catch {
25
+ // Developer mode: monorepo source checkout.
26
26
  const here = path.dirname(fileURLToPath(import.meta.url));
27
- runtimeFile = path.resolve(here, '../../../plugins/vmz-plugin-markdown-it/runtime.ts');
27
+ const fallback = path.resolve(here, '../../../plugins/vmz-plugin-markdown-it/runtime.ts');
28
+ if (existsSync(fallback))
29
+ runtimeFile = fallback;
30
+ }
31
+ if (!runtimeFile || !existsSync(runtimeFile)) {
32
+ throw new Error('markdown engine needs `@vmz/plugin-markdown-it` (optional peer of `@vmz/vmz`).\n' +
33
+ ' Install: pnpm add -D @vmz/plugin-markdown-it');
28
34
  }
29
35
  const mod = await importMaybeTs(runtimeFile);
30
36
  if (typeof mod.analyzeMarkdown !== 'function') {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Scan /documents tree → pages + locale dirs (D0).
2
+ * Scan /documents tree → pages + locale dirs .
3
3
  */
4
4
  /**
5
5
  * PageKey from locale-relative markdown path (strip extension; index → parent or "index").
@@ -9,9 +9,9 @@ export declare function pageKeyFromRel(relMd: any): any;
9
9
  /**
10
10
  * @param {string} documentsRoot absolute path to .../documents
11
11
  * @returns {{
12
- * locales: string[],
13
- * pages: Array<{ identity: { pageKey: string, locale: string }, sourcePath: string }>,
14
- * diagnostics: Array<{ code: string, severity: string, message: string, path?: string }>,
12
+ * locales: string[],
13
+ * pages: Array<{ identity: { pageKey: string, locale: string }, sourcePath: string }>,
14
+ * diagnostics: Array<{ code: string, severity: string, message: string, path?: string }>,
15
15
  * }}
16
16
  */
17
17
  export declare function scanDocumentsTree(documentsRoot: any): {
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Scan /documents tree → pages + locale dirs (D0).
3
+ * Scan /documents tree → pages + locale dirs .
4
4
  */
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
@@ -50,9 +50,9 @@ export function pageKeyFromRel(relMd) {
50
50
  /**
51
51
  * @param {string} documentsRoot absolute path to .../documents
52
52
  * @returns {{
53
- * locales: string[],
54
- * pages: Array<{ identity: { pageKey: string, locale: string }, sourcePath: string }>,
55
- * diagnostics: Array<{ code: string, severity: string, message: string, path?: string }>,
53
+ * locales: string[],
54
+ * pages: Array<{ identity: { pageKey: string, locale: string }, sourcePath: string }>,
55
+ * diagnostics: Array<{ code: string, severity: string, message: string, path?: string }>,
56
56
  * }}
57
57
  */
58
58
  export function scanDocumentsTree(documentsRoot) {