@vmz/core 0.1.20 → 0.1.21

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.
@@ -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);
@@ -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.21",
4
4
  "type": "module",
5
5
  "description": "VMZ production runtime core — DOM / SSR / HTTP / WriteBarrier (no compiler)",
6
6
  "exports": {
@@ -58,12 +58,12 @@
58
58
  "linkedom": "^0.18.13"
59
59
  },
60
60
  "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"
61
+ "@vmz/vmz-win32-x64": "0.1.21",
62
+ "@vmz/vmz-win32-arm64": "0.1.21",
63
+ "@vmz/vmz-darwin-x64": "0.1.21",
64
+ "@vmz/vmz-darwin-arm64": "0.1.21",
65
+ "@vmz/vmz-linux-x64": "0.1.21",
66
+ "@vmz/vmz-linux-arm64": "0.1.21"
67
67
  },
68
68
  "publishConfig": {
69
69
  "access": "public"