@llui/dom 0.0.19 → 0.0.20

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.
package/README.md CHANGED
@@ -57,13 +57,15 @@ Element helpers (`div`, `button`, `span`, etc.) stay as imports — they're stat
57
57
 
58
58
  ### Core
59
59
 
60
- | Export | Purpose |
61
- | --------------------- | ------------------------------------------------- |
62
- | `component(def)` | Create a component definition |
63
- | `mountApp(el, def)` | Mount a component to a DOM element |
64
- | `hydrateApp(el, def)` | Hydrate server-rendered HTML |
65
- | `flush()` | Synchronously flush all pending updates |
66
- | `createView(send)` | Create a full View bundle (for tests/dynamic use) |
60
+ | Export | Purpose |
61
+ | ------------------------------ | --------------------------------------------------------- |
62
+ | `component(def)` | Create a component definition |
63
+ | `mountApp(el, def)` | Mount a component to a DOM element |
64
+ | `hydrateApp(el, def)` | Hydrate server-rendered HTML |
65
+ | `mountAtAnchor(anchor, def)` | Mount a component relative to a comment anchor |
66
+ | `hydrateAtAnchor(anchor, def)` | Hydrate server-rendered HTML relative to a comment anchor |
67
+ | `flush()` | Synchronously flush all pending updates |
68
+ | `createView(send)` | Create a full View bundle (for tests/dynamic use) |
67
69
 
68
70
  ### View Primitives
69
71
 
package/dist/hmr.d.ts CHANGED
@@ -5,6 +5,7 @@ import type { ComponentDef, AppHandle } from './types.js';
5
5
  */
6
6
  export declare function enableHmr(): void;
7
7
  export declare function registerForHmr(name: string, inst: object, container: HTMLElement): void;
8
+ export declare function registerForAnchor(name: string, inst: object, anchor: Comment, endSentinel: Comment): void;
8
9
  export declare function unregisterForHmr(name: string, inst: object): void;
9
10
  /**
10
11
  * Hot-swap a component definition on all live instances.
package/dist/hmr.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAUzD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAEhC;AAWD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CAIvF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAMjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACtC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC5B,SAAS,GAAG,IAAI,CAwElB"}
1
+ {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAUzD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAQhC;AAmBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CAIvF;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,OAAO,GACnB,IAAI,CAIN;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAMjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACtC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC5B,SAAS,GAAG,IAAI,CAyElB"}
package/dist/hmr.js CHANGED
@@ -10,12 +10,23 @@ import { createView } from './view-helpers.js';
10
10
  * Importing this module registers it with mountApp for hot-swapping.
11
11
  */
12
12
  export function enableHmr() {
13
- _setHmrModule({ enableHmr, registerForHmr, unregisterForHmr, replaceComponent });
13
+ _setHmrModule({
14
+ enableHmr,
15
+ registerForHmr,
16
+ registerForAnchor,
17
+ unregisterForHmr,
18
+ replaceComponent,
19
+ });
14
20
  }
15
21
  const hmrRegistry = new Map();
16
22
  export function registerForHmr(name, inst, container) {
17
23
  const entries = hmrRegistry.get(name) ?? [];
18
- entries.push({ inst: inst, container });
24
+ entries.push({ kind: 'container', inst: inst, container });
25
+ hmrRegistry.set(name, entries);
26
+ }
27
+ export function registerForAnchor(name, inst, anchor, endSentinel) {
28
+ const entries = hmrRegistry.get(name) ?? [];
29
+ entries.push({ kind: 'anchor', inst: inst, anchor, endSentinel });
19
30
  hmrRegistry.set(name, entries);
20
31
  }
21
32
  export function unregisterForHmr(name, inst) {
@@ -43,9 +54,8 @@ export function replaceComponent(name, newDef) {
43
54
  if (!entries || entries.length === 0)
44
55
  return null;
45
56
  let handle = null;
46
- for (const { inst, container } of entries) {
47
- const typedInst = inst;
48
- // Replace functions on the live definition
57
+ for (const entry of entries) {
58
+ const typedInst = entry.inst;
49
59
  typedInst.def = {
50
60
  ...typedInst.def,
51
61
  update: newDef.update,
@@ -55,52 +65,82 @@ export function replaceComponent(name, newDef) {
55
65
  __update: newDef.__update,
56
66
  __handlers: newDef.__handlers,
57
67
  };
58
- // Dispose old scope tree — removes all old DOM nodes and bindings
59
68
  disposeScope(typedInst.rootScope);
60
- container.textContent = '';
61
- // Create fresh scope tree
69
+ // Clear the owned region per-kind.
70
+ if (entry.kind === 'container') {
71
+ entry.container.textContent = '';
72
+ }
73
+ else {
74
+ // anchor kind — wipe siblings between anchor and endSentinel, keep the
75
+ // anchor AND the end sentinel (they bracket the fresh render).
76
+ let sib = entry.anchor.nextSibling;
77
+ while (sib !== null && sib !== entry.endSentinel) {
78
+ const next = sib.nextSibling;
79
+ sib.parentNode.removeChild(sib);
80
+ sib = next;
81
+ }
82
+ }
62
83
  typedInst.rootScope = createScope(null);
63
84
  typedInst.rootScope._kind = 'root';
64
85
  typedInst.allBindings = [];
65
86
  typedInst.structuralBlocks = [];
66
- // Re-run view with current state
67
87
  setFlatBindings(typedInst.allBindings);
68
88
  setRenderContext({
69
89
  rootScope: typedInst.rootScope,
70
90
  state: typedInst.state,
71
91
  allBindings: typedInst.allBindings,
72
92
  structuralBlocks: typedInst.structuralBlocks,
73
- container,
93
+ container: entry.kind === 'container' ? entry.container : (entry.anchor.parentElement ?? undefined),
74
94
  send: typedInst.send,
75
95
  instance: typedInst,
76
96
  });
77
97
  const nodes = typedInst.def.view(createView(typedInst.send));
78
98
  clearRenderContext();
79
99
  setFlatBindings(null);
80
- for (const node of nodes) {
81
- container.appendChild(node);
100
+ if (entry.kind === 'container') {
101
+ for (const node of nodes) {
102
+ entry.container.appendChild(node);
103
+ }
104
+ }
105
+ else {
106
+ for (const node of nodes) {
107
+ entry.anchor.parentNode.insertBefore(node, entry.endSentinel);
108
+ }
82
109
  }
83
- // Return AppHandle for the first instance
84
110
  if (!handle) {
85
- handle = {
86
- dispose() {
87
- unregisterForHmr(name, inst);
88
- inst.abortController.abort();
89
- unregisterInstance(inst);
90
- disposeScope(typedInst.rootScope);
91
- container.textContent = '';
92
- },
93
- flush() {
94
- flushInstance(inst);
95
- },
96
- send(msg) {
97
- ;
98
- typedInst.send(msg);
99
- },
100
- };
111
+ handle = makeReplacementHandle(name, entry, typedInst);
101
112
  }
102
113
  }
103
114
  console.log(`[LLui HMR] ${name} updated — state preserved`);
104
115
  return handle;
105
116
  }
117
+ function makeReplacementHandle(name, entry, typedInst) {
118
+ return {
119
+ dispose() {
120
+ unregisterForHmr(name, entry.inst);
121
+ entry.inst.abortController.abort();
122
+ unregisterInstance(entry.inst);
123
+ disposeScope(typedInst.rootScope);
124
+ if (entry.kind === 'container') {
125
+ entry.container.textContent = '';
126
+ }
127
+ else {
128
+ let sib = entry.anchor.nextSibling;
129
+ while (sib !== null && sib !== entry.endSentinel) {
130
+ const next = sib.nextSibling;
131
+ sib.parentNode.removeChild(sib);
132
+ sib = next;
133
+ }
134
+ entry.endSentinel.parentNode?.removeChild(entry.endSentinel);
135
+ }
136
+ },
137
+ flush() {
138
+ flushInstance(entry.inst);
139
+ },
140
+ send(msg) {
141
+ ;
142
+ typedInst.send(msg);
143
+ },
144
+ };
145
+ }
106
146
  //# sourceMappingURL=hmr.js.map
package/dist/hmr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hmr.js","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentDef, AppHandle } from './types.js'\nimport type { ComponentInstance } from './update-loop.js'\nimport { flushInstance } from './update-loop.js'\nimport { createScope, disposeScope } from './scope.js'\nimport { setRenderContext, clearRenderContext } from './render-context.js'\nimport { setFlatBindings } from './binding.js'\nimport { unregisterInstance } from './runtime.js'\nimport { _setHmrModule } from './mount.js'\nimport { createView } from './view-helpers.js'\n\n/**\n * Enable HMR state preservation. Called by compiler-generated dev code.\n * Importing this module registers it with mountApp for hot-swapping.\n */\nexport function enableHmr(): void {\n _setHmrModule({ enableHmr, registerForHmr, unregisterForHmr, replaceComponent })\n}\n\n// ── HMR Registry ─────────────────────────────────────────────────\n\ninterface HmrEntry {\n inst: ComponentInstance\n container: HTMLElement\n}\n\nconst hmrRegistry = new Map<string, HmrEntry[]>()\n\nexport function registerForHmr(name: string, inst: object, container: HTMLElement): void {\n const entries = hmrRegistry.get(name) ?? []\n entries.push({ inst: inst as ComponentInstance, container })\n hmrRegistry.set(name, entries)\n}\n\nexport function unregisterForHmr(name: string, inst: object): void {\n const entries = hmrRegistry.get(name)\n if (!entries) return\n const idx = entries.findIndex((e) => e.inst === inst)\n if (idx !== -1) entries.splice(idx, 1)\n if (entries.length === 0) hmrRegistry.delete(name)\n}\n\n/**\n * Hot-swap a component definition on all live instances.\n *\n * Preserves the current state. Replaces update, view, onEffect, and __dirty.\n * Disposes the old scope tree (removing old DOM and bindings),\n * re-runs view(currentState, send) to rebuild fresh DOM.\n *\n * Returns an AppHandle for the first instance (for mountApp compatibility),\n * or null if no instances are registered (first mount).\n */\nexport function replaceComponent<S, M, E>(\n name: string,\n newDef: ComponentDef<S, M, E>,\n): AppHandle | null {\n const entries = hmrRegistry.get(name)\n if (!entries || entries.length === 0) return null\n\n let handle: AppHandle | null = null\n\n for (const { inst, container } of entries) {\n const typedInst = inst as ComponentInstance<S, M, E>\n\n // Replace functions on the live definition\n typedInst.def = {\n ...typedInst.def,\n update: newDef.update,\n view: newDef.view,\n onEffect: newDef.onEffect,\n __dirty: newDef.__dirty,\n __update: newDef.__update,\n __handlers: newDef.__handlers,\n }\n\n // Dispose old scope tree — removes all old DOM nodes and bindings\n disposeScope(typedInst.rootScope)\n container.textContent = ''\n\n // Create fresh scope tree\n typedInst.rootScope = createScope(null)\n typedInst.rootScope._kind = 'root'\n typedInst.allBindings = []\n typedInst.structuralBlocks = []\n\n // Re-run view with current state\n setFlatBindings(typedInst.allBindings)\n setRenderContext({\n rootScope: typedInst.rootScope,\n state: typedInst.state,\n allBindings: typedInst.allBindings,\n structuralBlocks: typedInst.structuralBlocks,\n container,\n send: typedInst.send as (msg: unknown) => void,\n instance: typedInst as ComponentInstance,\n })\n const nodes = typedInst.def.view(createView<S, M>(typedInst.send))\n clearRenderContext()\n setFlatBindings(null)\n\n for (const node of nodes) {\n container.appendChild(node)\n }\n\n // Return AppHandle for the first instance\n if (!handle) {\n handle = {\n dispose() {\n unregisterForHmr(name, inst)\n inst.abortController.abort()\n unregisterInstance(inst)\n disposeScope(typedInst.rootScope)\n container.textContent = ''\n },\n flush() {\n flushInstance(inst)\n },\n send(msg: unknown) {\n ;(typedInst.send as (m: unknown) => void)(msg)\n },\n }\n }\n }\n\n console.log(`[LLui HMR] ${name} updated — state preserved`)\n\n return handle\n}\n"]}
1
+ {"version":3,"file":"hmr.js","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentDef, AppHandle } from './types.js'\nimport type { ComponentInstance } from './update-loop.js'\nimport { flushInstance } from './update-loop.js'\nimport { createScope, disposeScope } from './scope.js'\nimport { setRenderContext, clearRenderContext } from './render-context.js'\nimport { setFlatBindings } from './binding.js'\nimport { unregisterInstance } from './runtime.js'\nimport { _setHmrModule } from './mount.js'\nimport { createView } from './view-helpers.js'\n\n/**\n * Enable HMR state preservation. Called by compiler-generated dev code.\n * Importing this module registers it with mountApp for hot-swapping.\n */\nexport function enableHmr(): void {\n _setHmrModule({\n enableHmr,\n registerForHmr,\n registerForAnchor,\n unregisterForHmr,\n replaceComponent,\n })\n}\n\n// ── HMR Registry ─────────────────────────────────────────────────\n\ntype HmrEntry =\n | {\n kind: 'container'\n inst: ComponentInstance\n container: HTMLElement\n }\n | {\n kind: 'anchor'\n inst: ComponentInstance\n anchor: Comment\n endSentinel: Comment\n }\n\nconst hmrRegistry = new Map<string, HmrEntry[]>()\n\nexport function registerForHmr(name: string, inst: object, container: HTMLElement): void {\n const entries = hmrRegistry.get(name) ?? []\n entries.push({ kind: 'container', inst: inst as ComponentInstance, container })\n hmrRegistry.set(name, entries)\n}\n\nexport function registerForAnchor(\n name: string,\n inst: object,\n anchor: Comment,\n endSentinel: Comment,\n): void {\n const entries = hmrRegistry.get(name) ?? []\n entries.push({ kind: 'anchor', inst: inst as ComponentInstance, anchor, endSentinel })\n hmrRegistry.set(name, entries)\n}\n\nexport function unregisterForHmr(name: string, inst: object): void {\n const entries = hmrRegistry.get(name)\n if (!entries) return\n const idx = entries.findIndex((e) => e.inst === inst)\n if (idx !== -1) entries.splice(idx, 1)\n if (entries.length === 0) hmrRegistry.delete(name)\n}\n\n/**\n * Hot-swap a component definition on all live instances.\n *\n * Preserves the current state. Replaces update, view, onEffect, and __dirty.\n * Disposes the old scope tree (removing old DOM and bindings),\n * re-runs view(currentState, send) to rebuild fresh DOM.\n *\n * Returns an AppHandle for the first instance (for mountApp compatibility),\n * or null if no instances are registered (first mount).\n */\nexport function replaceComponent<S, M, E>(\n name: string,\n newDef: ComponentDef<S, M, E>,\n): AppHandle | null {\n const entries = hmrRegistry.get(name)\n if (!entries || entries.length === 0) return null\n\n let handle: AppHandle | null = null\n\n for (const entry of entries) {\n const typedInst = entry.inst as ComponentInstance<S, M, E>\n\n typedInst.def = {\n ...typedInst.def,\n update: newDef.update,\n view: newDef.view,\n onEffect: newDef.onEffect,\n __dirty: newDef.__dirty,\n __update: newDef.__update,\n __handlers: newDef.__handlers,\n }\n\n disposeScope(typedInst.rootScope)\n\n // Clear the owned region per-kind.\n if (entry.kind === 'container') {\n entry.container.textContent = ''\n } else {\n // anchor kind — wipe siblings between anchor and endSentinel, keep the\n // anchor AND the end sentinel (they bracket the fresh render).\n let sib = entry.anchor.nextSibling\n while (sib !== null && sib !== entry.endSentinel) {\n const next = sib.nextSibling\n sib.parentNode!.removeChild(sib)\n sib = next\n }\n }\n\n typedInst.rootScope = createScope(null)\n typedInst.rootScope._kind = 'root'\n typedInst.allBindings = []\n typedInst.structuralBlocks = []\n\n setFlatBindings(typedInst.allBindings)\n setRenderContext({\n rootScope: typedInst.rootScope,\n state: typedInst.state,\n allBindings: typedInst.allBindings,\n structuralBlocks: typedInst.structuralBlocks,\n container:\n entry.kind === 'container' ? entry.container : (entry.anchor.parentElement ?? undefined),\n send: typedInst.send as (msg: unknown) => void,\n instance: typedInst as ComponentInstance,\n })\n const nodes = typedInst.def.view(createView<S, M>(typedInst.send))\n clearRenderContext()\n setFlatBindings(null)\n\n if (entry.kind === 'container') {\n for (const node of nodes) {\n entry.container.appendChild(node)\n }\n } else {\n for (const node of nodes) {\n entry.anchor.parentNode!.insertBefore(node, entry.endSentinel)\n }\n }\n\n if (!handle) {\n handle = makeReplacementHandle(name, entry, typedInst)\n }\n }\n\n console.log(`[LLui HMR] ${name} updated — state preserved`)\n\n return handle\n}\n\nfunction makeReplacementHandle<S, M, E>(\n name: string,\n entry: HmrEntry,\n typedInst: ComponentInstance<S, M, E>,\n): AppHandle {\n return {\n dispose() {\n unregisterForHmr(name, entry.inst)\n entry.inst.abortController.abort()\n unregisterInstance(entry.inst)\n disposeScope(typedInst.rootScope)\n if (entry.kind === 'container') {\n entry.container.textContent = ''\n } else {\n let sib = entry.anchor.nextSibling\n while (sib !== null && sib !== entry.endSentinel) {\n const next = sib.nextSibling\n sib.parentNode!.removeChild(sib)\n sib = next\n }\n entry.endSentinel.parentNode?.removeChild(entry.endSentinel)\n }\n },\n flush() {\n flushInstance(entry.inst)\n },\n send(msg: unknown) {\n ;(typedInst.send as (m: unknown) => void)(msg)\n },\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export type { ComponentDef, Send, Props, AppHandle, Scope, Binding, BindingKind, TransitionOptions, BranchOptions, ShowOptions, EachOptions, ItemAccessor, PortalOptions, ForeignOptions, ChildOptions, } from './types.js';
2
2
  export { component } from './component.js';
3
3
  export { createView, type View } from './view-helpers.js';
4
- export { mountApp, hydrateApp, type MountOptions } from './mount.js';
4
+ export { mountApp, hydrateApp, mountAtAnchor, hydrateAtAnchor, type MountOptions } from './mount.js';
5
5
  export type { LluiDebugAPI, ElementReport, MessageRecord, StateDiff } from './devtools.js';
6
6
  export type { CoverageSnapshot } from './tracking/coverage.js';
7
7
  export type { EachDiff } from './tracking/each-diff.js';
@@ -16,6 +16,7 @@ export { createContext, provide, provideValue, useContext, useContextValue, type
16
16
  export { sliceHandler } from './slice-handler.js';
17
17
  export { childHandlers, type ChildState, type ChildMsg, type ModuleState, type ModuleMsg, } from './compose.js';
18
18
  export { text } from './primitives/text.js';
19
+ export { unsafeHtml } from './primitives/unsafe-html.js';
19
20
  export { branch } from './primitives/branch.js';
20
21
  export { each } from './primitives/each.js';
21
22
  export { virtualEach, type VirtualEachOptions } from './primitives/virtual-each.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  export { component } from './component.js';
4
4
  export { createView } from './view-helpers.js';
5
5
  // ── Mount ─────────────────────────────────────────────────────────
6
- export { mountApp, hydrateApp } from './mount.js';
6
+ export { mountApp, hydrateApp, mountAtAnchor, hydrateAtAnchor } from './mount.js';
7
7
  // ── Runtime ───────────────────────────────────────────────────────
8
8
  export { flush } from './runtime.js';
9
9
  export { addressOf } from './addressed.js';
@@ -14,6 +14,7 @@ export { sliceHandler } from './slice-handler.js';
14
14
  export { childHandlers, } from './compose.js';
15
15
  // ── View Primitives ───────────────────────────────────────────────
16
16
  export { text } from './primitives/text.js';
17
+ export { unsafeHtml } from './primitives/unsafe-html.js';
17
18
  export { branch } from './primitives/branch.js';
18
19
  export { each } from './primitives/each.js';
19
20
  export { virtualEach } from './primitives/virtual-each.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// ── Types ─────────────────────────────────────────────────────────\n\nexport type {\n ComponentDef,\n Send,\n Props,\n AppHandle,\n Scope,\n Binding,\n BindingKind,\n TransitionOptions,\n BranchOptions,\n ShowOptions,\n EachOptions,\n ItemAccessor,\n PortalOptions,\n ForeignOptions,\n ChildOptions,\n} from './types.js'\n\n// ── Component ─────────────────────────────────────────────────────\n\nexport { component } from './component.js'\nexport { createView, type View } from './view-helpers.js'\n\n// ── Mount ─────────────────────────────────────────────────────────\n\nexport { mountApp, hydrateApp, type MountOptions } from './mount.js'\n// installDevTools is NOT re-exported here to keep it out of production bundles.\n// Import directly: import { installDevTools } from '@llui/dom/devtools'\nexport type { LluiDebugAPI, ElementReport, MessageRecord, StateDiff } from './devtools.js'\nexport type { CoverageSnapshot } from './tracking/coverage.js'\nexport type { EachDiff } from './tracking/each-diff.js'\nexport type { DisposerEvent } from './tracking/disposer-log.js'\nexport type { PendingEffect, EffectTimelineEntry, EffectMatch } from './tracking/effect-timeline.js'\nexport type { ScopeNode } from './types.js'\n\n// ── Runtime ───────────────────────────────────────────────────────\n\nexport { flush } from './runtime.js'\nexport { addressOf } from './addressed.js'\nexport { renderToString, renderNodes, serializeNodes } from './ssr.js'\nexport { mergeHandlers } from './merge-handlers.js'\nexport {\n createContext,\n provide,\n provideValue,\n useContext,\n useContextValue,\n type Context,\n} from './primitives/context.js'\nexport { sliceHandler } from './slice-handler.js'\nexport {\n childHandlers,\n type ChildState,\n type ChildMsg,\n type ModuleState,\n type ModuleMsg,\n} from './compose.js'\n\n// ── View Primitives ───────────────────────────────────────────────\n\nexport { text } from './primitives/text.js'\nexport { branch } from './primitives/branch.js'\nexport { each } from './primitives/each.js'\nexport { virtualEach, type VirtualEachOptions } from './primitives/virtual-each.js'\nexport { show } from './primitives/show.js'\nexport { slice } from './primitives/slice.js'\nexport { portal } from './primitives/portal.js'\nexport { foreign } from './primitives/foreign.js'\nexport { child } from './primitives/child.js'\nexport { lazy, type LazyOptions } from './primitives/lazy.js'\nexport type { LazyDef, AnyComponentDef } from './types.js'\nexport { memo } from './primitives/memo.js'\nexport { selector } from './primitives/selector.js'\nexport { onMount } from './primitives/on-mount.js'\nexport { errorBoundary } from './primitives/error-boundary.js'\n\n// ── Element Helpers ───────────────────────────────────────────────\n\nexport {\n a,\n abbr,\n article,\n aside,\n b,\n blockquote,\n br,\n button,\n canvas,\n code,\n dd,\n details,\n dialog,\n div,\n dl,\n dt,\n em,\n fieldset,\n figcaption,\n figure,\n footer,\n form,\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n header,\n hr,\n i,\n iframe,\n img,\n input,\n label,\n legend,\n li,\n main,\n mark,\n nav,\n ol,\n optgroup,\n option,\n output,\n p,\n pre,\n progress,\n section,\n select,\n small,\n span,\n strong,\n sub,\n summary,\n sup,\n table,\n tbody,\n td,\n textarea,\n tfoot,\n th,\n thead,\n time,\n tr,\n ul,\n video,\n} from './elements.js'\n\n// ── SVG Elements ─────────────────────────────────────────────────\n\nexport {\n svg,\n g,\n defs,\n symbol,\n use,\n circle,\n ellipse,\n line,\n path,\n polygon,\n polyline,\n rect,\n text as svgText,\n tspan,\n textPath,\n clipPath,\n linearGradient,\n radialGradient,\n stop,\n mask,\n pattern,\n marker,\n filter,\n feBlend,\n feColorMatrix,\n feComponentTransfer,\n feComposite,\n feConvolveMatrix,\n feDiffuseLighting,\n feDisplacementMap,\n feDropShadow,\n feFlood,\n feGaussianBlur,\n feImage,\n feMerge,\n feMergeNode,\n feMorphology,\n feOffset,\n feSpecularLighting,\n feTile,\n feTurbulence,\n fePointLight,\n feSpotLight,\n feDistantLight,\n feFuncR,\n feFuncG,\n feFuncB,\n feFuncA,\n image,\n foreignObject,\n animate,\n animateMotion,\n animateTransform,\n set,\n mpath,\n desc,\n title as svgTitle,\n metadata,\n} from './svg-elements.js'\n\n// ── MathML Elements ──────────────────────────────────────────────\n\nexport {\n math,\n mi,\n mn,\n mo,\n ms,\n mtext,\n mrow,\n mfrac,\n msqrt,\n mroot,\n msup,\n msub,\n msubsup,\n munder,\n mover,\n munderover,\n mmultiscripts,\n mprescripts,\n mnone,\n mtable,\n mtr,\n mtd,\n mspace,\n mpadded,\n mphantom,\n menclose,\n merror,\n maction,\n semantics,\n annotation,\n annotationXml,\n} from './mathml-elements.js'\n\n// ── Form Utilities ────────────────────────────────────────────────\n\nexport { applyField, type FieldMsg } from './form.js'\n\n// ── Compiler Target ───────────────────────────────────────────────\n\nexport { elSplit } from './el-split.js'\nexport { elTemplate } from './el-template.js'\nexport { _runPhase2 as __runPhase2, _handleMsg as __handleMsg } from './update-loop.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// ── Types ─────────────────────────────────────────────────────────\n\nexport type {\n ComponentDef,\n Send,\n Props,\n AppHandle,\n Scope,\n Binding,\n BindingKind,\n TransitionOptions,\n BranchOptions,\n ShowOptions,\n EachOptions,\n ItemAccessor,\n PortalOptions,\n ForeignOptions,\n ChildOptions,\n} from './types.js'\n\n// ── Component ─────────────────────────────────────────────────────\n\nexport { component } from './component.js'\nexport { createView, type View } from './view-helpers.js'\n\n// ── Mount ─────────────────────────────────────────────────────────\n\nexport { mountApp, hydrateApp, mountAtAnchor, hydrateAtAnchor, type MountOptions } from './mount.js'\n// installDevTools is NOT re-exported here to keep it out of production bundles.\n// Import directly: import { installDevTools } from '@llui/dom/devtools'\nexport type { LluiDebugAPI, ElementReport, MessageRecord, StateDiff } from './devtools.js'\nexport type { CoverageSnapshot } from './tracking/coverage.js'\nexport type { EachDiff } from './tracking/each-diff.js'\nexport type { DisposerEvent } from './tracking/disposer-log.js'\nexport type { PendingEffect, EffectTimelineEntry, EffectMatch } from './tracking/effect-timeline.js'\nexport type { ScopeNode } from './types.js'\n\n// ── Runtime ───────────────────────────────────────────────────────\n\nexport { flush } from './runtime.js'\nexport { addressOf } from './addressed.js'\nexport { renderToString, renderNodes, serializeNodes } from './ssr.js'\nexport { mergeHandlers } from './merge-handlers.js'\nexport {\n createContext,\n provide,\n provideValue,\n useContext,\n useContextValue,\n type Context,\n} from './primitives/context.js'\nexport { sliceHandler } from './slice-handler.js'\nexport {\n childHandlers,\n type ChildState,\n type ChildMsg,\n type ModuleState,\n type ModuleMsg,\n} from './compose.js'\n\n// ── View Primitives ───────────────────────────────────────────────\n\nexport { text } from './primitives/text.js'\nexport { unsafeHtml } from './primitives/unsafe-html.js'\nexport { branch } from './primitives/branch.js'\nexport { each } from './primitives/each.js'\nexport { virtualEach, type VirtualEachOptions } from './primitives/virtual-each.js'\nexport { show } from './primitives/show.js'\nexport { slice } from './primitives/slice.js'\nexport { portal } from './primitives/portal.js'\nexport { foreign } from './primitives/foreign.js'\nexport { child } from './primitives/child.js'\nexport { lazy, type LazyOptions } from './primitives/lazy.js'\nexport type { LazyDef, AnyComponentDef } from './types.js'\nexport { memo } from './primitives/memo.js'\nexport { selector } from './primitives/selector.js'\nexport { onMount } from './primitives/on-mount.js'\nexport { errorBoundary } from './primitives/error-boundary.js'\n\n// ── Element Helpers ───────────────────────────────────────────────\n\nexport {\n a,\n abbr,\n article,\n aside,\n b,\n blockquote,\n br,\n button,\n canvas,\n code,\n dd,\n details,\n dialog,\n div,\n dl,\n dt,\n em,\n fieldset,\n figcaption,\n figure,\n footer,\n form,\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n header,\n hr,\n i,\n iframe,\n img,\n input,\n label,\n legend,\n li,\n main,\n mark,\n nav,\n ol,\n optgroup,\n option,\n output,\n p,\n pre,\n progress,\n section,\n select,\n small,\n span,\n strong,\n sub,\n summary,\n sup,\n table,\n tbody,\n td,\n textarea,\n tfoot,\n th,\n thead,\n time,\n tr,\n ul,\n video,\n} from './elements.js'\n\n// ── SVG Elements ─────────────────────────────────────────────────\n\nexport {\n svg,\n g,\n defs,\n symbol,\n use,\n circle,\n ellipse,\n line,\n path,\n polygon,\n polyline,\n rect,\n text as svgText,\n tspan,\n textPath,\n clipPath,\n linearGradient,\n radialGradient,\n stop,\n mask,\n pattern,\n marker,\n filter,\n feBlend,\n feColorMatrix,\n feComponentTransfer,\n feComposite,\n feConvolveMatrix,\n feDiffuseLighting,\n feDisplacementMap,\n feDropShadow,\n feFlood,\n feGaussianBlur,\n feImage,\n feMerge,\n feMergeNode,\n feMorphology,\n feOffset,\n feSpecularLighting,\n feTile,\n feTurbulence,\n fePointLight,\n feSpotLight,\n feDistantLight,\n feFuncR,\n feFuncG,\n feFuncB,\n feFuncA,\n image,\n foreignObject,\n animate,\n animateMotion,\n animateTransform,\n set,\n mpath,\n desc,\n title as svgTitle,\n metadata,\n} from './svg-elements.js'\n\n// ── MathML Elements ──────────────────────────────────────────────\n\nexport {\n math,\n mi,\n mn,\n mo,\n ms,\n mtext,\n mrow,\n mfrac,\n msqrt,\n mroot,\n msup,\n msub,\n msubsup,\n munder,\n mover,\n munderover,\n mmultiscripts,\n mprescripts,\n mnone,\n mtable,\n mtr,\n mtd,\n mspace,\n mpadded,\n mphantom,\n menclose,\n merror,\n maction,\n semantics,\n annotation,\n annotationXml,\n} from './mathml-elements.js'\n\n// ── Form Utilities ────────────────────────────────────────────────\n\nexport { applyField, type FieldMsg } from './form.js'\n\n// ── Compiler Target ───────────────────────────────────────────────\n\nexport { elSplit } from './el-split.js'\nexport { elTemplate } from './el-template.js'\nexport { _runPhase2 as __runPhase2, _handleMsg as __handleMsg } from './update-loop.js'\n"]}
package/dist/mount.d.ts CHANGED
@@ -23,5 +23,37 @@ export interface MountOptions {
23
23
  parentScope?: Scope;
24
24
  }
25
25
  export declare function mountApp<S, M, E>(container: HTMLElement, def: ComponentDef<S, M, E>, data?: unknown, options?: MountOptions): AppHandle;
26
+ /**
27
+ * Mount a component relative to a comment anchor rather than inside a
28
+ * container element. Inserts a synthesized end sentinel (`<!-- llui-mount-end -->`)
29
+ * immediately after the anchor and places the component's nodes between
30
+ * the pair. The anchor must already be attached to a live DOM tree.
31
+ *
32
+ * Unlike `mountApp`, the caller's anchor node is preserved across the
33
+ * handle's lifetime — only the content between the pair (and the end
34
+ * sentinel itself) is disposed. Used by `@llui/vike` persistent layouts
35
+ * to mount chain layers without a wrapper element.
36
+ *
37
+ * If a pre-existing `<!-- llui-mount-end -->` is found after the anchor
38
+ * (e.g. stale from an undisposed prior mount), the content between the
39
+ * anchor and that sentinel is swept and the sentinel is reused. Dev mode
40
+ * warns in that case.
41
+ */
42
+ export declare function mountAtAnchor<S, M, E>(anchor: Comment, def: ComponentDef<S, M, E>, data?: unknown, options?: MountOptions): AppHandle;
43
+ /**
44
+ * Hydrate a component relative to a comment anchor rather than inside a
45
+ * container element. Analogous to `hydrateApp` — uses `serverState` as
46
+ * the initial state (not `init()`'s output) while preserving `init()`'s
47
+ * effects for post-mount dispatch.
48
+ *
49
+ * The DOM-handling path is identical to `mountAtAnchor`: reuses a
50
+ * pre-existing end sentinel when present, synthesizes one otherwise.
51
+ * Atomic-swaps the owned region whether or not server content is there
52
+ * to replace. No error for a missing end sentinel — the vike chain's
53
+ * outer `hydrateApp`'s `replaceChildren` wipes inner layers' sentinels,
54
+ * so inner-layer `hydrateAtAnchor` calls routinely find nothing to
55
+ * reuse, and that's normal.
56
+ */
57
+ export declare function hydrateAtAnchor<S, M, E>(anchor: Comment, def: ComponentDef<S, M, E>, serverState: S, options?: MountOptions): AppHandle;
26
58
  export declare function hydrateApp<S, M, E>(container: HTMLElement, def: ComponentDef<S, M, E>, serverState: S, options?: MountOptions): AppHandle;
27
59
  //# sourceMappingURL=mount.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAUhE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,UAAU;QAClB,GAAG,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,OAAO,CAAA;SAAE,CAAA;KACxB;CACF;AAsBD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,KAAK,CAAA;CACpB;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC9B,SAAS,EAAE,WAAW,EACtB,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,SAAS,CA6FX;AA6DD,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAChC,SAAS,EAAE,WAAW,EACtB,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,WAAW,EAAE,CAAC,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,SAAS,CAqEX"}
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AA2ChE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,UAAU;QAClB,GAAG,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,OAAO,CAAA;SAAE,CAAA;KACxB;CACF;AAsBD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,KAAK,CAAA;CACpB;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC9B,SAAS,EAAE,WAAW,EACtB,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,SAAS,CA6FX;AAmDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACnC,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,SAAS,CA6FX;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACrC,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,WAAW,EAAE,CAAC,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,SAAS,CA+EX;AAYD,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAChC,SAAS,EAAE,WAAW,EACtB,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,WAAW,EAAE,CAAC,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,SAAS,CAqEX"}
package/dist/mount.js CHANGED
@@ -5,6 +5,37 @@ import { setFlatBindings } from './binding.js';
5
5
  import { registerInstance, unregisterInstance } from './runtime.js';
6
6
  import { createView } from './view-helpers.js';
7
7
  import { pushMountQueue, popMountQueue, flushMountQueue } from './primitives/on-mount.js';
8
+ // ── Sentinel-region helpers (used by anchor-based mount primitives) ─────
9
+ /**
10
+ * Remove every sibling from `anchor.nextSibling` up to but not including
11
+ * `stopBefore`. Used by anchor-based mount primitives and their HMR
12
+ * swap path to clear the owned DOM region between the pair.
13
+ */
14
+ function _removeBetween(anchor, stopBefore) {
15
+ const parent = anchor.parentNode;
16
+ if (parent === null)
17
+ return;
18
+ while (anchor.nextSibling !== null && anchor.nextSibling !== stopBefore) {
19
+ parent.removeChild(anchor.nextSibling);
20
+ }
21
+ }
22
+ /**
23
+ * Walk forward from `anchor.nextSibling` looking for an existing
24
+ * `<!-- llui-mount-end -->` sentinel. Used by mount/hydrate at anchor
25
+ * to reuse a server-emitted (or stale) sentinel rather than synthesizing
26
+ * a duplicate. Returns null if no matching comment is found before the
27
+ * end of the parent's children.
28
+ */
29
+ function _findEndSentinel(anchor) {
30
+ let node = anchor.nextSibling;
31
+ while (node !== null) {
32
+ if (node.nodeType === 8 && node.nodeValue === 'llui-mount-end') {
33
+ return node;
34
+ }
35
+ node = node.nextSibling;
36
+ }
37
+ return null;
38
+ }
8
39
  // ── HMR (dev only) ──────────────────────────────────────────────
9
40
  // Set by enableHmr() from '@llui/dom/hmr' — never imported in production.
10
41
  let hmrModule = null;
@@ -161,6 +192,198 @@ function findNonSerializable(v, path = 'state', depth = 0, seen = new WeakSet())
161
192
  }
162
193
  return null;
163
194
  }
195
+ /**
196
+ * Mount a component relative to a comment anchor rather than inside a
197
+ * container element. Inserts a synthesized end sentinel (`<!-- llui-mount-end -->`)
198
+ * immediately after the anchor and places the component's nodes between
199
+ * the pair. The anchor must already be attached to a live DOM tree.
200
+ *
201
+ * Unlike `mountApp`, the caller's anchor node is preserved across the
202
+ * handle's lifetime — only the content between the pair (and the end
203
+ * sentinel itself) is disposed. Used by `@llui/vike` persistent layouts
204
+ * to mount chain layers without a wrapper element.
205
+ *
206
+ * If a pre-existing `<!-- llui-mount-end -->` is found after the anchor
207
+ * (e.g. stale from an undisposed prior mount), the content between the
208
+ * anchor and that sentinel is swept and the sentinel is reused. Dev mode
209
+ * warns in that case.
210
+ */
211
+ export function mountAtAnchor(anchor, def, data, options) {
212
+ if (anchor.parentNode === null) {
213
+ throw new Error(`[LLui] mountAtAnchor: anchor comment must be attached to a live DOM tree before mount`);
214
+ }
215
+ // Locate or synthesize the end sentinel.
216
+ const existingEnd = _findEndSentinel(anchor);
217
+ let endSentinel;
218
+ if (existingEnd !== null) {
219
+ if (import.meta.env?.DEV) {
220
+ console.warn(`[LLui] mountAtAnchor: anchor has a pre-existing end sentinel. ` +
221
+ `A prior mount was not disposed — sweeping stale siblings and reusing the sentinel.`);
222
+ }
223
+ _removeBetween(anchor, existingEnd);
224
+ endSentinel = existingEnd;
225
+ }
226
+ else {
227
+ endSentinel = document.createComment('llui-mount-end');
228
+ anchor.parentNode.insertBefore(endSentinel, anchor.nextSibling);
229
+ }
230
+ const inst = createComponentInstance(def, data, options?.parentScope ?? null);
231
+ if (devToolsInstall)
232
+ devToolsInstall(inst);
233
+ if (import.meta.env?.DEV) {
234
+ const offender = findNonSerializable(inst.state);
235
+ if (offender) {
236
+ console.warn(`[LLui] <${def.name}> initial state contains a non-serializable value at "${offender.path}":`, offender.value, '\nState must be plain JSON (no Date/Map/Set/class instances/functions).' +
237
+ '\nThis will break SSR hydration, state replay, and devtools snapshots.' +
238
+ '\nhint: Convert to a serializable representation (e.g., Date → ISO string, Map → Record).');
239
+ }
240
+ }
241
+ const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue();
242
+ setFlatBindings(inst.allBindings);
243
+ setRenderContext({
244
+ ...inst,
245
+ container: anchor.parentElement ?? undefined,
246
+ send: inst.send,
247
+ instance: inst,
248
+ });
249
+ const nodes = def.view(createView(inst.send));
250
+ clearRenderContext();
251
+ setFlatBindings(null);
252
+ popMountQueue(prevMountQueue);
253
+ // Batch-insert via DocumentFragment — one layout pass instead of N.
254
+ if (nodes.length > 1) {
255
+ const frag = document.createDocumentFragment();
256
+ for (const node of nodes)
257
+ frag.appendChild(node);
258
+ anchor.parentNode.insertBefore(frag, endSentinel);
259
+ }
260
+ else if (nodes.length === 1) {
261
+ anchor.parentNode.insertBefore(nodes[0], endSentinel);
262
+ }
263
+ flushMountQueue(onMountQueue);
264
+ registerInstance(inst);
265
+ if (hmrModule && def.name) {
266
+ hmrModule.registerForAnchor(def.name, inst, anchor, endSentinel);
267
+ }
268
+ dispatchInitialEffects(inst);
269
+ let disposed = false;
270
+ return {
271
+ dispose() {
272
+ if (disposed)
273
+ return;
274
+ disposed = true;
275
+ if (hmrModule && def.name)
276
+ hmrModule.unregisterForHmr(def.name, inst);
277
+ inst.abortController.abort();
278
+ unregisterInstance(inst);
279
+ inst.rootScope.disposalCause = 'app-unmount';
280
+ disposeScope(inst.rootScope);
281
+ _removeBetween(anchor, endSentinel);
282
+ endSentinel.parentNode?.removeChild(endSentinel);
283
+ },
284
+ flush() {
285
+ if (disposed)
286
+ return;
287
+ flushInstance(inst);
288
+ },
289
+ send(msg) {
290
+ if (disposed)
291
+ return;
292
+ inst.send(msg);
293
+ },
294
+ };
295
+ }
296
+ /**
297
+ * Hydrate a component relative to a comment anchor rather than inside a
298
+ * container element. Analogous to `hydrateApp` — uses `serverState` as
299
+ * the initial state (not `init()`'s output) while preserving `init()`'s
300
+ * effects for post-mount dispatch.
301
+ *
302
+ * The DOM-handling path is identical to `mountAtAnchor`: reuses a
303
+ * pre-existing end sentinel when present, synthesizes one otherwise.
304
+ * Atomic-swaps the owned region whether or not server content is there
305
+ * to replace. No error for a missing end sentinel — the vike chain's
306
+ * outer `hydrateApp`'s `replaceChildren` wipes inner layers' sentinels,
307
+ * so inner-layer `hydrateAtAnchor` calls routinely find nothing to
308
+ * reuse, and that's normal.
309
+ */
310
+ export function hydrateAtAnchor(anchor, def, serverState, options) {
311
+ if (anchor.parentNode === null) {
312
+ throw new Error(`[LLui] hydrateAtAnchor: anchor comment must be attached to a live DOM tree before hydrate`);
313
+ }
314
+ const existingEnd = _findEndSentinel(anchor);
315
+ let endSentinel;
316
+ if (existingEnd !== null) {
317
+ _removeBetween(anchor, existingEnd);
318
+ endSentinel = existingEnd;
319
+ }
320
+ else {
321
+ endSentinel = document.createComment('llui-mount-end');
322
+ anchor.parentNode.insertBefore(endSentinel, anchor.nextSibling);
323
+ }
324
+ // Run original init() to capture effects, then override state with server's.
325
+ const [, originalEffects] = def.init(undefined);
326
+ const hydrateDef = {
327
+ ...def,
328
+ init: () => [serverState, originalEffects],
329
+ };
330
+ const inst = createComponentInstance(hydrateDef, undefined, options?.parentScope ?? null);
331
+ if (devToolsInstall)
332
+ devToolsInstall(inst);
333
+ const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue();
334
+ setFlatBindings(inst.allBindings);
335
+ setRenderContext({
336
+ ...inst,
337
+ container: anchor.parentElement ?? undefined,
338
+ send: inst.send,
339
+ instance: inst,
340
+ });
341
+ const nodes = hydrateDef.view(createView(inst.send));
342
+ clearRenderContext();
343
+ setFlatBindings(null);
344
+ popMountQueue(prevMountQueue);
345
+ if (nodes.length > 1) {
346
+ const frag = document.createDocumentFragment();
347
+ for (const node of nodes)
348
+ frag.appendChild(node);
349
+ anchor.parentNode.insertBefore(frag, endSentinel);
350
+ }
351
+ else if (nodes.length === 1) {
352
+ anchor.parentNode.insertBefore(nodes[0], endSentinel);
353
+ }
354
+ flushMountQueue(onMountQueue);
355
+ registerInstance(inst);
356
+ if (hmrModule && def.name) {
357
+ hmrModule.registerForAnchor(def.name, inst, anchor, endSentinel);
358
+ }
359
+ dispatchInitialEffects(inst);
360
+ let disposed = false;
361
+ return {
362
+ dispose() {
363
+ if (disposed)
364
+ return;
365
+ disposed = true;
366
+ if (hmrModule && def.name)
367
+ hmrModule.unregisterForHmr(def.name, inst);
368
+ inst.abortController.abort();
369
+ unregisterInstance(inst);
370
+ inst.rootScope.disposalCause = 'app-unmount';
371
+ disposeScope(inst.rootScope);
372
+ _removeBetween(anchor, endSentinel);
373
+ endSentinel.parentNode?.removeChild(endSentinel);
374
+ },
375
+ flush() {
376
+ if (disposed)
377
+ return;
378
+ flushInstance(inst);
379
+ },
380
+ send(msg) {
381
+ if (disposed)
382
+ return;
383
+ inst.send(msg);
384
+ },
385
+ };
386
+ }
164
387
  function dispatchInitialEffects(inst) {
165
388
  if (inst.initialEffects.length === 0 || !inst.def.onEffect)
166
389
  return;
package/dist/mount.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mount.js","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentDef, AppHandle, Scope } from './types.js'\nimport { createComponentInstance, flushInstance, type ComponentInstance } from './update-loop.js'\nimport { disposeScope } from './scope.js'\nimport { setRenderContext, clearRenderContext } from './render-context.js'\nimport { setFlatBindings } from './binding.js'\nimport { registerInstance, unregisterInstance } from './runtime.js'\nimport { createView } from './view-helpers.js'\nimport { pushMountQueue, popMountQueue, flushMountQueue } from './primitives/on-mount.js'\n\n// Vite injects import.meta.env.DEV — declare the shape for TypeScript\ndeclare global {\n interface ImportMeta {\n env?: { DEV?: boolean }\n }\n}\n\n// ── HMR (dev only) ──────────────────────────────────────────────\n// Set by enableHmr() from '@llui/dom/hmr' — never imported in production.\n\nlet hmrModule: typeof import('./hmr') | null = null\n\n/** @internal Called by enableHmr in the hmr module */\nexport function _setHmrModule(m: typeof import('./hmr')): void {\n hmrModule = m\n}\n\n// ── DevTools auto-install (dev only) ────────────────────────────\n// Set by enableDevTools() from '@llui/dom/devtools' — never imported in production.\n\nlet devToolsInstall: ((inst: object) => void) | null = null\n\n/** @internal Called by enableDevTools in the devtools module */\nexport function _setDevToolsInstall(fn: ((inst: object) => void) | null): void {\n devToolsInstall = fn\n}\n\nexport interface MountOptions {\n devTools?: boolean\n /**\n * Parent scope for the mounted component's rootScope. When provided,\n * the rootScope is created as a child of this scope — context lookups\n * from within the component walk up through the parent's scope tree,\n * and disposing the parent scope cascades into this instance's scope.\n * Used by `@llui/vike`'s persistent-layout machinery to mount a page\n * as a true scope-tree child of its enclosing layout, so layout-\n * provided contexts flow naturally into pages via `useContext`.\n *\n * When omitted (the default), the rootScope is detached — same as\n * every `mountApp` call before persistent layouts existed.\n */\n parentScope?: Scope\n}\n\nexport function mountApp<S, M, E>(\n container: HTMLElement,\n def: ComponentDef<S, M, E>,\n data?: unknown,\n options?: MountOptions,\n): AppHandle {\n // HMR: if this component is already mounted (module re-execution\n // during hot update), swap the definition instead of creating a new instance.\n // HMR swap bypasses parentScope — HMR re-mounts the outermost app handle,\n // which in a layout setup means the layout re-mounts at the root and the\n // rest of the chain is re-established via the normal mount path.\n if (hmrModule && def.name && !options?.parentScope) {\n const swapped = hmrModule.replaceComponent(def.name, def)\n if (swapped) return swapped\n }\n\n const inst = createComponentInstance(def, data, options?.parentScope ?? null)\n\n // Dev-only: auto-install devtools if enabled via '@llui/dom/devtools' import\n if (devToolsInstall) devToolsInstall(inst)\n\n // Dev-only: warn if initial state contains non-serializable values.\n // Silent bug-bomb: Date/Map/Set/class instances break SSR, hydration, replay tools.\n if (import.meta.env?.DEV) {\n const offender = findNonSerializable(inst.state)\n if (offender) {\n console.warn(\n `[LLui] <${def.name}> initial state contains a non-serializable value at \"${offender.path}\":`,\n offender.value,\n '\\nState must be plain JSON (no Date/Map/Set/class instances/functions).' +\n '\\nThis will break SSR hydration, state replay, and devtools snapshots.' +\n '\\nhint: Convert to a serializable representation (e.g., Date → ISO string, Map → Record).',\n )\n }\n }\n\n // Run view() within a render context so primitives can register bindings.\n // Also collect onMount callbacks in a queue we'll flush synchronously\n // after node insertion — prevents the race where a user event fires\n // between mount and the queueMicrotask callback running.\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = def.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n // Batch-insert via DocumentFragment — one layout-invalidating operation\n // instead of N individual appendChild calls on a live container element.\n if (nodes.length > 1) {\n const frag = document.createDocumentFragment()\n for (const node of nodes) frag.appendChild(node)\n container.appendChild(frag)\n } else if (nodes.length === 1) {\n container.appendChild(nodes[0]!)\n }\n\n // Flush onMount callbacks SYNCHRONOUSLY now that the DOM is in place.\n // Any listeners they attach are ready before this function returns,\n // so a synchronous dispatchEvent in the caller's next line fires\n // against a fully-wired tree.\n flushMountQueue(onMountQueue)\n\n registerInstance(inst)\n if (hmrModule && def.name) {\n hmrModule.registerForHmr(def.name, inst, container)\n }\n dispatchInitialEffects(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n if (hmrModule && def.name) hmrModule.unregisterForHmr(def.name, inst)\n inst.abortController.abort()\n unregisterInstance(inst)\n // Tag the root scope so the disposer log reports app-level\n // teardown distinct from in-tree component-unmount events.\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n container.textContent = ''\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n\n// Walks an object graph looking for non-JSON-serializable values. Returns the\n// first offender found (depth-first), or null if everything is fine. Stops at\n// depth 6 to bound runtime cost for large states.\nfunction findNonSerializable(\n v: unknown,\n path = 'state',\n depth = 0,\n seen = new WeakSet<object>(),\n): { path: string; value: unknown } | null {\n if (depth > 6) return null\n if (v === null || v === undefined) return null\n const t = typeof v\n if (t === 'string' || t === 'number' || t === 'boolean') return null\n if (t === 'function') return { path, value: v }\n if (t === 'symbol' || t === 'bigint') return { path, value: v }\n if (t !== 'object') return null\n const obj = v as object\n if (seen.has(obj)) return null\n seen.add(obj)\n if (obj instanceof Date) return { path: `${path} (Date)`, value: v }\n if (obj instanceof Map) return { path: `${path} (Map)`, value: v }\n if (obj instanceof Set) return { path: `${path} (Set)`, value: v }\n if (obj instanceof RegExp) return { path: `${path} (RegExp)`, value: v }\n if (obj instanceof Promise) return { path: `${path} (Promise)`, value: v }\n // Plain objects/arrays have Object.prototype / Array.prototype. Class instances\n // have a different prototype.\n const proto = Object.getPrototypeOf(obj)\n if (proto !== null && proto !== Object.prototype && proto !== Array.prototype) {\n return { path: `${path} (${proto?.constructor?.name ?? 'class instance'})`, value: v }\n }\n if (Array.isArray(v)) {\n for (let i = 0; i < v.length; i++) {\n const r = findNonSerializable(v[i], `${path}[${i}]`, depth + 1, seen)\n if (r) return r\n }\n return null\n }\n for (const k of Object.keys(obj)) {\n const r = findNonSerializable(\n (obj as Record<string, unknown>)[k],\n `${path}.${k}`,\n depth + 1,\n seen,\n )\n if (r) return r\n }\n return null\n}\n\nfunction dispatchInitialEffects<S, M, E>(\n inst: ReturnType<typeof createComponentInstance<S, M, E>>,\n): void {\n if (inst.initialEffects.length === 0 || !inst.def.onEffect) return\n for (const effect of inst.initialEffects) {\n inst.def.onEffect({ effect, send: inst.send, signal: inst.signal })\n }\n inst.initialEffects = []\n}\n\nexport function hydrateApp<S, M, E>(\n container: HTMLElement,\n def: ComponentDef<S, M, E>,\n serverState: S,\n options?: MountOptions,\n): AppHandle {\n // Run the original init once to capture its effects. The state it\n // returns is discarded — we use `serverState` (what the server\n // rendered with) instead. The effects are preserved and dispatched\n // after the DOM is in place, so components that rely on \"load data\n // or wire subscriptions on mount\" behave consistently between fresh\n // mount and SSR+hydrate. If the original init has already-loaded\n // data for the hydration case, gate the effect emission inside init\n // itself (e.g. based on a `loaded` flag in state).\n const [, originalEffects] = (def.init as (data: unknown) => [S, E[]])(undefined)\n\n const hydrateDef: ComponentDef<S, M, E> = {\n ...def,\n init: () => [serverState, originalEffects],\n }\n\n const inst = createComponentInstance(hydrateDef, undefined, options?.parentScope ?? null)\n\n // Build the component DOM and swap atomically with server HTML.\n // Server HTML remains visible until JS finishes — no flash.\n // onMount callbacks are collected in a queue and flushed synchronously\n // after the swap, matching mountApp's ordering.\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = hydrateDef.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n // Atomic swap — replaces server HTML with client DOM in one operation\n container.replaceChildren(...nodes)\n\n // Flush onMount callbacks synchronously now that the DOM is in place.\n flushMountQueue(onMountQueue)\n\n // Fire the original init's effects post-swap, matching mountApp's\n // lifecycle. Previously these were silently dropped.\n dispatchInitialEffects(inst)\n\n registerInstance(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n inst.abortController.abort()\n unregisterInstance(inst)\n // Tag the root scope so the disposer log reports app-level\n // teardown distinct from in-tree component-unmount events.\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n container.textContent = ''\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n"]}
1
+ {"version":3,"file":"mount.js","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentDef, AppHandle, Scope } from './types.js'\nimport { createComponentInstance, flushInstance, type ComponentInstance } from './update-loop.js'\nimport { disposeScope } from './scope.js'\nimport { setRenderContext, clearRenderContext } from './render-context.js'\nimport { setFlatBindings } from './binding.js'\nimport { registerInstance, unregisterInstance } from './runtime.js'\nimport { createView } from './view-helpers.js'\nimport { pushMountQueue, popMountQueue, flushMountQueue } from './primitives/on-mount.js'\n\n// ── Sentinel-region helpers (used by anchor-based mount primitives) ─────\n\n/**\n * Remove every sibling from `anchor.nextSibling` up to but not including\n * `stopBefore`. Used by anchor-based mount primitives and their HMR\n * swap path to clear the owned DOM region between the pair.\n */\nfunction _removeBetween(anchor: Comment, stopBefore: Comment): void {\n const parent = anchor.parentNode\n if (parent === null) return\n while (anchor.nextSibling !== null && anchor.nextSibling !== stopBefore) {\n parent.removeChild(anchor.nextSibling)\n }\n}\n\n/**\n * Walk forward from `anchor.nextSibling` looking for an existing\n * `<!-- llui-mount-end -->` sentinel. Used by mount/hydrate at anchor\n * to reuse a server-emitted (or stale) sentinel rather than synthesizing\n * a duplicate. Returns null if no matching comment is found before the\n * end of the parent's children.\n */\nfunction _findEndSentinel(anchor: Comment): Comment | null {\n let node: Node | null = anchor.nextSibling\n while (node !== null) {\n if (node.nodeType === 8 && (node as Comment).nodeValue === 'llui-mount-end') {\n return node as Comment\n }\n node = node.nextSibling\n }\n return null\n}\n\n// Vite injects import.meta.env.DEV — declare the shape for TypeScript\ndeclare global {\n interface ImportMeta {\n env?: { DEV?: boolean }\n }\n}\n\n// ── HMR (dev only) ──────────────────────────────────────────────\n// Set by enableHmr() from '@llui/dom/hmr' — never imported in production.\n\nlet hmrModule: typeof import('./hmr') | null = null\n\n/** @internal Called by enableHmr in the hmr module */\nexport function _setHmrModule(m: typeof import('./hmr')): void {\n hmrModule = m\n}\n\n// ── DevTools auto-install (dev only) ────────────────────────────\n// Set by enableDevTools() from '@llui/dom/devtools' — never imported in production.\n\nlet devToolsInstall: ((inst: object) => void) | null = null\n\n/** @internal Called by enableDevTools in the devtools module */\nexport function _setDevToolsInstall(fn: ((inst: object) => void) | null): void {\n devToolsInstall = fn\n}\n\nexport interface MountOptions {\n devTools?: boolean\n /**\n * Parent scope for the mounted component's rootScope. When provided,\n * the rootScope is created as a child of this scope — context lookups\n * from within the component walk up through the parent's scope tree,\n * and disposing the parent scope cascades into this instance's scope.\n * Used by `@llui/vike`'s persistent-layout machinery to mount a page\n * as a true scope-tree child of its enclosing layout, so layout-\n * provided contexts flow naturally into pages via `useContext`.\n *\n * When omitted (the default), the rootScope is detached — same as\n * every `mountApp` call before persistent layouts existed.\n */\n parentScope?: Scope\n}\n\nexport function mountApp<S, M, E>(\n container: HTMLElement,\n def: ComponentDef<S, M, E>,\n data?: unknown,\n options?: MountOptions,\n): AppHandle {\n // HMR: if this component is already mounted (module re-execution\n // during hot update), swap the definition instead of creating a new instance.\n // HMR swap bypasses parentScope — HMR re-mounts the outermost app handle,\n // which in a layout setup means the layout re-mounts at the root and the\n // rest of the chain is re-established via the normal mount path.\n if (hmrModule && def.name && !options?.parentScope) {\n const swapped = hmrModule.replaceComponent(def.name, def)\n if (swapped) return swapped\n }\n\n const inst = createComponentInstance(def, data, options?.parentScope ?? null)\n\n // Dev-only: auto-install devtools if enabled via '@llui/dom/devtools' import\n if (devToolsInstall) devToolsInstall(inst)\n\n // Dev-only: warn if initial state contains non-serializable values.\n // Silent bug-bomb: Date/Map/Set/class instances break SSR, hydration, replay tools.\n if (import.meta.env?.DEV) {\n const offender = findNonSerializable(inst.state)\n if (offender) {\n console.warn(\n `[LLui] <${def.name}> initial state contains a non-serializable value at \"${offender.path}\":`,\n offender.value,\n '\\nState must be plain JSON (no Date/Map/Set/class instances/functions).' +\n '\\nThis will break SSR hydration, state replay, and devtools snapshots.' +\n '\\nhint: Convert to a serializable representation (e.g., Date → ISO string, Map → Record).',\n )\n }\n }\n\n // Run view() within a render context so primitives can register bindings.\n // Also collect onMount callbacks in a queue we'll flush synchronously\n // after node insertion — prevents the race where a user event fires\n // between mount and the queueMicrotask callback running.\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = def.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n // Batch-insert via DocumentFragment — one layout-invalidating operation\n // instead of N individual appendChild calls on a live container element.\n if (nodes.length > 1) {\n const frag = document.createDocumentFragment()\n for (const node of nodes) frag.appendChild(node)\n container.appendChild(frag)\n } else if (nodes.length === 1) {\n container.appendChild(nodes[0]!)\n }\n\n // Flush onMount callbacks SYNCHRONOUSLY now that the DOM is in place.\n // Any listeners they attach are ready before this function returns,\n // so a synchronous dispatchEvent in the caller's next line fires\n // against a fully-wired tree.\n flushMountQueue(onMountQueue)\n\n registerInstance(inst)\n if (hmrModule && def.name) {\n hmrModule.registerForHmr(def.name, inst, container)\n }\n dispatchInitialEffects(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n if (hmrModule && def.name) hmrModule.unregisterForHmr(def.name, inst)\n inst.abortController.abort()\n unregisterInstance(inst)\n // Tag the root scope so the disposer log reports app-level\n // teardown distinct from in-tree component-unmount events.\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n container.textContent = ''\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n\n// Walks an object graph looking for non-JSON-serializable values. Returns the\n// first offender found (depth-first), or null if everything is fine. Stops at\n// depth 6 to bound runtime cost for large states.\nfunction findNonSerializable(\n v: unknown,\n path = 'state',\n depth = 0,\n seen = new WeakSet<object>(),\n): { path: string; value: unknown } | null {\n if (depth > 6) return null\n if (v === null || v === undefined) return null\n const t = typeof v\n if (t === 'string' || t === 'number' || t === 'boolean') return null\n if (t === 'function') return { path, value: v }\n if (t === 'symbol' || t === 'bigint') return { path, value: v }\n if (t !== 'object') return null\n const obj = v as object\n if (seen.has(obj)) return null\n seen.add(obj)\n if (obj instanceof Date) return { path: `${path} (Date)`, value: v }\n if (obj instanceof Map) return { path: `${path} (Map)`, value: v }\n if (obj instanceof Set) return { path: `${path} (Set)`, value: v }\n if (obj instanceof RegExp) return { path: `${path} (RegExp)`, value: v }\n if (obj instanceof Promise) return { path: `${path} (Promise)`, value: v }\n // Plain objects/arrays have Object.prototype / Array.prototype. Class instances\n // have a different prototype.\n const proto = Object.getPrototypeOf(obj)\n if (proto !== null && proto !== Object.prototype && proto !== Array.prototype) {\n return { path: `${path} (${proto?.constructor?.name ?? 'class instance'})`, value: v }\n }\n if (Array.isArray(v)) {\n for (let i = 0; i < v.length; i++) {\n const r = findNonSerializable(v[i], `${path}[${i}]`, depth + 1, seen)\n if (r) return r\n }\n return null\n }\n for (const k of Object.keys(obj)) {\n const r = findNonSerializable(\n (obj as Record<string, unknown>)[k],\n `${path}.${k}`,\n depth + 1,\n seen,\n )\n if (r) return r\n }\n return null\n}\n\n/**\n * Mount a component relative to a comment anchor rather than inside a\n * container element. Inserts a synthesized end sentinel (`<!-- llui-mount-end -->`)\n * immediately after the anchor and places the component's nodes between\n * the pair. The anchor must already be attached to a live DOM tree.\n *\n * Unlike `mountApp`, the caller's anchor node is preserved across the\n * handle's lifetime — only the content between the pair (and the end\n * sentinel itself) is disposed. Used by `@llui/vike` persistent layouts\n * to mount chain layers without a wrapper element.\n *\n * If a pre-existing `<!-- llui-mount-end -->` is found after the anchor\n * (e.g. stale from an undisposed prior mount), the content between the\n * anchor and that sentinel is swept and the sentinel is reused. Dev mode\n * warns in that case.\n */\nexport function mountAtAnchor<S, M, E>(\n anchor: Comment,\n def: ComponentDef<S, M, E>,\n data?: unknown,\n options?: MountOptions,\n): AppHandle {\n if (anchor.parentNode === null) {\n throw new Error(\n `[LLui] mountAtAnchor: anchor comment must be attached to a live DOM tree before mount`,\n )\n }\n\n // Locate or synthesize the end sentinel.\n const existingEnd = _findEndSentinel(anchor)\n let endSentinel: Comment\n if (existingEnd !== null) {\n if (import.meta.env?.DEV) {\n console.warn(\n `[LLui] mountAtAnchor: anchor has a pre-existing end sentinel. ` +\n `A prior mount was not disposed — sweeping stale siblings and reusing the sentinel.`,\n )\n }\n _removeBetween(anchor, existingEnd)\n endSentinel = existingEnd\n } else {\n endSentinel = document.createComment('llui-mount-end')\n anchor.parentNode.insertBefore(endSentinel, anchor.nextSibling)\n }\n\n const inst = createComponentInstance(def, data, options?.parentScope ?? null)\n\n if (devToolsInstall) devToolsInstall(inst)\n\n if (import.meta.env?.DEV) {\n const offender = findNonSerializable(inst.state)\n if (offender) {\n console.warn(\n `[LLui] <${def.name}> initial state contains a non-serializable value at \"${offender.path}\":`,\n offender.value,\n '\\nState must be plain JSON (no Date/Map/Set/class instances/functions).' +\n '\\nThis will break SSR hydration, state replay, and devtools snapshots.' +\n '\\nhint: Convert to a serializable representation (e.g., Date → ISO string, Map → Record).',\n )\n }\n }\n\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container: anchor.parentElement ?? undefined,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = def.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n // Batch-insert via DocumentFragment — one layout pass instead of N.\n if (nodes.length > 1) {\n const frag = document.createDocumentFragment()\n for (const node of nodes) frag.appendChild(node)\n anchor.parentNode.insertBefore(frag, endSentinel)\n } else if (nodes.length === 1) {\n anchor.parentNode.insertBefore(nodes[0]!, endSentinel)\n }\n\n flushMountQueue(onMountQueue)\n\n registerInstance(inst)\n if (hmrModule && def.name) {\n hmrModule.registerForAnchor(def.name, inst, anchor, endSentinel)\n }\n dispatchInitialEffects(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n if (hmrModule && def.name) hmrModule.unregisterForHmr(def.name, inst)\n inst.abortController.abort()\n unregisterInstance(inst)\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n _removeBetween(anchor, endSentinel)\n endSentinel.parentNode?.removeChild(endSentinel)\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n\n/**\n * Hydrate a component relative to a comment anchor rather than inside a\n * container element. Analogous to `hydrateApp` — uses `serverState` as\n * the initial state (not `init()`'s output) while preserving `init()`'s\n * effects for post-mount dispatch.\n *\n * The DOM-handling path is identical to `mountAtAnchor`: reuses a\n * pre-existing end sentinel when present, synthesizes one otherwise.\n * Atomic-swaps the owned region whether or not server content is there\n * to replace. No error for a missing end sentinel — the vike chain's\n * outer `hydrateApp`'s `replaceChildren` wipes inner layers' sentinels,\n * so inner-layer `hydrateAtAnchor` calls routinely find nothing to\n * reuse, and that's normal.\n */\nexport function hydrateAtAnchor<S, M, E>(\n anchor: Comment,\n def: ComponentDef<S, M, E>,\n serverState: S,\n options?: MountOptions,\n): AppHandle {\n if (anchor.parentNode === null) {\n throw new Error(\n `[LLui] hydrateAtAnchor: anchor comment must be attached to a live DOM tree before hydrate`,\n )\n }\n\n const existingEnd = _findEndSentinel(anchor)\n let endSentinel: Comment\n if (existingEnd !== null) {\n _removeBetween(anchor, existingEnd)\n endSentinel = existingEnd\n } else {\n endSentinel = document.createComment('llui-mount-end')\n anchor.parentNode.insertBefore(endSentinel, anchor.nextSibling)\n }\n\n // Run original init() to capture effects, then override state with server's.\n const [, originalEffects] = (def.init as (data: unknown) => [S, E[]])(undefined)\n const hydrateDef: ComponentDef<S, M, E> = {\n ...def,\n init: () => [serverState, originalEffects],\n }\n\n const inst = createComponentInstance(hydrateDef, undefined, options?.parentScope ?? null)\n\n if (devToolsInstall) devToolsInstall(inst)\n\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container: anchor.parentElement ?? undefined,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = hydrateDef.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n if (nodes.length > 1) {\n const frag = document.createDocumentFragment()\n for (const node of nodes) frag.appendChild(node)\n anchor.parentNode.insertBefore(frag, endSentinel)\n } else if (nodes.length === 1) {\n anchor.parentNode.insertBefore(nodes[0]!, endSentinel)\n }\n\n flushMountQueue(onMountQueue)\n\n registerInstance(inst)\n if (hmrModule && def.name) {\n hmrModule.registerForAnchor(def.name, inst, anchor, endSentinel)\n }\n dispatchInitialEffects(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n if (hmrModule && def.name) hmrModule.unregisterForHmr(def.name, inst)\n inst.abortController.abort()\n unregisterInstance(inst)\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n _removeBetween(anchor, endSentinel)\n endSentinel.parentNode?.removeChild(endSentinel)\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n\nfunction dispatchInitialEffects<S, M, E>(\n inst: ReturnType<typeof createComponentInstance<S, M, E>>,\n): void {\n if (inst.initialEffects.length === 0 || !inst.def.onEffect) return\n for (const effect of inst.initialEffects) {\n inst.def.onEffect({ effect, send: inst.send, signal: inst.signal })\n }\n inst.initialEffects = []\n}\n\nexport function hydrateApp<S, M, E>(\n container: HTMLElement,\n def: ComponentDef<S, M, E>,\n serverState: S,\n options?: MountOptions,\n): AppHandle {\n // Run the original init once to capture its effects. The state it\n // returns is discarded — we use `serverState` (what the server\n // rendered with) instead. The effects are preserved and dispatched\n // after the DOM is in place, so components that rely on \"load data\n // or wire subscriptions on mount\" behave consistently between fresh\n // mount and SSR+hydrate. If the original init has already-loaded\n // data for the hydration case, gate the effect emission inside init\n // itself (e.g. based on a `loaded` flag in state).\n const [, originalEffects] = (def.init as (data: unknown) => [S, E[]])(undefined)\n\n const hydrateDef: ComponentDef<S, M, E> = {\n ...def,\n init: () => [serverState, originalEffects],\n }\n\n const inst = createComponentInstance(hydrateDef, undefined, options?.parentScope ?? null)\n\n // Build the component DOM and swap atomically with server HTML.\n // Server HTML remains visible until JS finishes — no flash.\n // onMount callbacks are collected in a queue and flushed synchronously\n // after the swap, matching mountApp's ordering.\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = hydrateDef.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n // Atomic swap — replaces server HTML with client DOM in one operation\n container.replaceChildren(...nodes)\n\n // Flush onMount callbacks synchronously now that the DOM is in place.\n flushMountQueue(onMountQueue)\n\n // Fire the original init's effects post-swap, matching mountApp's\n // lifecycle. Previously these were silently dropped.\n dispatchInitialEffects(inst)\n\n registerInstance(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n inst.abortController.abort()\n unregisterInstance(inst)\n // Tag the root scope so the disposer log reports app-level\n // teardown distinct from in-tree component-unmount events.\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n container.textContent = ''\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"slice.d.ts","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAA2C,MAAM,aAAa,CAAA;AAChF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAS9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,EAChC,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;CAAE,EACpC,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG,GACrB,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAuDd"}
1
+ {"version":3,"file":"slice.d.ts","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAA2C,MAAM,aAAa,CAAA;AAChF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAU9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,EAChC,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;CAAE,EACpC,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG,GACrB,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CA2Dd"}
@@ -2,6 +2,7 @@ import { show as _show } from './show.js';
2
2
  import { branch as _branch } from './branch.js';
3
3
  import { each as _each } from './each.js';
4
4
  import { text as _text } from './text.js';
5
+ import { unsafeHtml as _unsafeHtml } from './unsafe-html.js';
5
6
  import { memo as _memo } from './memo.js';
6
7
  import { selector as _selector } from './selector.js';
7
8
  import { useContext } from './context.js';
@@ -57,6 +58,11 @@ export function slice(h, lift) {
57
58
  return _text(accessor);
58
59
  return _text((r) => accessor(lift(r)), mask);
59
60
  },
61
+ unsafeHtml: (accessor, mask) => {
62
+ if (typeof accessor === 'string')
63
+ return _unsafeHtml(accessor);
64
+ return _unsafeHtml((r) => accessor(lift(r)), mask);
65
+ },
60
66
  memo: (accessor) => {
61
67
  const m = _memo((r) => accessor(lift(r)));
62
68
  return (s) => m(s);
@@ -1 +1 @@
1
- {"version":3,"file":"slice.js","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send, EachOptions, ShowOptions, BranchOptions } from '../types.js'\nimport type { View } from '../view-helpers.js'\nimport { show as _show } from './show.js'\nimport { branch as _branch } from './branch.js'\nimport { each as _each } from './each.js'\nimport { text as _text } from './text.js'\nimport { memo as _memo } from './memo.js'\nimport { selector as _selector } from './selector.js'\nimport { useContext, type Context } from './context.js'\n\n/**\n * Build a `View<Sub, M>` that composes a selector into every state-bound\n * accessor. Used to write view-functions over a sub-slice of parent state:\n *\n * ```ts\n * import { slice } from '@llui/dom'\n *\n * view: (h) => {\n * const formView = slice(h, (s) => s.form)\n * return [...formView.show({ when: f => f.valid, render: (h) => [...] })]\n * }\n * ```\n *\n * Kept as a standalone function rather than a method on the View bundle so\n * apps that don't use it don't pay for its bundle cost — tree-shaken when\n * unused.\n */\nexport function slice<Root, Sub, M>(\n h: View<Root, M> | { send: Send<M> },\n lift: (r: Root) => Sub,\n): View<Sub, M> {\n const send = h.send\n\n // Wrap a Sub-typed case callback to work with the Root-typed primitive.\n // The inner callback receives a View<Root, M> from the primitive, and we\n // narrow it to View<Sub, M> via a recursive slice() call.\n const wrapCase =\n (fn: (h: View<Sub, M>) => Node[]) =>\n (rootH: View<Root, M>): Node[] =>\n fn(slice(rootH, lift))\n\n const wrapCases = (\n cases: Record<string | number, (h: View<Sub, M>) => Node[]>,\n ): Record<string | number, (h: View<Root, M>) => Node[]> => {\n const out: Record<string | number, (h: View<Root, M>) => Node[]> = {}\n for (const key of Object.keys(cases)) {\n out[key] = wrapCase(cases[key]!)\n }\n return out\n }\n\n return {\n send,\n show: (opts: ShowOptions<Sub, M>) =>\n _show<Root, M>({\n ...opts,\n when: (r) => opts.when(lift(r)),\n render: wrapCase(opts.render),\n fallback: opts.fallback ? wrapCase(opts.fallback) : undefined,\n }),\n branch: (opts: BranchOptions<Sub, M>) =>\n _branch<Root, M>({\n ...opts,\n on: (r) => opts.on(lift(r)),\n cases: wrapCases(opts.cases),\n }),\n each: <T>(opts: EachOptions<Sub, T, M>) =>\n _each<Root, T, M>({\n ...opts,\n items: (r) => opts.items(lift(r)),\n }),\n text: (accessor, mask) => {\n if (typeof accessor === 'string') return _text(accessor)\n return _text<Root>((r) => accessor(lift(r)), mask)\n },\n memo: <T>(accessor: (s: Sub) => T) => {\n const m = _memo<Root, T>((r) => accessor(lift(r)))\n return (s: Sub) => m(s as unknown as Root)\n },\n selector: <V>(field: (s: Sub) => V) => _selector<Root, V>((r) => field(lift(r))),\n ctx: <T>(c: Context<T>) => {\n const root = useContext<Root, T>(c)\n return (s: Sub) => root(s as unknown as Root)\n },\n }\n}\n"]}
1
+ {"version":3,"file":"slice.js","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send, EachOptions, ShowOptions, BranchOptions } from '../types.js'\nimport type { View } from '../view-helpers.js'\nimport { show as _show } from './show.js'\nimport { branch as _branch } from './branch.js'\nimport { each as _each } from './each.js'\nimport { text as _text } from './text.js'\nimport { unsafeHtml as _unsafeHtml } from './unsafe-html.js'\nimport { memo as _memo } from './memo.js'\nimport { selector as _selector } from './selector.js'\nimport { useContext, type Context } from './context.js'\n\n/**\n * Build a `View<Sub, M>` that composes a selector into every state-bound\n * accessor. Used to write view-functions over a sub-slice of parent state:\n *\n * ```ts\n * import { slice } from '@llui/dom'\n *\n * view: (h) => {\n * const formView = slice(h, (s) => s.form)\n * return [...formView.show({ when: f => f.valid, render: (h) => [...] })]\n * }\n * ```\n *\n * Kept as a standalone function rather than a method on the View bundle so\n * apps that don't use it don't pay for its bundle cost — tree-shaken when\n * unused.\n */\nexport function slice<Root, Sub, M>(\n h: View<Root, M> | { send: Send<M> },\n lift: (r: Root) => Sub,\n): View<Sub, M> {\n const send = h.send\n\n // Wrap a Sub-typed case callback to work with the Root-typed primitive.\n // The inner callback receives a View<Root, M> from the primitive, and we\n // narrow it to View<Sub, M> via a recursive slice() call.\n const wrapCase =\n (fn: (h: View<Sub, M>) => Node[]) =>\n (rootH: View<Root, M>): Node[] =>\n fn(slice(rootH, lift))\n\n const wrapCases = (\n cases: Record<string | number, (h: View<Sub, M>) => Node[]>,\n ): Record<string | number, (h: View<Root, M>) => Node[]> => {\n const out: Record<string | number, (h: View<Root, M>) => Node[]> = {}\n for (const key of Object.keys(cases)) {\n out[key] = wrapCase(cases[key]!)\n }\n return out\n }\n\n return {\n send,\n show: (opts: ShowOptions<Sub, M>) =>\n _show<Root, M>({\n ...opts,\n when: (r) => opts.when(lift(r)),\n render: wrapCase(opts.render),\n fallback: opts.fallback ? wrapCase(opts.fallback) : undefined,\n }),\n branch: (opts: BranchOptions<Sub, M>) =>\n _branch<Root, M>({\n ...opts,\n on: (r) => opts.on(lift(r)),\n cases: wrapCases(opts.cases),\n }),\n each: <T>(opts: EachOptions<Sub, T, M>) =>\n _each<Root, T, M>({\n ...opts,\n items: (r) => opts.items(lift(r)),\n }),\n text: (accessor, mask) => {\n if (typeof accessor === 'string') return _text(accessor)\n return _text<Root>((r) => accessor(lift(r)), mask)\n },\n unsafeHtml: (accessor, mask) => {\n if (typeof accessor === 'string') return _unsafeHtml(accessor)\n return _unsafeHtml<Root>((r) => accessor(lift(r)), mask)\n },\n memo: <T>(accessor: (s: Sub) => T) => {\n const m = _memo<Root, T>((r) => accessor(lift(r)))\n return (s: Sub) => m(s as unknown as Root)\n },\n selector: <V>(field: (s: Sub) => V) => _selector<Root, V>((r) => field(lift(r))),\n ctx: <T>(c: Context<T>) => {\n const root = useContext<Root, T>(c)\n return (s: Sub) => root(s as unknown as Root)\n },\n }\n}\n"]}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Insert raw HTML into the DOM tree.
3
+ *
4
+ * **Security.** The caller is responsible for sanitizing `html`. Never
5
+ * interpolate unsanitized user input — this is the XSS escape hatch and
6
+ * the name carries the warning deliberately.
7
+ *
8
+ * **Opaque to the framework.** The parsed subtree is treated as a black
9
+ * box by LLui — no bindings, events, `each`, `show`, or `child` inside
10
+ * it will be discovered or driven by the framework. Use element helpers
11
+ * for anything that needs to react to state or dispatch messages.
12
+ *
13
+ * **Static form.** `unsafeHtml('<b>hi</b>')` parses once at view time
14
+ * and returns the parsed nodes. Zero reactive overhead.
15
+ *
16
+ * **Reactive form.** `unsafeHtml((s) => s.markdownHtml)` registers a
17
+ * structural block that re-parses when the returned string differs
18
+ * (strict `===`) from the previous value. Unchanged strings short-
19
+ * circuit — existing DOM identity (focus, selection, listeners
20
+ * attached outside LLui) is preserved.
21
+ *
22
+ * **Mask hint.** The second parameter mirrors `text()`: the compiler
23
+ * passes a precise mask derived from which state fields the accessor
24
+ * reads. Phase 2 / Phase 1 skip the reconcile when no interesting bit
25
+ * is set. Defaults to FULL_MASK for hand-written components.
26
+ */
27
+ export declare function unsafeHtml<S>(accessor: ((s: S) => string) | string, mask?: number): Node[];
28
+ //# sourceMappingURL=unsafe-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unsafe-html.d.ts","sourceRoot":"","sources":["../../src/primitives/unsafe-html.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,CA8C1F"}
@@ -0,0 +1,82 @@
1
+ import { getRenderContext } from '../render-context.js';
2
+ import { FULL_MASK } from '../update-loop.js';
3
+ /**
4
+ * Insert raw HTML into the DOM tree.
5
+ *
6
+ * **Security.** The caller is responsible for sanitizing `html`. Never
7
+ * interpolate unsanitized user input — this is the XSS escape hatch and
8
+ * the name carries the warning deliberately.
9
+ *
10
+ * **Opaque to the framework.** The parsed subtree is treated as a black
11
+ * box by LLui — no bindings, events, `each`, `show`, or `child` inside
12
+ * it will be discovered or driven by the framework. Use element helpers
13
+ * for anything that needs to react to state or dispatch messages.
14
+ *
15
+ * **Static form.** `unsafeHtml('<b>hi</b>')` parses once at view time
16
+ * and returns the parsed nodes. Zero reactive overhead.
17
+ *
18
+ * **Reactive form.** `unsafeHtml((s) => s.markdownHtml)` registers a
19
+ * structural block that re-parses when the returned string differs
20
+ * (strict `===`) from the previous value. Unchanged strings short-
21
+ * circuit — existing DOM identity (focus, selection, listeners
22
+ * attached outside LLui) is preserved.
23
+ *
24
+ * **Mask hint.** The second parameter mirrors `text()`: the compiler
25
+ * passes a precise mask derived from which state fields the accessor
26
+ * reads. Phase 2 / Phase 1 skip the reconcile when no interesting bit
27
+ * is set. Defaults to FULL_MASK for hand-written components.
28
+ */
29
+ export function unsafeHtml(accessor, mask) {
30
+ if (typeof accessor === 'string') {
31
+ return parseHtmlToNodes(accessor);
32
+ }
33
+ const ctx = getRenderContext('unsafeHtml');
34
+ const blocks = ctx.structuralBlocks;
35
+ const blockMask = mask ?? FULL_MASK;
36
+ const anchor = document.createComment('unsafeHtml');
37
+ let currentHtml = accessor(ctx.state);
38
+ let currentNodes = parseHtmlToNodes(currentHtml);
39
+ const block = {
40
+ mask: blockMask,
41
+ reconcile(state) {
42
+ const newHtml = accessor(state);
43
+ // Identity short-circuit — don't rebuild the subtree when the
44
+ // author handed us the same string. Preserves focus, selection,
45
+ // and any listeners outside LLui's view of the world.
46
+ if (newHtml === currentHtml)
47
+ return;
48
+ const parent = anchor.parentNode;
49
+ if (!parent)
50
+ return;
51
+ const oldNodes = currentNodes;
52
+ currentNodes = parseHtmlToNodes(newHtml);
53
+ currentHtml = newHtml;
54
+ // The anchor sits immediately before the current node range, so
55
+ // `anchor.nextSibling` is the first old node (or whatever follows
56
+ // when the old list is empty). Insert new nodes there, then
57
+ // remove the old ones — matches `branch()`'s enter/leave order so
58
+ // transitions can inspect both sets.
59
+ const ref = anchor.nextSibling;
60
+ for (const node of currentNodes) {
61
+ parent.insertBefore(node, ref);
62
+ }
63
+ for (const node of oldNodes) {
64
+ if (node.parentNode)
65
+ node.parentNode.removeChild(node);
66
+ }
67
+ },
68
+ };
69
+ blocks.push(block);
70
+ return [anchor, ...currentNodes];
71
+ }
72
+ function parseHtmlToNodes(html) {
73
+ // `<template>` parses into an inert DocumentFragment without running
74
+ // scripts, resolving images, or firing connection callbacks. The
75
+ // childNodes live reference is stable until we move the nodes out.
76
+ const template = document.createElement('template');
77
+ template.innerHTML = html;
78
+ // Snapshot — moving nodes to a parent drains template.content, but
79
+ // callers hold this array and we iterate it on reconcile.
80
+ return Array.from(template.content.childNodes);
81
+ }
82
+ //# sourceMappingURL=unsafe-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unsafe-html.js","sourceRoot":"","sources":["../../src/primitives/unsafe-html.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,UAAU,CAAI,QAAqC,EAAE,IAAa;IAChF,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACnC,CAAC;IAED,MAAM,GAAG,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAC1C,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAA;IACnC,MAAM,SAAS,GAAG,IAAI,IAAI,SAAS,CAAA;IAEnC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAA;IACnD,IAAI,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAU,CAAC,CAAA;IAC1C,IAAI,YAAY,GAAW,gBAAgB,CAAC,WAAW,CAAC,CAAA;IAExD,MAAM,KAAK,GAAoB;QAC7B,IAAI,EAAE,SAAS;QACf,SAAS,CAAC,KAAc;YACtB,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAU,CAAC,CAAA;YACpC,8DAA8D;YAC9D,gEAAgE;YAChE,sDAAsD;YACtD,IAAI,OAAO,KAAK,WAAW;gBAAE,OAAM;YAEnC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAA;YAChC,IAAI,CAAC,MAAM;gBAAE,OAAM;YAEnB,MAAM,QAAQ,GAAG,YAAY,CAAA;YAC7B,YAAY,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;YACxC,WAAW,GAAG,OAAO,CAAA;YAErB,gEAAgE;YAChE,kEAAkE;YAClE,4DAA4D;YAC5D,kEAAkE;YAClE,qCAAqC;YACrC,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAA;YAC9B,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;gBAChC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;YAChC,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;gBAC5B,IAAI,IAAI,CAAC,UAAU;oBAAE,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;YACxD,CAAC;QACH,CAAC;KACF,CAAA;IACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAElB,OAAO,CAAC,MAAM,EAAE,GAAG,YAAY,CAAC,CAAA;AAClC,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,qEAAqE;IACrE,iEAAiE;IACjE,mEAAmE;IACnE,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;IACnD,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAA;IACzB,mEAAmE;IACnE,0DAA0D;IAC1D,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;AAChD,CAAC","sourcesContent":["import type { StructuralBlock } from '../structural.js'\nimport { getRenderContext } from '../render-context.js'\nimport { FULL_MASK } from '../update-loop.js'\n\n/**\n * Insert raw HTML into the DOM tree.\n *\n * **Security.** The caller is responsible for sanitizing `html`. Never\n * interpolate unsanitized user input — this is the XSS escape hatch and\n * the name carries the warning deliberately.\n *\n * **Opaque to the framework.** The parsed subtree is treated as a black\n * box by LLui — no bindings, events, `each`, `show`, or `child` inside\n * it will be discovered or driven by the framework. Use element helpers\n * for anything that needs to react to state or dispatch messages.\n *\n * **Static form.** `unsafeHtml('<b>hi</b>')` parses once at view time\n * and returns the parsed nodes. Zero reactive overhead.\n *\n * **Reactive form.** `unsafeHtml((s) => s.markdownHtml)` registers a\n * structural block that re-parses when the returned string differs\n * (strict `===`) from the previous value. Unchanged strings short-\n * circuit — existing DOM identity (focus, selection, listeners\n * attached outside LLui) is preserved.\n *\n * **Mask hint.** The second parameter mirrors `text()`: the compiler\n * passes a precise mask derived from which state fields the accessor\n * reads. Phase 2 / Phase 1 skip the reconcile when no interesting bit\n * is set. Defaults to FULL_MASK for hand-written components.\n */\nexport function unsafeHtml<S>(accessor: ((s: S) => string) | string, mask?: number): Node[] {\n if (typeof accessor === 'string') {\n return parseHtmlToNodes(accessor)\n }\n\n const ctx = getRenderContext('unsafeHtml')\n const blocks = ctx.structuralBlocks\n const blockMask = mask ?? FULL_MASK\n\n const anchor = document.createComment('unsafeHtml')\n let currentHtml = accessor(ctx.state as S)\n let currentNodes: Node[] = parseHtmlToNodes(currentHtml)\n\n const block: StructuralBlock = {\n mask: blockMask,\n reconcile(state: unknown) {\n const newHtml = accessor(state as S)\n // Identity short-circuit — don't rebuild the subtree when the\n // author handed us the same string. Preserves focus, selection,\n // and any listeners outside LLui's view of the world.\n if (newHtml === currentHtml) return\n\n const parent = anchor.parentNode\n if (!parent) return\n\n const oldNodes = currentNodes\n currentNodes = parseHtmlToNodes(newHtml)\n currentHtml = newHtml\n\n // The anchor sits immediately before the current node range, so\n // `anchor.nextSibling` is the first old node (or whatever follows\n // when the old list is empty). Insert new nodes there, then\n // remove the old ones — matches `branch()`'s enter/leave order so\n // transitions can inspect both sets.\n const ref = anchor.nextSibling\n for (const node of currentNodes) {\n parent.insertBefore(node, ref)\n }\n for (const node of oldNodes) {\n if (node.parentNode) node.parentNode.removeChild(node)\n }\n },\n }\n blocks.push(block)\n\n return [anchor, ...currentNodes]\n}\n\nfunction parseHtmlToNodes(html: string): Node[] {\n // `<template>` parses into an inert DocumentFragment without running\n // scripts, resolving images, or firing connection callbacks. The\n // childNodes live reference is stable until we move the nodes out.\n const template = document.createElement('template')\n template.innerHTML = html\n // Snapshot — moving nodes to a parent drains template.content, but\n // callers hold this array and we iterate it on reconcile.\n return Array.from(template.content.childNodes)\n}\n"]}
@@ -32,6 +32,12 @@ export interface View<S, M> {
32
32
  branch(opts: BranchOptions<S, M>): Node[];
33
33
  each<T>(opts: EachOptions<S, T, M>): Node[];
34
34
  text(accessor: ((s: S) => string) | string, mask?: number): Text;
35
+ /**
36
+ * Insert raw HTML into the tree. Caller is responsible for sanitizing.
37
+ * The parsed subtree is opaque to LLui — no nested bindings, events,
38
+ * or primitives inside it will be tracked. See `unsafeHtml` for details.
39
+ */
40
+ unsafeHtml(accessor: ((s: S) => string) | string, mask?: number): Node[];
35
41
  memo<T>(accessor: (s: S) => T): (s: S) => T;
36
42
  selector<V>(field: (s: S) => V): SelectorInstance<V>;
37
43
  ctx<T>(c: Context<T>): (s: S) => T;
@@ -1 +1 @@
1
- {"version":3,"file":"view-helpers.d.ts","sourceRoot":"","sources":["../src/view-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAM/E,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AACvF,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAElE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC;IACxB,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;IACb,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IACrC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IACzC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChE,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;IAC3C,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAA;IACpD,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;CACnC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAY1D"}
1
+ {"version":3,"file":"view-helpers.d.ts","sourceRoot":"","sources":["../src/view-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAO/E,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AACvF,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAElE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC;IACxB,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;IACb,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IACrC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IACzC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChE;;;;OAIG;IACH,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,CAAA;IACxE,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;IAC3C,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAA;IACpD,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;CACnC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAc1D"}
@@ -2,6 +2,7 @@ import { show as _show } from './primitives/show.js';
2
2
  import { branch as _branch } from './primitives/branch.js';
3
3
  import { each as _each } from './primitives/each.js';
4
4
  import { text as _text } from './primitives/text.js';
5
+ import { unsafeHtml as _unsafeHtml } from './primitives/unsafe-html.js';
5
6
  import { memo as _memo } from './primitives/memo.js';
6
7
  import { selector as _selector } from './primitives/selector.js';
7
8
  import { useContext } from './primitives/context.js';
@@ -16,6 +17,7 @@ export function createView(send) {
16
17
  branch: (opts) => _branch(opts),
17
18
  each: (opts) => _each(opts),
18
19
  text: (accessor, mask) => typeof accessor === 'string' ? _text(accessor) : _text(accessor, mask),
20
+ unsafeHtml: (accessor, mask) => typeof accessor === 'string' ? _unsafeHtml(accessor) : _unsafeHtml(accessor, mask),
19
21
  memo: (accessor) => _memo(accessor),
20
22
  selector: (field) => _selector(field),
21
23
  ctx: (c) => useContext(c),
@@ -1 +1 @@
1
- {"version":3,"file":"view-helpers.js","sourceRoot":"","sources":["../src/view-helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,QAAQ,IAAI,SAAS,EAAyB,MAAM,0BAA0B,CAAA;AACvF,OAAO,EAAE,UAAU,EAAgB,MAAM,yBAAyB,CAAA;AAsClE;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAO,IAAa;IAC5C,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAO,IAAI,CAAC;QACjC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAO,IAAI,CAAC;QACrC,IAAI,EAAE,CAAI,IAA0B,EAAE,EAAE,CAAC,KAAK,CAAU,IAAI,CAAC;QAC7D,IAAI,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CACvB,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAI,QAAQ,EAAE,IAAI,CAAC;QAC3E,IAAI,EAAE,CAAI,QAAqB,EAAE,EAAE,CAAC,KAAK,CAAO,QAAQ,CAAC;QACzD,QAAQ,EAAE,CAAI,KAAkB,EAAE,EAAE,CAAC,SAAS,CAAO,KAAK,CAAC;QAC3D,GAAG,EAAE,CAAI,CAAa,EAAE,EAAE,CAAC,UAAU,CAAO,CAAC,CAAC;KAC/C,CAAA;AACH,CAAC","sourcesContent":["import type { Send, ShowOptions, BranchOptions, EachOptions } from './types.js'\nimport { show as _show } from './primitives/show.js'\nimport { branch as _branch } from './primitives/branch.js'\nimport { each as _each } from './primitives/each.js'\nimport { text as _text } from './primitives/text.js'\nimport { memo as _memo } from './primitives/memo.js'\nimport { selector as _selector, type SelectorInstance } from './primitives/selector.js'\nimport { useContext, type Context } from './primitives/context.js'\n\n/**\n * Typed view helpers bound to a component's `State` / `Msg`. The sole\n * argument to `view`, so every state-bound primitive infers `State` from\n * the component definition — no per-call `show<State>(...)` annotation.\n *\n * ```ts\n * view: ({ send, show, text }) => [\n * ...show({ when: s => s.count > 0, render: () => [...] }),\n * text(s => String(s.count)),\n * ]\n * ```\n *\n * Tip: to view-function over a sub-slice of parent state, import `slice`\n * as a standalone helper:\n *\n * ```ts\n * import { slice } from '@llui/dom'\n * const form = slice(h, s => s.form) // returns View<FormState, Msg>\n * ```\n *\n * The Vite plugin's mask-injection pass recognizes all three call forms\n * equivalently: `h.text(...)` (member expression), `text(...)` (destructured\n * alias), and `text(...)` (bare import from `@llui/dom`). No per-binding\n * gating is lost when calling through `h`.\n */\nexport interface View<S, M> {\n send: Send<M>\n show(opts: ShowOptions<S, M>): Node[]\n branch(opts: BranchOptions<S, M>): Node[]\n each<T>(opts: EachOptions<S, T, M>): Node[]\n text(accessor: ((s: S) => string) | string, mask?: number): Text\n memo<T>(accessor: (s: S) => T): (s: S) => T\n selector<V>(field: (s: S) => V): SelectorInstance<V>\n ctx<T>(c: Context<T>): (s: S) => T\n}\n\n/**\n * Create a `View<S, M>` bundle for a component's `view` callback.\n * Delegates straight to the underlying primitives — zero per-call overhead.\n */\nexport function createView<S, M>(send: Send<M>): View<S, M> {\n return {\n send,\n show: (opts) => _show<S, M>(opts),\n branch: (opts) => _branch<S, M>(opts),\n each: <T>(opts: EachOptions<S, T, M>) => _each<S, T, M>(opts),\n text: (accessor, mask) =>\n typeof accessor === 'string' ? _text(accessor) : _text<S>(accessor, mask),\n memo: <T>(accessor: (s: S) => T) => _memo<S, T>(accessor),\n selector: <V>(field: (s: S) => V) => _selector<S, V>(field),\n ctx: <T>(c: Context<T>) => useContext<S, T>(c),\n }\n}\n"]}
1
+ {"version":3,"file":"view-helpers.js","sourceRoot":"","sources":["../src/view-helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,UAAU,IAAI,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACvE,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,QAAQ,IAAI,SAAS,EAAyB,MAAM,0BAA0B,CAAA;AACvF,OAAO,EAAE,UAAU,EAAgB,MAAM,yBAAyB,CAAA;AA4ClE;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAO,IAAa;IAC5C,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAO,IAAI,CAAC;QACjC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAO,IAAI,CAAC;QACrC,IAAI,EAAE,CAAI,IAA0B,EAAE,EAAE,CAAC,KAAK,CAAU,IAAI,CAAC;QAC7D,IAAI,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CACvB,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAI,QAAQ,EAAE,IAAI,CAAC;QAC3E,UAAU,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAC7B,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAI,QAAQ,EAAE,IAAI,CAAC;QACvF,IAAI,EAAE,CAAI,QAAqB,EAAE,EAAE,CAAC,KAAK,CAAO,QAAQ,CAAC;QACzD,QAAQ,EAAE,CAAI,KAAkB,EAAE,EAAE,CAAC,SAAS,CAAO,KAAK,CAAC;QAC3D,GAAG,EAAE,CAAI,CAAa,EAAE,EAAE,CAAC,UAAU,CAAO,CAAC,CAAC;KAC/C,CAAA;AACH,CAAC","sourcesContent":["import type { Send, ShowOptions, BranchOptions, EachOptions } from './types.js'\nimport { show as _show } from './primitives/show.js'\nimport { branch as _branch } from './primitives/branch.js'\nimport { each as _each } from './primitives/each.js'\nimport { text as _text } from './primitives/text.js'\nimport { unsafeHtml as _unsafeHtml } from './primitives/unsafe-html.js'\nimport { memo as _memo } from './primitives/memo.js'\nimport { selector as _selector, type SelectorInstance } from './primitives/selector.js'\nimport { useContext, type Context } from './primitives/context.js'\n\n/**\n * Typed view helpers bound to a component's `State` / `Msg`. The sole\n * argument to `view`, so every state-bound primitive infers `State` from\n * the component definition — no per-call `show<State>(...)` annotation.\n *\n * ```ts\n * view: ({ send, show, text }) => [\n * ...show({ when: s => s.count > 0, render: () => [...] }),\n * text(s => String(s.count)),\n * ]\n * ```\n *\n * Tip: to view-function over a sub-slice of parent state, import `slice`\n * as a standalone helper:\n *\n * ```ts\n * import { slice } from '@llui/dom'\n * const form = slice(h, s => s.form) // returns View<FormState, Msg>\n * ```\n *\n * The Vite plugin's mask-injection pass recognizes all three call forms\n * equivalently: `h.text(...)` (member expression), `text(...)` (destructured\n * alias), and `text(...)` (bare import from `@llui/dom`). No per-binding\n * gating is lost when calling through `h`.\n */\nexport interface View<S, M> {\n send: Send<M>\n show(opts: ShowOptions<S, M>): Node[]\n branch(opts: BranchOptions<S, M>): Node[]\n each<T>(opts: EachOptions<S, T, M>): Node[]\n text(accessor: ((s: S) => string) | string, mask?: number): Text\n /**\n * Insert raw HTML into the tree. Caller is responsible for sanitizing.\n * The parsed subtree is opaque to LLui — no nested bindings, events,\n * or primitives inside it will be tracked. See `unsafeHtml` for details.\n */\n unsafeHtml(accessor: ((s: S) => string) | string, mask?: number): Node[]\n memo<T>(accessor: (s: S) => T): (s: S) => T\n selector<V>(field: (s: S) => V): SelectorInstance<V>\n ctx<T>(c: Context<T>): (s: S) => T\n}\n\n/**\n * Create a `View<S, M>` bundle for a component's `view` callback.\n * Delegates straight to the underlying primitives — zero per-call overhead.\n */\nexport function createView<S, M>(send: Send<M>): View<S, M> {\n return {\n send,\n show: (opts) => _show<S, M>(opts),\n branch: (opts) => _branch<S, M>(opts),\n each: <T>(opts: EachOptions<S, T, M>) => _each<S, T, M>(opts),\n text: (accessor, mask) =>\n typeof accessor === 'string' ? _text(accessor) : _text<S>(accessor, mask),\n unsafeHtml: (accessor, mask) =>\n typeof accessor === 'string' ? _unsafeHtml(accessor) : _unsafeHtml<S>(accessor, mask),\n memo: <T>(accessor: (s: S) => T) => _memo<S, T>(accessor),\n selector: <V>(field: (s: S) => V) => _selector<S, V>(field),\n ctx: <T>(c: Context<T>) => useContext<S, T>(c),\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llui/dom",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {