@vmz/core 0.1.20 → 0.1.22

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.
@@ -55,7 +55,7 @@ export declare function loadComponentEntries(distDir: any, opts?: {}): Promise<a
55
55
  */
56
56
  export declare function importAndRegisterComponentEntries(distDir: any, entries: any, registerComponents: any, opts?: {}): Promise<{}>;
57
57
  /**
58
- * Bootstrap component registry from deployment (all, closure, or directory fallback).
58
+ * Bootstrap component registry from deployment (all or closure — no directory fallback).
59
59
  * @param {string} distDir
60
60
  * @param {(map: Record<string, unknown>) => void} registerComponents
61
61
  * @param {{
@@ -4,7 +4,6 @@
4
4
  * Parse/validate and graph queries delegate to Rust vmz-artifacts via N-API.
5
5
  */
6
6
  import fs from 'node:fs';
7
- import { readdir } from 'node:fs/promises';
8
7
  import path from 'node:path';
9
8
  import { pathToFileURL } from 'node:url';
10
9
  import { requireNativeFn } from './native-addon.js';
@@ -131,35 +130,13 @@ export async function loadComponentEntries(distDir, opts = {}) {
131
130
  entries = entries.filter((e) => closure.has(e.chunkId));
132
131
  }
133
132
  }
134
- else if (!strict) {
135
- entries = await listComponentEntriesFromDirectory(distDir);
133
+ else if (strict) {
134
+ throw new Error(`vmz: missing vmz-deployment.json under ${distDir} (plan-only host)`);
136
135
  }
136
+ // No directory scan fallback — component closure comes from Deployment Plan only.
137
137
  entries = mergeExplicitComponentEntries(entries, opts.explicit);
138
138
  return dedupeComponentEntriesByTag(entries, { strict });
139
139
  }
140
- /**
141
- * @param {string} distDir
142
- * @returns {Promise<Array<{ chunkId: string, name: string, entry: string }>>}
143
- */
144
- async function listComponentEntriesFromDirectory(distDir) {
145
- const folder = path.join(distDir, 'components');
146
- /** @type {string[]} */
147
- let files = [];
148
- try {
149
- files = await readdir(folder);
150
- }
151
- catch {
152
- return [];
153
- }
154
- return files
155
- .filter((name) => name.endsWith('.client.js'))
156
- .map((f) => {
157
- const name = f.replace(/\.client\.js$/, '');
158
- const chunkId = `components/${name}`;
159
- return { chunkId, name, entry: `components/${name}.client.js` };
160
- })
161
- .sort((a, b) => a.name.localeCompare(b.name));
162
- }
163
140
  /**
164
141
  * @param {string} distDir
165
142
  * @param {Array<{ chunkId: string, name: string, entry: string }>} entries
@@ -192,7 +169,7 @@ export async function importAndRegisterComponentEntries(distDir, entries, regist
192
169
  return map;
193
170
  }
194
171
  /**
195
- * Bootstrap component registry from deployment (all, closure, or directory fallback).
172
+ * Bootstrap component registry from deployment (all or closure — no directory fallback).
196
173
  * @param {string} distDir
197
174
  * @param {(map: Record<string, unknown>) => void} registerComponents
198
175
  * @param {{
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Direct host box model (`ui-direct-host-box`).
3
+ * Chip hosts opt into `display: contents`; block surfaces keep a real layout box.
4
+ */
5
+ /** @type {ReadonlySet<string>} */
6
+ export declare const INLINE_HOST_CONTENTS: Set<string>;
7
+ /**
8
+ * @param {string} name
9
+ * @param {{ __vmzHostBox?: string } | null | undefined} Ctor
10
+ * @returns {'contents' | 'block'}
11
+ */
12
+ export declare function resolveDirectHostBox(name: any, Ctor: any): any;
13
+ /**
14
+ * Apply host box to a live DOM element (create / hydrate / resume).
15
+ * @param {HTMLElement} host
16
+ * @param {string} name
17
+ * @param {{ __vmzHostBox?: string } | null | undefined} Ctor
18
+ */
19
+ export declare function applyDirectHostBox(host: any, name: any, Ctor: any): void;
20
+ /**
21
+ * SSR attrs fragment for host box (only when contents).
22
+ * @param {string} name
23
+ * @param {{ __vmzHostBox?: string } | null | undefined} Ctor
24
+ * @returns {string | null}
25
+ */
26
+ export declare function directHostBoxStyleAttr(name: any, Ctor: any): string;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Direct host box model (`ui-direct-host-box`).
3
+ * Chip hosts opt into `display: contents`; block surfaces keep a real layout box.
4
+ */
5
+ /** @type {ReadonlySet<string>} */
6
+ export const INLINE_HOST_CONTENTS = new Set(['Button', 'Badge', 'Link', 'Tag', 'Icon']);
7
+ /**
8
+ * @param {string} name
9
+ * @param {{ __vmzHostBox?: string } | null | undefined} Ctor
10
+ * @returns {'contents' | 'block'}
11
+ */
12
+ export function resolveDirectHostBox(name, Ctor) {
13
+ const explicit = Ctor && typeof Ctor.__vmzHostBox === 'string' ? Ctor.__vmzHostBox : null;
14
+ if (explicit === 'contents' || explicit === 'block')
15
+ return explicit;
16
+ if (INLINE_HOST_CONTENTS.has(String(name || '')))
17
+ return 'contents';
18
+ return 'block';
19
+ }
20
+ /**
21
+ * Apply host box to a live DOM element (create / hydrate / resume).
22
+ * @param {HTMLElement} host
23
+ * @param {string} name
24
+ * @param {{ __vmzHostBox?: string } | null | undefined} Ctor
25
+ */
26
+ export function applyDirectHostBox(host, name, Ctor) {
27
+ if (!host || !host.style)
28
+ return;
29
+ if (resolveDirectHostBox(name, Ctor) === 'contents') {
30
+ host.style.display = 'contents';
31
+ }
32
+ }
33
+ /**
34
+ * SSR attrs fragment for host box (only when contents).
35
+ * @param {string} name
36
+ * @param {{ __vmzHostBox?: string } | null | undefined} Ctor
37
+ * @returns {string | null}
38
+ */
39
+ export function directHostBoxStyleAttr(name, Ctor) {
40
+ return resolveDirectHostBox(name, Ctor) === 'contents' ? 'display:contents' : null;
41
+ }
@@ -6,6 +6,8 @@
6
6
  * Mount and client patches run that same schedule (SSR/hydrate/resume in dom-ssr).
7
7
  * Field writes only run registered dep patches — never re-create structure.
8
8
  */
9
+ export { applyDirectHostBox, INLINE_HOST_CONTENTS, resolveDirectHostBox } from './direct-host-box.js';
10
+ export { createUnknownComponentElement, markUnknownComponentHost, serializeUnknownComponentNode, UNKNOWN_COMPONENT_ERROR, } from './unknown-component.js';
9
11
  /** @param {boolean} [on] */
10
12
  export declare function __vmzPrecisionEnable(on?: boolean): void;
11
13
  /** @param {boolean} [on] */
package/dist/dom-core.js CHANGED
@@ -7,14 +7,12 @@
7
7
  * Mount and client patches run that same schedule (SSR/hydrate/resume in dom-ssr).
8
8
  * Field writes only run registered dep patches — never re-create structure.
9
9
  */
10
+ import { applyDirectHostBox } from './direct-host-box.js';
11
+ import { createUnknownComponentElement } from './unknown-component.js';
12
+ export { applyDirectHostBox, INLINE_HOST_CONTENTS, resolveDirectHostBox } from './direct-host-box.js';
13
+ export { createUnknownComponentElement, markUnknownComponentHost, serializeUnknownComponentNode, UNKNOWN_COMPONENT_ERROR, } from './unknown-component.js';
10
14
  /** @type {Record<string, new (props?: object) => any>} */
11
15
  const components = Object.create(null);
12
- /**
13
- * Inline chip hosts that opt into `display: contents` (`ui-direct-host-box`).
14
- * Block surfaces (DataTable / shells / overlays) must keep a real layout box.
15
- * @type {Set<string>}
16
- */
17
- const INLINE_HOST_CONTENTS = new Set(['Button', 'Badge', 'Link', 'Tag', 'Icon']);
18
16
  /**
19
17
  * Precision lab counters (test / MCP / benchmarks — not a user API).
20
18
  * Primary keys: BindingId (IR). `*ByDep` is transitional stable-string adapter.
@@ -452,22 +450,23 @@ export const directApi = {
452
450
  // resume: resume on schedule; EventEntry may lazy-load chunk via __vmzLoadComponent.
453
451
  scheduleClientOn(host, String(client), async () => {
454
452
  const Ctor = await resolveComponent(name);
455
- if (!Ctor)
456
- throw new Error(`vmz:dom unknown component <${name} />`);
453
+ if (!Ctor) {
454
+ // Replace placeholder island with leaf error node (do not throw page).
455
+ const err = createUnknownComponentElement(name, 'island');
456
+ host.replaceWith(err);
457
+ return;
458
+ }
457
459
  await resume(Ctor, host, { props: resolved, state: {} });
458
460
  });
459
461
  return host;
460
462
  }
461
463
  const Ctor = components[name];
462
- if (!Ctor)
463
- throw new Error(`vmz:dom unknown component <${name} />`);
464
- // Inline chips opt into no-box host (`ui-direct-host-box`). Block surfaces
465
- // (tables/shells/overlays) keep a real box — default `contents` breaks
466
- // hit-testing / slot hosts (DataTable select timed out in ui-automation).
467
- const hostBox = Ctor.__vmzHostBox;
468
- if (hostBox === 'contents' || (hostBox == null && INLINE_HOST_CONTENTS.has(String(name)))) {
469
- host.style.display = 'contents';
464
+ if (!Ctor) {
465
+ return createUnknownComponentElement(name, 'client');
470
466
  }
467
+ // Inline chips → `display: contents` (`ui-direct-host-box`). Block surfaces
468
+ // keep a real box (DataTable select timed out when defaulting everything).
469
+ applyDirectHostBox(host, name, Ctor);
471
470
  const child = createInstance(Ctor, resolved);
472
471
  if (!(Ctor.__vmzDirect && typeof Ctor.__vmzCreate === 'function')) {
473
472
  throw new Error(`vmz:dom direct component <${name}> requires __vmzCreate (rebuild child with Direct)`);
package/dist/dom-ssr.js CHANGED
@@ -3,7 +3,9 @@
3
3
  * VMZ DOM SSR / hydrate / resume — precise patches, no VDOM diff.
4
4
  * Imports client DOM primitives from ./dom-core.js for tree-shakeable browser entry.
5
5
  */
6
+ import { applyDirectHostBox, directHostBoxStyleAttr } from './direct-host-box.js';
6
7
  import { applyDomAttr, applyPreservedState, BOOLEAN_HTML_ATTRS, createInstance, destroy, directApi, eventPropHandlerName, getRegisteredComponent, hasMeaningfulChild, isEventEntryStrategy, mount, noteDomCreate, resolveComponent, runDirectCreate, scheduleClientOn, settlePendingChildMounts, snapshotInstanceState, stripFns, } from './dom-core.js';
8
+ import { markUnknownComponentHost, serializeUnknownComponentNode } from './unknown-component.js';
7
9
  /** @type {Error | null} last linkedom resolve failure (for clear SSR errors) */
8
10
  let _ssrDocumentLastError = null;
9
11
  /**
@@ -742,8 +744,9 @@ const serializeApi = {
742
744
  },
743
745
  component(hostInst, name, props, client) {
744
746
  const Ctor = getRegisteredComponent(name);
745
- if (!Ctor)
746
- throw new Error(`vmz:dom unknown component <${name} />`);
747
+ if (!Ctor) {
748
+ return serializeUnknownComponentNode(name);
749
+ }
747
750
  /** @type {Record<string, any>} */
748
751
  const resolved = {};
749
752
  for (const [k, v] of Object.entries(props || {})) {
@@ -788,6 +791,9 @@ const serializeApi = {
788
791
  'data-vmz-props': JSON.stringify(stripFns(resolved)),
789
792
  'data-vmz-resume': JSON.stringify(resume),
790
793
  };
794
+ const boxStyle = directHostBoxStyleAttr(name, Ctor);
795
+ if (boxStyle)
796
+ attrs.style = boxStyle;
791
797
  if (isEventEntryStrategy(String(client))) {
792
798
  attrs['data-vmz-entry'] = 'event';
793
799
  }
@@ -810,11 +816,9 @@ const serializeApi = {
810
816
  const node = Ctor.__vmzCreate.call(child, serializeApi);
811
817
  /** @type {Record<string, string>} */
812
818
  const attrs = { 'data-vmz': name };
813
- const hostBox = Ctor.__vmzHostBox;
814
- if (hostBox === 'contents' ||
815
- (hostBox == null && (name === 'Button' || name === 'Badge' || name === 'Link' || name === 'Tag' || name === 'Icon'))) {
816
- attrs.style = 'display:contents';
817
- }
819
+ const boxStyle = directHostBoxStyleAttr(name, Ctor);
820
+ if (boxStyle)
821
+ attrs.style = boxStyle;
818
822
  return {
819
823
  __kind: 'el',
820
824
  tag: 'div',
@@ -892,6 +896,8 @@ export async function resume(Component, container, slice = null) {
892
896
  destroy(container.__vmzInst);
893
897
  container.__vmzInst = null;
894
898
  }
899
+ const hostName = container.getAttribute('data-vmz') || container.getAttribute('data-vmz-island') || Component.name || '';
900
+ applyDirectHostBox(container, hostName, Component);
895
901
  const props = parsed.props || {};
896
902
  const inst = createInstance(Component, props);
897
903
  if (parsed.state)
@@ -935,7 +941,7 @@ export function resumeIslands(root = globalThis.document) {
935
941
  scheduleClientOn(el, strategy, async () => {
936
942
  const Ctor = await resolveComponent(name);
937
943
  if (!Ctor) {
938
- console.error(`vmz:dom resume: unknown component ${name}`);
944
+ markUnknownComponentHost(el, name, 'resume');
939
945
  return;
940
946
  }
941
947
  await resume(Ctor, el);
@@ -961,7 +967,7 @@ export function attachEventEntries(root = globalThis.document) {
961
967
  scheduleClientOn(el, strategy, async () => {
962
968
  const Ctor = await resolveComponent(name);
963
969
  if (!Ctor) {
964
- console.error(`vmz:dom EventEntry: unknown component ${name}`);
970
+ markUnknownComponentHost(el, name, 'event-entry');
965
971
  return;
966
972
  }
967
973
  await resume(Ctor, el);
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Discover compiled client component modules from dist (deployment graph or components/).
3
- * Shared by serve-host SSR, static emit, and test hosts.
2
+ * Discover compiled client component modules from Deployment Plan only.
3
+ * Shared by serve-host SSR, static emit, and test hosts (plan-only host).
4
4
  */
5
5
  export { DEPLOYMENT_SCHEMA, readDeploymentDocument, componentEntriesFromDeployment, collectDependsOnClosure, dedupeComponentEntriesByTag, mergeExplicitComponentEntries, loadComponentEntries, importAndRegisterComponentEntries, bootstrapComponentRegistry, } from './deployment-registry.js';
6
6
  export { createRenderHost } from './render-host.js';
@@ -1,10 +1,8 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * Discover compiled client component modules from dist (deployment graph or components/).
4
- * Shared by serve-host SSR, static emit, and test hosts.
3
+ * Discover compiled client component modules from Deployment Plan only.
4
+ * Shared by serve-host SSR, static emit, and test hosts (plan-only host).
5
5
  */
6
- import fs from 'node:fs';
7
- import { readdir } from 'node:fs/promises';
8
6
  import path from 'node:path';
9
7
  import { pathToFileURL } from 'node:url';
10
8
  import { componentEntriesFromDeployment, dedupeComponentEntriesByTag, mergeExplicitComponentEntries, readDeploymentDocument, } from './deployment-registry.js';
@@ -31,24 +29,9 @@ export async function listClientComponents(dir, opts = {}) {
31
29
  }));
32
30
  }
33
31
  if (strict) {
34
- throw new Error(`vmz: missing vmz-deployment.json under ${dir} (strict deployment mode)`);
32
+ throw new Error(`vmz: missing vmz-deployment.json under ${dir} (plan-only host)`);
35
33
  }
36
- const folder = path.join(dir, 'components');
37
- /** @type {string[]} */
38
- let files = [];
39
- try {
40
- files = await readdir(folder);
41
- }
42
- catch {
43
- return [];
44
- }
45
- return files
46
- .filter((name) => name.endsWith('.client.js'))
47
- .map((f) => {
48
- const name = f.replace(/\.client\.js$/, '');
49
- return { name, entry: `components/${name}.client.js`, chunkId: `components/${name}` };
50
- })
51
- .sort((a, b) => a.name.localeCompare(b.name));
34
+ return [];
52
35
  }
53
36
  /**
54
37
  * Sync variant for callers that already use fs sync (legacy static-emit helpers).
@@ -72,24 +55,9 @@ export function listClientComponentsSync(dir, opts = {}) {
72
55
  }));
73
56
  }
74
57
  if (strict) {
75
- throw new Error(`vmz: missing vmz-deployment.json under ${dir} (strict deployment mode)`);
76
- }
77
- const folder = path.join(dir, 'components');
78
- /** @type {string[]} */
79
- let files = [];
80
- try {
81
- files = fs.readdirSync(folder);
82
- }
83
- catch {
84
- return [];
58
+ throw new Error(`vmz: missing vmz-deployment.json under ${dir} (plan-only host)`);
85
59
  }
86
- return files
87
- .filter((name) => name.endsWith('.client.js'))
88
- .map((f) => {
89
- const name = f.replace(/\.client\.js$/, '');
90
- return { name, entry: `components/${name}.client.js`, chunkId: `components/${name}` };
91
- })
92
- .sort((a, b) => a.name.localeCompare(b.name));
60
+ return [];
93
61
  }
94
62
  /**
95
63
  * @param {Array<{ name: string, entry: string, chunkId?: string }>} entries
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Single HTML `<a data-vmz-route>` locale rewrite for serve-host and static-emit.
3
+ * Plan-only hosts must not keep a second copy of this semantic (0.1.22).
4
+ */
5
+ export type LocaleHrefArtifact = {
6
+ locales?: Array<{
7
+ id: string;
8
+ } | string>;
9
+ defaultLocale?: string;
10
+ routing?: {
11
+ strategy?: string;
12
+ defaultPrefix?: string;
13
+ defaultLocale?: string;
14
+ };
15
+ };
16
+ /** Rewrite a same-app href to the given LocaleId via route realization. */
17
+ export declare function localizeSameAppHref(href: string, localeId: string, artifact: LocaleHrefArtifact): string;
18
+ /** Rewrite `<a data-vmz-route href>` in HTML body to retain `localeId`. */
19
+ export declare function localizeBodyLinks(html: string, localeId: string, artifact: LocaleHrefArtifact, escapeAttr?: (s: string) => string): string;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Single HTML `<a data-vmz-route>` locale rewrite for serve-host and static-emit.
3
+ * Plan-only hosts must not keep a second copy of this semantic (0.1.22).
4
+ */
5
+ function normalizePath(pathname) {
6
+ let p = String(pathname || '/');
7
+ if (!p.startsWith('/'))
8
+ p = `/${p}`;
9
+ if (p.length > 1 && p.endsWith('/'))
10
+ p = p.slice(0, -1);
11
+ return p || '/';
12
+ }
13
+ function parseLocaleFromPath(pathname, supportedLocales) {
14
+ const parts = normalizePath(pathname).split('/').filter(Boolean);
15
+ if (!parts.length)
16
+ return { localeId: null, restPath: '/' };
17
+ if (supportedLocales.includes(parts[0])) {
18
+ const rest = parts.slice(1);
19
+ return { localeId: parts[0], restPath: rest.length ? `/${rest.join('/')}` : '/' };
20
+ }
21
+ return { localeId: null, restPath: normalizePath(pathname) };
22
+ }
23
+ function realizeRoutePath(localeId, pathPattern, routing) {
24
+ const strategy = routing.strategy || 'prefix';
25
+ const defaultPrefix = routing.defaultPrefix || 'include';
26
+ const defaultLocale = routing.defaultLocale;
27
+ const base = normalizePath(pathPattern);
28
+ if (strategy === 'none' || strategy === 'domain') {
29
+ return { path: base };
30
+ }
31
+ const omitDefault = defaultPrefix === 'omit' && localeId === defaultLocale;
32
+ if (omitDefault) {
33
+ return { path: base };
34
+ }
35
+ const pathOut = base === '/' ? `/${localeId}` : `/${localeId}${base}`;
36
+ return { path: pathOut };
37
+ }
38
+ /** Rewrite a same-app href to the given LocaleId via route realization. */
39
+ export function localizeSameAppHref(href, localeId, artifact) {
40
+ if (!href || !localeId || !artifact)
41
+ return href;
42
+ if (href.startsWith('#') || /^(mailto|tel|javascript):/i.test(href))
43
+ return href;
44
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href) && !href.startsWith('/'))
45
+ return href;
46
+ let pathname = String(href);
47
+ let search = '';
48
+ let hash = '';
49
+ const hashIdx = pathname.indexOf('#');
50
+ if (hashIdx >= 0) {
51
+ hash = pathname.slice(hashIdx);
52
+ pathname = pathname.slice(0, hashIdx);
53
+ }
54
+ const qIdx = pathname.indexOf('?');
55
+ if (qIdx >= 0) {
56
+ search = pathname.slice(qIdx);
57
+ pathname = pathname.slice(0, qIdx);
58
+ }
59
+ if (!pathname)
60
+ pathname = '/';
61
+ const supported = (artifact.locales || []).map((l) => (typeof l === 'string' ? l : l.id)).filter(Boolean);
62
+ const defaultLocale = artifact.defaultLocale || artifact.routing?.defaultLocale;
63
+ const routing = {
64
+ strategy: artifact.routing?.strategy || 'prefix',
65
+ defaultPrefix: artifact.routing?.defaultPrefix || 'include',
66
+ defaultLocale,
67
+ };
68
+ const parsed = parseLocaleFromPath(pathname, supported);
69
+ const rest = parsed.restPath || '/';
70
+ const realized = realizeRoutePath(localeId, rest, routing);
71
+ return `${realized.path}${search}${hash}`;
72
+ }
73
+ /** Rewrite `<a data-vmz-route href>` in HTML body to retain `localeId`. */
74
+ export function localizeBodyLinks(html, localeId, artifact, escapeAttr = (s) => String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;')) {
75
+ if (!html || !localeId || !artifact)
76
+ return html;
77
+ return String(html).replace(/<a\b([^>]*)>/gi, (full, attrs) => {
78
+ if (!/\bdata-vmz-route\s*=/.test(attrs))
79
+ return full;
80
+ const hm = attrs.match(/\bhref\s*=\s*"([^"]*)"/i);
81
+ if (!hm)
82
+ return full;
83
+ const next = localizeSameAppHref(hm[1], localeId, artifact);
84
+ if (next === hm[1])
85
+ return full;
86
+ const newAttrs = attrs.replace(/\bhref\s*=\s*"[^"]*"/i, `href="${escapeAttr(next)}"`);
87
+ return `<a${newAttrs}>`;
88
+ });
89
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * PositionContext backed by compiler OffsetIndex via N-API (`offsetIndexLineCol`).
3
+ * `@vmz/diagnostic` stays free of a native dependency — hosts wire this helper.
4
+ */
5
+ export type LineCol = {
6
+ line: number;
7
+ column: number;
8
+ };
9
+ export type PositionContext = {
10
+ lineCol(offset: number): LineCol;
11
+ };
12
+ /**
13
+ * Build a diagnostic PositionContext for `sourceText` using the native OffsetIndex.
14
+ */
15
+ export declare function createPositionContext(sourceText: string): PositionContext;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * PositionContext backed by compiler OffsetIndex via N-API (`offsetIndexLineCol`).
3
+ * `@vmz/diagnostic` stays free of a native dependency — hosts wire this helper.
4
+ */
5
+ import { requireNativeFn } from './native-addon.js';
6
+ /**
7
+ * Build a diagnostic PositionContext for `sourceText` using the native OffsetIndex.
8
+ */
9
+ export function createPositionContext(sourceText) {
10
+ const source = String(sourceText ?? '');
11
+ const lineColFn = requireNativeFn('offsetIndexLineCol');
12
+ return {
13
+ lineCol(offset) {
14
+ const off = Math.max(0, Number(offset) || 0);
15
+ const row = lineColFn(source, off >>> 0);
16
+ return { line: row.line, column: row.column };
17
+ },
18
+ };
19
+ }
@@ -1,18 +1,18 @@
1
1
  /**
2
- * File-route layout chain: Application shell (outermost) + nested page Layout components.
2
+ * Layout chain from Deployment Plan only (plan-only host).
3
3
  */
4
4
  /** Chunk id for `src/Application.vmz` emit (`Application.client.js`). */
5
5
  export declare const APPLICATION_SHELL_CHUNK = "Application";
6
6
  /**
7
- * True when the compile output includes a root Application shell.
7
+ * True when Deployment Plan lists an Application shell unit.
8
8
  */
9
9
  export declare function hasApplicationShell(distDir: string): boolean;
10
10
  /**
11
- * Nearest page Layout.client.js walking up from the page chunk (outer to inner).
12
- * Does not include Application — use resolveRouteLayoutChain.
11
+ * Nested layout ids from Plan for a page (excludes Application).
12
+ * Prefer `resolveRouteLayoutChain` for SSR / hydrate.
13
13
  */
14
14
  export declare function resolveNestedLayoutChain(distDir: string, pageChunkId: string): string[];
15
15
  /**
16
- * Full SSR / hydrate layout chain: optional Application shell, then nested page layouts.
16
+ * Full SSR / hydrate layout chain from Deployment Plan `layoutChain`.
17
17
  */
18
18
  export declare function resolveRouteLayoutChain(distDir: string, pageChunkId: string): string[];
@@ -1,41 +1,36 @@
1
1
  /**
2
- * File-route layout chain: Application shell (outermost) + nested page Layout components.
2
+ * Layout chain from Deployment Plan only (plan-only host).
3
3
  */
4
- import { existsSync } from 'node:fs';
5
- import path from 'node:path';
4
+ import { readDeploymentDocument } from './deployment-registry.js';
6
5
  /** Chunk id for `src/Application.vmz` emit (`Application.client.js`). */
7
6
  export const APPLICATION_SHELL_CHUNK = 'Application';
8
7
  /**
9
- * True when the compile output includes a root Application shell.
8
+ * True when Deployment Plan lists an Application shell unit.
10
9
  */
11
10
  export function hasApplicationShell(distDir) {
12
- return existsSync(path.join(distDir, `${APPLICATION_SHELL_CHUNK}.client.js`));
11
+ const deployment = readDeploymentDocument(distDir, { strict: false });
12
+ const units = Array.isArray(deployment?.units) ? deployment.units : [];
13
+ return units.some((u) => String(u?.chunkId || '') === APPLICATION_SHELL_CHUNK);
13
14
  }
14
15
  /**
15
- * Nearest page Layout.client.js walking up from the page chunk (outer to inner).
16
- * Does not include Application — use resolveRouteLayoutChain.
16
+ * Nested layout ids from Plan for a page (excludes Application).
17
+ * Prefer `resolveRouteLayoutChain` for SSR / hydrate.
17
18
  */
18
19
  export function resolveNestedLayoutChain(distDir, pageChunkId) {
19
- const rel = pageChunkId.replace(/^pages\//, '');
20
- const parts = rel.split('/').filter(Boolean);
21
- parts.pop();
22
- const chain = [];
23
- for (let i = parts.length; i >= 0; i--) {
24
- const dirParts = parts.slice(0, i);
25
- const layoutChunk = ['pages', ...dirParts, 'Layout'].join('/');
26
- if (existsSync(path.join(distDir, `${layoutChunk}.client.js`))) {
27
- chain.unshift(layoutChunk);
28
- }
29
- }
30
- return chain;
20
+ return resolveRouteLayoutChain(distDir, pageChunkId).filter((id) => id !== APPLICATION_SHELL_CHUNK);
31
21
  }
32
22
  /**
33
- * Full SSR / hydrate layout chain: optional Application shell, then nested page layouts.
23
+ * Full SSR / hydrate layout chain from Deployment Plan `layoutChain`.
34
24
  */
35
25
  export function resolveRouteLayoutChain(distDir, pageChunkId) {
36
- const chain = resolveNestedLayoutChain(distDir, pageChunkId);
37
- if (hasApplicationShell(distDir)) {
38
- chain.unshift(APPLICATION_SHELL_CHUNK);
26
+ const deployment = readDeploymentDocument(distDir, { strict: true });
27
+ const units = Array.isArray(deployment?.units) ? deployment.units : [];
28
+ const unit = units.find((u) => String(u?.chunkId || '').replace(/\\/g, '/') === pageChunkId);
29
+ if (!unit) {
30
+ throw new Error(`resolveRouteLayoutChain: no deployment unit for ${pageChunkId} (plan-only host)`);
31
+ }
32
+ if (!Array.isArray(unit.layoutChain)) {
33
+ throw new Error(`resolveRouteLayoutChain: page unit ${pageChunkId} missing layoutChain (plan-only host)`);
39
34
  }
40
- return chain;
35
+ return unit.layoutChain.map((id) => String(id));
41
36
  }
@@ -17,11 +17,12 @@
17
17
  * dist so soft reload does not keep a stale `lib/*.js` ESM cache entry.
18
18
  */
19
19
  import { existsSync, readFileSync } from 'node:fs';
20
- import { readdir, readFile, writeFile } from 'node:fs/promises';
20
+ import { readFile, writeFile } from 'node:fs/promises';
21
21
  import http from 'node:http';
22
22
  import { createRequire, registerHooks } from 'node:module';
23
23
  import path from 'node:path';
24
24
  import { fileURLToPath, pathToFileURL } from 'node:url';
25
+ import { localizeBodyLinks } from './localize-body-links.js';
25
26
  import { createRenderHost } from './render-host.js';
26
27
  import { listClientComponents } from './list-client-components.js';
27
28
  import { loadNativeAddon } from './native-addon.js';
@@ -141,7 +142,7 @@ let reloadToken = Date.now();
141
142
  let ssrRenderHost = null;
142
143
  /** @type {string | null} Correlatable build id from vmz dev (Living §12.8). */
143
144
  let lastDevBuildId = null;
144
- /** @type {Array<{ chunkId: string, pageRel: string, segs: ReturnType<typeof parseChunkSegments> }>} */
145
+ /** @type {Array<{ chunkId: string, pageRel: string, segs: ReturnType<typeof parsePathPattern> }>} */
145
146
  let pageCatalog = [];
146
147
  /** @type {Map<string, any>} */
147
148
  const pageCtors = new Map();
@@ -606,7 +607,7 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
606
607
  }
607
608
  // Locale discipline: same-app Links retain current LocaleId (realization authority).
608
609
  if (localeArtifact && localeId) {
609
- bodyHtml = localizeBodyLinksInHost(bodyHtml, localeId, localeArtifact);
610
+ bodyHtml = localizeBodyLinks(bodyHtml, localeId, localeArtifact);
610
611
  }
611
612
  }
612
613
  finally {
@@ -799,9 +800,7 @@ async function softReload(opts = {}) {
799
800
  cacheBust: nextToken,
800
801
  });
801
802
  const nextCatalog = await listPageClientFiles(distDir);
802
- if (!nextCatalog.length) {
803
- throw new Error(`vmz serve: no pages/**/*.client.js in ${distDir}`);
804
- }
803
+ // empty catalog throws inside listPageClientFiles
805
804
  /** @type {Map<string, any>} */
806
805
  const nextCtors = new Map();
807
806
  if (!islandHmr) {
@@ -1053,79 +1052,6 @@ function resolveLocalePath(pathname, cookieHeader) {
1053
1052
  redirectTo: null,
1054
1053
  };
1055
1054
  }
1056
- /**
1057
- * Realize href for current LocaleId (prefix strategy). Kept local so serve-host
1058
- * stays free of CLI package imports.
1059
- * @param {string} href
1060
- * @param {string} localeId
1061
- * @param {any} artifact
1062
- */
1063
- function localizeSameAppHrefHost(href, localeId, artifact) {
1064
- if (!href || !localeId || !artifact)
1065
- return href;
1066
- if (href.startsWith('#') || /^(mailto|tel|javascript):/i.test(href))
1067
- return href;
1068
- if (/^[a-z][a-z0-9+.-]*:/i.test(href) && !href.startsWith('/'))
1069
- return href;
1070
- let pathname = String(href);
1071
- let search = '';
1072
- let hash = '';
1073
- const hashIdx = pathname.indexOf('#');
1074
- if (hashIdx >= 0) {
1075
- hash = pathname.slice(hashIdx);
1076
- pathname = pathname.slice(0, hashIdx);
1077
- }
1078
- const qIdx = pathname.indexOf('?');
1079
- if (qIdx >= 0) {
1080
- search = pathname.slice(qIdx);
1081
- pathname = pathname.slice(0, qIdx);
1082
- }
1083
- if (!pathname)
1084
- pathname = '/';
1085
- const supported = (artifact.locales || []).map((l) => l.id).filter(Boolean);
1086
- const defaultLocale = artifact.defaultLocale || artifact.routing?.defaultLocale;
1087
- const routing = artifact.routing || {};
1088
- const strategy = routing.strategy || 'prefix';
1089
- const defaultPrefix = routing.defaultPrefix || 'include';
1090
- const parts = pathname.split('/').filter(Boolean);
1091
- let rest = pathname;
1092
- if (parts.length && supported.includes(parts[0])) {
1093
- const r = parts.slice(1);
1094
- rest = r.length ? `/${r.join('/')}` : '/';
1095
- }
1096
- if (rest.length > 1 && rest.endsWith('/'))
1097
- rest = rest.slice(0, -1);
1098
- if (!rest.startsWith('/'))
1099
- rest = `/${rest}`;
1100
- if (strategy === 'none' || strategy === 'domain')
1101
- return `${rest}${search}${hash}`;
1102
- const omitDefault = defaultPrefix === 'omit' && localeId === defaultLocale;
1103
- if (omitDefault)
1104
- return `${rest}${search}${hash}`;
1105
- const pathOut = rest === '/' ? `/${localeId}` : `/${localeId}${rest}`;
1106
- return `${pathOut}${search}${hash}`;
1107
- }
1108
- /**
1109
- * @param {string} html
1110
- * @param {string} localeId
1111
- * @param {any} artifact
1112
- */
1113
- function localizeBodyLinksInHost(html, localeId, artifact) {
1114
- if (!html || !localeId || !artifact)
1115
- return html;
1116
- return String(html).replace(/<a\b([^>]*)>/gi, (full, attrs) => {
1117
- if (!/\bdata-vmz-route\s*=/.test(attrs))
1118
- return full;
1119
- const hm = attrs.match(/\bhref\s*=\s*"([^"]*)"/i);
1120
- if (!hm)
1121
- return full;
1122
- const next = localizeSameAppHrefHost(hm[1], localeId, artifact);
1123
- if (next === hm[1])
1124
- return full;
1125
- const newAttrs = attrs.replace(/\bhref\s*=\s*"[^"]*"/i, `href="${escapeAttr(next)}"`);
1126
- return `<a${newAttrs}>`;
1127
- });
1128
- }
1129
1055
  /**
1130
1056
  * @param {string} chunkId
1131
1057
  * @param {string} localeId
@@ -1152,50 +1078,22 @@ async function loadPageCtor(chunkId) {
1152
1078
  return mod.default;
1153
1079
  }
1154
1080
  /**
1155
- * Discover compiled page modules. Prefer Route Graph `pathPattern` from
1156
- * `vmz-deployment.json`; fall back to walking `pages/**` (file-route only).
1081
+ * Discover compiled page modules from Route Graph `pathPattern` in
1082
+ * `vmz-deployment.json` only (plan-only host — no `pages/**` walk).
1157
1083
  * @param {string} dir
1158
1084
  */
1159
1085
  async function listPageClientFiles(dir) {
1160
1086
  const fromDep = await listPagesFromDeployment(dir);
1161
- if (fromDep.length)
1162
- return fromDep;
1163
- const root = path.join(dir, 'pages');
1164
- /** @type {Array<{ chunkId: string, pageRel: string, segs: ReturnType<typeof parseChunkSegments> }>} */
1165
- const out = [];
1166
- async function walk(abs, relParts) {
1167
- let ents;
1168
- try {
1169
- ents = await readdir(abs, { withFileTypes: true });
1170
- }
1171
- catch {
1172
- return;
1173
- }
1174
- for (const e of ents) {
1175
- if (e.isDirectory()) {
1176
- await walk(path.join(abs, e.name), [...relParts, e.name]);
1177
- }
1178
- else if (e.isFile() && e.name.endsWith('.client.js')) {
1179
- const stem = e.name.replace(/\.client\.js$/, '');
1180
- if (isRouteBoundaryStem(stem))
1181
- continue;
1182
- const chunkId = ['pages', ...relParts, stem].join('/');
1183
- out.push({
1184
- chunkId,
1185
- pageRel: `${chunkId}.client.js`,
1186
- segs: parseChunkSegments(chunkId),
1187
- });
1188
- }
1189
- }
1087
+ if (!fromDep.length) {
1088
+ throw new Error(`vmz serve: no page units with pathPattern in ${path.join(dir, 'vmz-deployment.json')} (plan-only host)`);
1190
1089
  }
1191
- await walk(root, []);
1192
- return out;
1090
+ return fromDep;
1193
1091
  }
1194
1092
  /**
1195
1093
  * @param {string} dir
1196
1094
  */
1197
1095
  async function listPagesFromDeployment(dir) {
1198
- /** @type {Array<{ chunkId: string, pageRel: string, segs: ReturnType<typeof parseChunkSegments> }>} */
1096
+ /** @type {Array<{ chunkId: string, pageRel: string, segs: ReturnType<typeof parsePathPattern> }>} */
1199
1097
  const out = [];
1200
1098
  try {
1201
1099
  const raw = await readFile(path.join(dir, 'vmz-deployment.json'), 'utf8');
@@ -1209,40 +1107,25 @@ async function listPagesFromDeployment(dir) {
1209
1107
  const stem = chunkId.split('/').pop() || '';
1210
1108
  if (isRouteBoundaryStem(stem))
1211
1109
  continue;
1212
- const pageRel = String(unit.clientEntry || `${chunkId}.client.js`).replace(/\\/g, '/');
1213
1110
  const pattern = String(unit.pathPattern || '').trim();
1111
+ if (!pattern) {
1112
+ throw new Error(`vmz serve: page unit ${chunkId} missing pathPattern in vmz-deployment.json (plan-only host)`);
1113
+ }
1114
+ const pageRel = String(unit.clientEntry || `${chunkId}.client.js`).replace(/\\/g, '/');
1214
1115
  out.push({
1215
1116
  chunkId,
1216
1117
  pageRel,
1217
- segs: pattern ? parsePathPattern(pattern) : parseChunkSegments(chunkId),
1118
+ segs: parsePathPattern(pattern),
1218
1119
  });
1219
1120
  }
1220
1121
  }
1221
- catch {
1122
+ catch (err) {
1123
+ if (err && typeof err.message === 'string' && err.message.includes('plan-only host'))
1124
+ throw err;
1222
1125
  return [];
1223
1126
  }
1224
1127
  return out;
1225
1128
  }
1226
- /**
1227
- * File-route segments from chunk id (`pages/Install` → `/install`).
1228
- * Skips URL-invisible `(group)` dirs; boundary stems never reach here.
1229
- * @param {string} chunkId
1230
- */
1231
- function parseChunkSegments(chunkId) {
1232
- const rel = chunkId.replace(/^pages\//, '');
1233
- const parts = rel.split('/').filter(Boolean);
1234
- /** @type {Array<{ kind: 'static' | 'param' | 'catch', value?: string, name?: string }>} */
1235
- const segs = [];
1236
- for (let i = 0; i < parts.length; i++) {
1237
- const p = parts[i];
1238
- if (isRouteGroupDir(p))
1239
- continue;
1240
- if (p === 'index' && i === parts.length - 1)
1241
- continue;
1242
- segs.push(parsePathSegment(p));
1243
- }
1244
- return segs;
1245
- }
1246
1129
  /**
1247
1130
  * Browser HTTP pattern (`/` / `/home` / `/users/:id` / `/blog/[...slug]`).
1248
1131
  * @param {string} pattern
@@ -1306,7 +1189,7 @@ function matchFileRoute(pathname, catalog) {
1306
1189
  return best;
1307
1190
  }
1308
1191
  /**
1309
- * @param {ReturnType<typeof parseChunkSegments>} segs
1192
+ * @param {ReturnType<typeof parsePathPattern>} segs
1310
1193
  * @param {string} pathname
1311
1194
  * @returns {Record<string, string>}
1312
1195
  */
@@ -1335,7 +1218,7 @@ function extractRouteParams(segs, pathname) {
1335
1218
  return params;
1336
1219
  }
1337
1220
  /**
1338
- * @param {ReturnType<typeof parseChunkSegments>} segs
1221
+ * @param {ReturnType<typeof parsePathPattern>} segs
1339
1222
  * @param {string[]} pathParts
1340
1223
  * @returns {number | null}
1341
1224
  */
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Unknown Direct component resilience (`ssr-unknown-component-error-node`).
3
+ * Leaf failure → subtree error node; do not throw out of document SSR.
4
+ */
5
+ /** @type {string} */
6
+ export declare const UNKNOWN_COMPONENT_ERROR = "unknown-component";
7
+ /**
8
+ * Append a leaf diagnostic for tooling (does not replace the error node).
9
+ * @param {string} name
10
+ * @param {string} [via]
11
+ */
12
+ export declare function noteUnknownComponent(name: any, via?: string): void;
13
+ /**
14
+ * SSR serialize-tree error node (keeps document HTTP 200).
15
+ * @param {string} name
16
+ */
17
+ export declare function serializeUnknownComponentNode(name: any): {
18
+ __kind: string;
19
+ tag: string;
20
+ attrs: {
21
+ 'data-vmz-error': string;
22
+ 'data-vmz-component': string;
23
+ };
24
+ children: any[];
25
+ appendChild(c: any): void;
26
+ };
27
+ /**
28
+ * Live DOM error host (client create / schedule).
29
+ * @param {string} name
30
+ * @param {string} [via]
31
+ * @returns {HTMLElement}
32
+ */
33
+ export declare function createUnknownComponentElement(name: any, via?: string): HTMLDivElement;
34
+ /**
35
+ * Mark an existing island host as unknown (resume / EventEntry).
36
+ * @param {HTMLElement} el
37
+ * @param {string} name
38
+ * @param {string} [via]
39
+ */
40
+ export declare function markUnknownComponentHost(el: any, name: any, via?: string): void;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Unknown Direct component resilience (`ssr-unknown-component-error-node`).
3
+ * Leaf failure → subtree error node; do not throw out of document SSR.
4
+ */
5
+ /** @type {string} */
6
+ export const UNKNOWN_COMPONENT_ERROR = 'unknown-component';
7
+ /**
8
+ * @returns {boolean}
9
+ */
10
+ function isDevSurface() {
11
+ try {
12
+ if (typeof process !== 'undefined' && process.env) {
13
+ if (process.env.VMZ_DEV === '1' || process.env.VMZ_DEV === 'true')
14
+ return true;
15
+ if (process.env.NODE_ENV === 'development')
16
+ return true;
17
+ }
18
+ }
19
+ catch {
20
+ /* ignore */
21
+ }
22
+ return false;
23
+ }
24
+ /**
25
+ * Append a leaf diagnostic for tooling (does not replace the error node).
26
+ * @param {string} name
27
+ * @param {string} [via]
28
+ */
29
+ export function noteUnknownComponent(name, via = 'ssr') {
30
+ const detail = { kind: UNKNOWN_COMPONENT_ERROR, component: String(name || ''), via };
31
+ try {
32
+ if (typeof console !== 'undefined' && typeof console.error === 'function') {
33
+ console.error(`vmz:dom unknown component <${name} /> (${via})`);
34
+ }
35
+ }
36
+ catch {
37
+ /* ignore */
38
+ }
39
+ try {
40
+ const g = typeof globalThis !== 'undefined' ? globalThis : null;
41
+ if (!g)
42
+ return;
43
+ if (!Array.isArray(g.__VMZ_COMPONENT_ERRORS__)) {
44
+ g.__VMZ_COMPONENT_ERRORS__ = [];
45
+ }
46
+ /** @type {unknown[]} */
47
+ const bag = g.__VMZ_COMPONENT_ERRORS__;
48
+ bag.push(detail);
49
+ }
50
+ catch {
51
+ /* ignore */
52
+ }
53
+ }
54
+ /**
55
+ * SSR serialize-tree error node (keeps document HTTP 200).
56
+ * @param {string} name
57
+ */
58
+ export function serializeUnknownComponentNode(name) {
59
+ noteUnknownComponent(name, 'ssr');
60
+ /** @type {Record<string, string>} */
61
+ const attrs = {
62
+ 'data-vmz-error': UNKNOWN_COMPONENT_ERROR,
63
+ 'data-vmz-component': String(name || ''),
64
+ };
65
+ /** @type {any[]} */
66
+ const children = [];
67
+ if (isDevSurface()) {
68
+ children.push({
69
+ __kind: 'text',
70
+ value: `Unknown component <${name} />`,
71
+ });
72
+ }
73
+ return {
74
+ __kind: 'el',
75
+ tag: 'div',
76
+ attrs,
77
+ children,
78
+ appendChild(c) {
79
+ if (c != null)
80
+ this.children.push(c);
81
+ },
82
+ };
83
+ }
84
+ /**
85
+ * Live DOM error host (client create / schedule).
86
+ * @param {string} name
87
+ * @param {string} [via]
88
+ * @returns {HTMLElement}
89
+ */
90
+ export function createUnknownComponentElement(name, via = 'client') {
91
+ noteUnknownComponent(name, via);
92
+ const host = document.createElement('div');
93
+ host.setAttribute('data-vmz-error', UNKNOWN_COMPONENT_ERROR);
94
+ host.setAttribute('data-vmz-component', String(name || ''));
95
+ if (isDevSurface()) {
96
+ host.textContent = `Unknown component <${name} />`;
97
+ }
98
+ return host;
99
+ }
100
+ /**
101
+ * Mark an existing island host as unknown (resume / EventEntry).
102
+ * @param {HTMLElement} el
103
+ * @param {string} name
104
+ * @param {string} [via]
105
+ */
106
+ export function markUnknownComponentHost(el, name, via = 'resume') {
107
+ if (!el)
108
+ return;
109
+ noteUnknownComponent(name, via);
110
+ el.setAttribute('data-vmz-error', UNKNOWN_COMPONENT_ERROR);
111
+ el.setAttribute('data-vmz-component', String(name || ''));
112
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vmz/core",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "description": "VMZ production runtime core — DOM / SSR / HTTP / WriteBarrier (no compiler)",
6
6
  "exports": {
@@ -39,6 +39,14 @@
39
39
  "./route-layout-chain": {
40
40
  "types": "./dist/route-layout-chain.d.ts",
41
41
  "default": "./dist/route-layout-chain.js"
42
+ },
43
+ "./localize-body-links": {
44
+ "types": "./dist/localize-body-links.d.ts",
45
+ "default": "./dist/localize-body-links.js"
46
+ },
47
+ "./position-context": {
48
+ "types": "./dist/position-context.d.ts",
49
+ "default": "./dist/position-context.js"
42
50
  }
43
51
  },
44
52
  "files": [
@@ -58,12 +66,12 @@
58
66
  "linkedom": "^0.18.13"
59
67
  },
60
68
  "optionalDependencies": {
61
- "@vmz/vmz-win32-x64": "0.1.20",
62
- "@vmz/vmz-win32-arm64": "0.1.20",
63
- "@vmz/vmz-darwin-x64": "0.1.20",
64
- "@vmz/vmz-darwin-arm64": "0.1.20",
65
- "@vmz/vmz-linux-x64": "0.1.20",
66
- "@vmz/vmz-linux-arm64": "0.1.20"
69
+ "@vmz/vmz-win32-x64": "0.1.22",
70
+ "@vmz/vmz-win32-arm64": "0.1.22",
71
+ "@vmz/vmz-darwin-x64": "0.1.22",
72
+ "@vmz/vmz-darwin-arm64": "0.1.22",
73
+ "@vmz/vmz-linux-x64": "0.1.22",
74
+ "@vmz/vmz-linux-arm64": "0.1.22"
67
75
  },
68
76
  "publishConfig": {
69
77
  "access": "public"