@streetui/dom 1.6.0 → 1.7.0

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/dist/index.cjs CHANGED
@@ -26,16 +26,24 @@ __export(index_exports, {
26
26
  ServerDOMAdapter: () => ServerDOMAdapter,
27
27
  ServerElement: () => ServerElement,
28
28
  ServerFragment: () => ServerFragment,
29
+ ServerRawHTML: () => ServerRawHTML,
29
30
  ServerStyle: () => ServerStyle,
30
31
  ServerText: () => ServerText,
31
32
  browserDOMAdapter: () => browserDOMAdapter,
33
+ containFocus: () => containFocus,
32
34
  escapeHtmlAttr: () => escapeHtmlAttr,
33
35
  escapeHtmlText: () => escapeHtmlText,
34
36
  focusById: () => focusById,
35
37
  focusFirst: () => focusFirst,
38
+ focusInitial: () => focusInitial,
39
+ getFocusable: () => getFocusable,
40
+ onEscape: () => onEscape,
41
+ restoreFocus: () => restoreFocus,
42
+ saveFocus: () => saveFocus,
36
43
  serializeChildren: () => serializeChildren,
37
44
  serializeServerNode: () => serializeServerNode,
38
- serverDOMAdapter: () => serverDOMAdapter
45
+ serverDOMAdapter: () => serverDOMAdapter,
46
+ trapFocus: () => trapFocus
39
47
  });
40
48
  module.exports = __toCommonJS(index_exports);
41
49
 
@@ -104,6 +112,18 @@ var BrowserDOMAdapter = class {
104
112
  focus(element) {
105
113
  element.focus?.();
106
114
  }
115
+ body() {
116
+ return document.body ?? null;
117
+ }
118
+ activeElement() {
119
+ return document.activeElement ?? null;
120
+ }
121
+ contains(ancestor, node) {
122
+ return ancestor.contains(node);
123
+ }
124
+ matches(element, selector) {
125
+ return typeof element.matches === "function" && element.matches(selector);
126
+ }
107
127
  isElement(node) {
108
128
  return node.nodeType === Node.ELEMENT_NODE;
109
129
  }
@@ -162,6 +182,14 @@ var ServerFragment = class {
162
182
  parent = null;
163
183
  children = [];
164
184
  };
185
+ var ServerRawHTML = class {
186
+ kind = "raw";
187
+ parent = null;
188
+ html;
189
+ constructor(html) {
190
+ this.html = html;
191
+ }
192
+ };
165
193
  var ServerElement = class {
166
194
  kind = "element";
167
195
  parent = null;
@@ -312,6 +340,8 @@ function serializeServerNode(node) {
312
340
  return `<!--${node.data}-->`;
313
341
  case "fragment":
314
342
  return serializeChildren(node);
343
+ case "raw":
344
+ return node.html;
315
345
  case "element": {
316
346
  const el = node;
317
347
  const tag = el.tagName;
@@ -351,6 +381,15 @@ var ServerDOMAdapter = class {
351
381
  createFragment() {
352
382
  return new ServerFragment();
353
383
  }
384
+ /**
385
+ * Create a verbatim pre-serialized HTML node (v1.7 static SSR plan, §6).
386
+ * Server-only: the browser adapter does not implement this, and the renderer
387
+ * fast path only invokes it when a static SSR plan is present (SSR). The
388
+ * stored HTML was produced by this same serializer, so it is emitted as-is.
389
+ */
390
+ createRawHTML(html) {
391
+ return new ServerRawHTML(html);
392
+ }
354
393
  appendChild(parent, child) {
355
394
  const p = asParent(parent);
356
395
  const c = asServer(child);
@@ -452,6 +491,18 @@ var ServerDOMAdapter = class {
452
491
  }
453
492
  focus() {
454
493
  }
494
+ body() {
495
+ return null;
496
+ }
497
+ activeElement() {
498
+ return null;
499
+ }
500
+ contains(_ancestor, _node) {
501
+ return false;
502
+ }
503
+ matches(_element, _selector) {
504
+ return false;
505
+ }
455
506
  isElement(node) {
456
507
  return asServer(node).kind === "element";
457
508
  }
@@ -517,6 +568,66 @@ function focusFirst(dom, container, selector = FOCUSABLE_SELECTOR) {
517
568
  dom.focus(el);
518
569
  return true;
519
570
  }
571
+ function getFocusable(dom, container, selector = FOCUSABLE_SELECTOR) {
572
+ return Array.from(dom.querySelectorAll(container, selector)).filter(
573
+ (el) => dom.matches(el, selector)
574
+ );
575
+ }
576
+ function saveFocus(dom) {
577
+ return dom.activeElement();
578
+ }
579
+ function restoreFocus(dom, saved) {
580
+ if (saved !== null) dom.focus(saved);
581
+ }
582
+ function focusInitial(dom, container, initialFocusId) {
583
+ if (initialFocusId !== void 0 && focusById(dom, container, initialFocusId)) return;
584
+ focusFirst(dom, container);
585
+ }
586
+ function trapFocus(dom, container) {
587
+ const onKeydown = (event) => {
588
+ if (event.key !== "Tab") return;
589
+ const items = getFocusable(dom, container);
590
+ if (items.length === 0) {
591
+ event.preventDefault();
592
+ return;
593
+ }
594
+ const first = items[0];
595
+ const last = items[items.length - 1];
596
+ const active = dom.activeElement();
597
+ if (active === null || !dom.contains(container, active)) {
598
+ event.preventDefault();
599
+ dom.focus(first);
600
+ } else if (event.shiftKey && active === first) {
601
+ event.preventDefault();
602
+ dom.focus(last);
603
+ } else if (!event.shiftKey && active === last) {
604
+ event.preventDefault();
605
+ dom.focus(first);
606
+ }
607
+ };
608
+ dom.addEventListener(container, "keydown", onKeydown);
609
+ return () => dom.removeEventListener(container, "keydown", onKeydown);
610
+ }
611
+ function containFocus(dom, container) {
612
+ const body = dom.body();
613
+ if (body === null) return () => {
614
+ };
615
+ const onFocusIn = (event) => {
616
+ const target = event.target;
617
+ if (target !== null && !dom.contains(container, target)) {
618
+ focusFirst(dom, container);
619
+ }
620
+ };
621
+ dom.addEventListener(body, "focusin", onFocusIn);
622
+ return () => dom.removeEventListener(body, "focusin", onFocusIn);
623
+ }
624
+ function onEscape(dom, target, handler) {
625
+ const onKeydown = (event) => {
626
+ if (event.key === "Escape") handler();
627
+ };
628
+ dom.addEventListener(target, "keydown", onKeydown);
629
+ return () => dom.removeEventListener(target, "keydown", onKeydown);
630
+ }
520
631
  // Annotate the CommonJS export names for ESM import in node:
521
632
  0 && (module.exports = {
522
633
  BrowserDOMAdapter,
@@ -525,15 +636,23 @@ function focusFirst(dom, container, selector = FOCUSABLE_SELECTOR) {
525
636
  ServerDOMAdapter,
526
637
  ServerElement,
527
638
  ServerFragment,
639
+ ServerRawHTML,
528
640
  ServerStyle,
529
641
  ServerText,
530
642
  browserDOMAdapter,
643
+ containFocus,
531
644
  escapeHtmlAttr,
532
645
  escapeHtmlText,
533
646
  focusById,
534
647
  focusFirst,
648
+ focusInitial,
649
+ getFocusable,
650
+ onEscape,
651
+ restoreFocus,
652
+ saveFocus,
535
653
  serializeChildren,
536
654
  serializeServerNode,
537
- serverDOMAdapter
655
+ serverDOMAdapter,
656
+ trapFocus
538
657
  });
539
658
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/browser-adapter.ts","../src/server-node.ts","../src/server-adapter.ts","../src/focus.ts"],"sourcesContent":["export * from './adapter.js';\nexport * from './browser-adapter.js';\nexport * from './server-node.js';\nexport * from './server-adapter.js';\nexport * from './focus.js';\n","/**\n * Browser implementation of DOMAdapter — delegates directly to browser APIs.\n */\n\nimport type { DOMAdapter } from './adapter.js';\n\nexport class BrowserDOMAdapter implements DOMAdapter {\n createElement(tag: string, ns?: string): Element {\n if (ns !== undefined) {\n return document.createElementNS(ns, tag);\n }\n return document.createElement(tag);\n }\n\n createTextNode(data: string): Text {\n return document.createTextNode(data);\n }\n\n createComment(data: string): Comment {\n return document.createComment(data);\n }\n\n createFragment(): DocumentFragment {\n return document.createDocumentFragment();\n }\n\n appendChild(parent: Node, child: Node): void {\n parent.appendChild(child);\n }\n\n insertBefore(parent: Node, child: Node, reference: Node | null): void {\n parent.insertBefore(child, reference);\n }\n\n removeChild(parent: Node, child: Node): void {\n parent.removeChild(child);\n }\n\n replaceChild(parent: Node, newChild: Node, oldChild: Node): void {\n parent.replaceChild(newChild, oldChild);\n }\n\n setAttribute(element: Element, name: string, value: string): void {\n element.setAttribute(name, value);\n }\n\n removeAttribute(element: Element, name: string): void {\n element.removeAttribute(name);\n }\n\n getAttribute(element: Element, name: string): string | null {\n return element.getAttribute(name);\n }\n\n setProperty(element: Element, name: string, value: unknown): void {\n (element as unknown as Record<string, unknown>)[name] = value;\n }\n\n setTextContent(node: Node, text: string): void {\n node.textContent = text;\n }\n\n getTextContent(node: Node): string | null {\n return node.textContent;\n }\n\n addEventListener(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n ): void {\n target.addEventListener(type, handler, options);\n }\n\n removeEventListener(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: EventListenerOptions,\n ): void {\n target.removeEventListener(type, handler, options);\n }\n\n querySelector(root: Element | Document, selector: string): Element | null {\n return root.querySelector(selector);\n }\n\n querySelectorAll(root: Element | Document, selector: string): NodeListOf<Element> {\n return root.querySelectorAll(selector);\n }\n\n getElementById(id: string): Element | null {\n return document.getElementById(id);\n }\n\n focus(element: Element): void {\n (element as unknown as { focus?: () => void }).focus?.();\n }\n\n isElement(node: Node): node is Element {\n return node.nodeType === Node.ELEMENT_NODE;\n }\n\n isTextNode(node: Node): node is Text {\n return node.nodeType === Node.TEXT_NODE;\n }\n\n tagName(element: Element): string {\n return element.tagName.toLowerCase();\n }\n\n parentNode(node: Node): Node | null {\n return node.parentNode;\n }\n\n nextSibling(node: Node): Node | null {\n return node.nextSibling;\n }\n\n firstChild(node: Node): Node | null {\n return node.firstChild;\n }\n\n childNodes(node: Node): Node[] {\n return Array.from(node.childNodes);\n }\n}\n\n// `/* @__PURE__ */`: convenience singleton, unreferenced by internal runtime\n// paths. Marking construction pure lets bundlers drop it when unused instead of\n// retaining it (and the BrowserDOMAdapter class) as an import-time side effect.\nexport const browserDOMAdapter = /* @__PURE__ */ new BrowserDOMAdapter();\n","/**\n * Server-side DOM node model.\n *\n * A tiny, dependency-free tree of plain objects that mirrors just enough of the\n * browser DOM for StreetUI's renderer to build a tree on the server and\n * serialize it to an HTML string. There is NO browser global here — these are\n * ordinary classes usable in any JavaScript environment (Node, workers, tests).\n *\n * The renderer never touches these types directly; it goes through the\n * `DOMAdapter` interface, and `ServerDOMAdapter` translates adapter calls into\n * operations on this model.\n */\n\nexport type ServerNodeKind = 'element' | 'text' | 'comment' | 'fragment';\n\nexport interface ServerNode {\n readonly kind: ServerNodeKind;\n parent: ServerParent | null;\n}\n\nexport type ServerParent = ServerElement | ServerFragment;\n\n/** A minimal inline-style holder mirroring `element.style.setProperty`. */\nexport class ServerStyle {\n readonly declarations = new Map<string, string>();\n setProperty(name: string, value: string): void {\n this.declarations.set(name, value);\n }\n get isEmpty(): boolean {\n return this.declarations.size === 0;\n }\n toCss(): string {\n return [...this.declarations.entries()].map(([k, v]) => `${k}: ${v}`).join('; ');\n }\n}\n\nexport class ServerText implements ServerNode {\n readonly kind = 'text' as const;\n parent: ServerParent | null = null;\n data: string;\n constructor(data: string) {\n this.data = data;\n }\n}\n\nexport class ServerComment implements ServerNode {\n readonly kind = 'comment' as const;\n parent: ServerParent | null = null;\n data: string;\n constructor(data: string) {\n this.data = data;\n }\n}\n\nexport class ServerFragment implements ServerNode {\n readonly kind = 'fragment' as const;\n parent: ServerParent | null = null;\n readonly children: ServerNode[] = [];\n}\n\nexport class ServerElement implements ServerNode {\n readonly kind = 'element' as const;\n parent: ServerParent | null = null;\n readonly tagName: string;\n readonly attributes = new Map<string, string>();\n readonly children: ServerNode[] = [];\n\n // Lazily-allocated stores. On the 10k-row SSR corpus ~0% of elements carry JS\n // properties or inline styles (measured, §5: 1 of 80,029 elements uses\n // `properties`, 0 use `style`), so eagerly allocating a `properties` Map plus\n // a `ServerStyle` (which itself holds a Map) per element wasted ~240k\n // allocations per /users render — all in the dominant mount phase. These are\n // created on first WRITE via the `properties`/`style` getters; the serializer\n // reads the raw `_properties`/`_style` fields so a READ never forces an\n // allocation. Output is byte-identical: an unset store previously serialized\n // to nothing (empty `properties.has(...)` / `style.isEmpty`), and a null store\n // is skipped the same way.\n _properties: Map<string, unknown> | null = null;\n _style: ServerStyle | null = null;\n\n constructor(tagName: string) {\n this.tagName = tagName.toLowerCase();\n }\n\n /** JS properties set via `setProperty` (e.g. input `value`, `checked`). Allocated on first access. */\n get properties(): Map<string, unknown> {\n return (this._properties ??= new Map());\n }\n\n /** Inline-style holder mirroring `element.style`. Allocated on first access. */\n get style(): ServerStyle {\n return (this._style ??= new ServerStyle());\n }\n}\n\n// ── HTML serialization ─────────────────────────────────────────────────────────\n\n/**\n * HTML \"void\" elements — self-closing, never given a closing tag or children.\n */\nconst VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n/**\n * Element properties that should be reflected into the serialized HTML so the\n * hydrated DOM carries the same initial state. `value`/`checked` matter for\n * form controls whose live state is a JS property, not an attribute.\n */\nconst SERIALIZED_PROPERTIES: Record<string, 'attr' | 'boolean'> = {\n value: 'attr',\n checked: 'boolean',\n selected: 'boolean',\n};\n\n/**\n * Precomputed `[name, kind]` pairs of SERIALIZED_PROPERTIES. Hoisted to module\n * scope so `serializeAttributes` does not allocate a fresh entries array for\n * every element serialized (measured hot: ~80k elements on the 10k-row route).\n */\nconst SERIALIZED_PROPERTY_ENTRIES: ReadonlyArray<readonly [string, 'attr' | 'boolean']> =\n Object.entries(SERIALIZED_PROPERTIES) as Array<[string, 'attr' | 'boolean']>;\n\n// Fast-path escaping. The chained `.replace(/…/g, …)` form makes 3–4 full\n// passes and allocates an intermediate string per pass even when nothing needs\n// escaping. These variants scan once and, in the overwhelmingly common case of\n// no special character, return the input unchanged (zero allocation). Output is\n// byte-identical to the chained form (verified over the real SSR corpus).\nconst TEXT_SPECIAL = /[&<>]/;\nconst ATTR_SPECIAL = /[&<>\"]/;\n\n/** Escape text node content. */\nexport function escapeHtmlText(value: string): string {\n if (!TEXT_SPECIAL.test(value)) return value;\n let out = '';\n let last = 0;\n for (let i = 0; i < value.length; i++) {\n let esc: string;\n switch (value.charCodeAt(i)) {\n case 38: esc = '&amp;'; break; // &\n case 60: esc = '&lt;'; break; // <\n case 62: esc = '&gt;'; break; // >\n default: continue;\n }\n out += value.slice(last, i) + esc;\n last = i + 1;\n }\n return out + value.slice(last);\n}\n\n/** Escape a double-quoted attribute value. */\nexport function escapeHtmlAttr(value: string): string {\n if (!ATTR_SPECIAL.test(value)) return value;\n let out = '';\n let last = 0;\n for (let i = 0; i < value.length; i++) {\n let esc: string;\n switch (value.charCodeAt(i)) {\n case 38: esc = '&amp;'; break; // &\n case 60: esc = '&lt;'; break; // <\n case 62: esc = '&gt;'; break; // >\n case 34: esc = '&quot;'; break; // \"\n default: continue;\n }\n out += value.slice(last, i) + esc;\n last = i + 1;\n }\n return out + value.slice(last);\n}\n\nfunction serializeAttributes(el: ServerElement): string {\n const parts: string[] = [];\n\n for (const [name, value] of el.attributes) {\n if (value === '') {\n parts.push(` ${name}`);\n } else {\n parts.push(` ${name}=\"${escapeHtmlAttr(value)}\"`);\n }\n }\n\n // Read the raw backing field (may be null): most elements have no JS\n // properties, so skipping the whole loop avoids touching a store that was\n // never allocated (§5).\n const props = el._properties;\n if (props !== null) {\n for (const [name, kind] of SERIALIZED_PROPERTY_ENTRIES) {\n if (!props.has(name)) continue;\n if (el.attributes.has(name)) continue; // an explicit attribute already won\n const raw = props.get(name);\n if (kind === 'boolean') {\n if (raw === true) parts.push(` ${name}`);\n } else {\n if (raw !== undefined && raw !== null) {\n parts.push(` ${name}=\"${escapeHtmlAttr(String(raw))}\"`);\n }\n }\n }\n }\n\n const style = el._style;\n if (style !== null && !style.isEmpty && !el.attributes.has('style')) {\n parts.push(` style=\"${escapeHtmlAttr(style.toCss())}\"`);\n }\n\n return parts.join('');\n}\n\n/** Serialize a single server node (element/text/comment/fragment) to HTML. */\nexport function serializeServerNode(node: ServerNode): string {\n switch (node.kind) {\n case 'text':\n return escapeHtmlText((node as ServerText).data);\n case 'comment':\n return `<!--${(node as ServerComment).data}-->`;\n case 'fragment':\n return serializeChildren(node as ServerFragment);\n case 'element': {\n const el = node as ServerElement;\n const tag = el.tagName;\n const attrs = serializeAttributes(el);\n if (VOID_ELEMENTS.has(tag)) {\n return `<${tag}${attrs}>`;\n }\n return `<${tag}${attrs}>${serializeChildren(el)}</${tag}>`;\n }\n }\n}\n\n/** Serialize the children of an element or fragment (its \"inner HTML\"). */\nexport function serializeChildren(node: ServerElement | ServerFragment): string {\n let out = '';\n for (const child of node.children) {\n out += serializeServerNode(child);\n }\n return out;\n}\n","/**\n * Server implementation of `DOMAdapter`.\n *\n * Builds a lightweight in-memory tree (see `server-node.ts`) instead of touching\n * a real browser DOM, then lets the caller serialize it to an HTML string. It is\n * completely free of browser globals, so the exact same renderer that runs in\n * the browser can produce HTML on the server.\n *\n * The `DOMAdapter` interface is typed against the lib DOM types (`Element`,\n * `Node`, `Text`, …). Our server nodes structurally stand in for those at\n * runtime, so the boundary uses `as unknown as` casts in one place. Everything\n * inside operates on the real server-node model.\n */\n\nimport type { DOMAdapter } from './adapter.js';\nimport {\n ServerElement,\n ServerText,\n ServerComment,\n ServerFragment,\n serializeChildren,\n serializeServerNode,\n type ServerNode,\n type ServerParent,\n} from './server-node.js';\n\nfunction asServer(node: unknown): ServerNode {\n return node as unknown as ServerNode;\n}\nfunction asParent(node: unknown): ServerParent {\n return node as unknown as ServerParent;\n}\n\nexport class ServerDOMAdapter implements DOMAdapter {\n createElement(tag: string, _ns?: string): Element {\n return new ServerElement(tag) as unknown as Element;\n }\n\n createTextNode(data: string): Text {\n return new ServerText(data) as unknown as Text;\n }\n\n createComment(data: string): Comment {\n return new ServerComment(data) as unknown as Comment;\n }\n\n createFragment(): DocumentFragment {\n return new ServerFragment() as unknown as DocumentFragment;\n }\n\n appendChild(parent: Node, child: Node): void {\n const p = asParent(parent);\n const c = asServer(child);\n this._detach(c);\n c.parent = p;\n p.children.push(c);\n }\n\n insertBefore(parent: Node, child: Node, reference: Node | null): void {\n const p = asParent(parent);\n const c = asServer(child);\n this._detach(c);\n c.parent = p;\n if (reference === null) {\n p.children.push(c);\n return;\n }\n const ref = asServer(reference);\n const idx = p.children.indexOf(ref);\n if (idx === -1) p.children.push(c);\n else p.children.splice(idx, 0, c);\n }\n\n removeChild(parent: Node, child: Node): void {\n const p = asParent(parent);\n const c = asServer(child);\n const idx = p.children.indexOf(c);\n if (idx !== -1) {\n p.children.splice(idx, 1);\n c.parent = null;\n }\n }\n\n replaceChild(parent: Node, newChild: Node, oldChild: Node): void {\n const p = asParent(parent);\n const nc = asServer(newChild);\n const oc = asServer(oldChild);\n const idx = p.children.indexOf(oc);\n if (idx === -1) return;\n this._detach(nc);\n nc.parent = p;\n p.children.splice(idx, 1, nc);\n oc.parent = null;\n }\n\n private _detach(node: ServerNode): void {\n if (node.parent !== null) {\n const siblings = node.parent.children;\n const idx = siblings.indexOf(node);\n if (idx !== -1) siblings.splice(idx, 1);\n node.parent = null;\n }\n }\n\n setAttribute(element: Element, name: string, value: string): void {\n (element as unknown as ServerElement).attributes.set(name, value);\n }\n\n removeAttribute(element: Element, name: string): void {\n (element as unknown as ServerElement).attributes.delete(name);\n }\n\n getAttribute(element: Element, name: string): string | null {\n return (element as unknown as ServerElement).attributes.get(name) ?? null;\n }\n\n setProperty(element: Element, name: string, value: unknown): void {\n (element as unknown as ServerElement).properties.set(name, value);\n }\n\n setTextContent(node: Node, text: string): void {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n const el = n as ServerElement | ServerFragment;\n el.children.length = 0;\n const t = new ServerText(text);\n t.parent = el;\n el.children.push(t);\n } else if (n.kind === 'text') {\n (n as ServerText).data = text;\n }\n }\n\n getTextContent(node: Node): string | null {\n const n = asServer(node);\n if (n.kind === 'text') return (n as ServerText).data;\n if (n.kind === 'element' || n.kind === 'fragment') {\n let out = '';\n for (const c of (n as ServerElement | ServerFragment).children) {\n out += this.getTextContent(c as unknown as Node) ?? '';\n }\n return out;\n }\n return null;\n }\n\n // Server nodes never dispatch events — listeners are a no-op on the server.\n addEventListener(): void {\n /* no-op on the server */\n }\n removeEventListener(): void {\n /* no-op on the server */\n }\n\n querySelector(): Element | null {\n return null;\n }\n querySelectorAll(): NodeListOf<Element> {\n return [] as unknown as NodeListOf<Element>;\n }\n getElementById(): Element | null {\n return null;\n }\n\n focus(): void {\n // No focus concept on the server — intentional no-op (SSR-safe).\n }\n\n isElement(node: Node): node is Element {\n return asServer(node).kind === 'element';\n }\n\n isTextNode(node: Node): node is Text {\n return asServer(node).kind === 'text';\n }\n\n tagName(element: Element): string {\n return (element as unknown as ServerElement).tagName;\n }\n\n parentNode(node: Node): Node | null {\n return (asServer(node).parent as unknown as Node | null) ?? null;\n }\n\n nextSibling(node: Node): Node | null {\n const n = asServer(node);\n const parent = n.parent;\n if (parent === null) return null;\n const idx = parent.children.indexOf(n);\n if (idx === -1 || idx + 1 >= parent.children.length) return null;\n return parent.children[idx + 1] as unknown as Node;\n }\n\n firstChild(node: Node): Node | null {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n const el = n as ServerElement | ServerFragment;\n return (el.children[0] as unknown as Node) ?? null;\n }\n return null;\n }\n\n childNodes(node: Node): Node[] {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n return (n as ServerElement | ServerFragment).children as unknown as Node[];\n }\n return [];\n }\n\n // ── Server-only ────────────────────────────────────────────────────────────\n\n /** Serialize a node's children (\"inner HTML\") to an HTML string. */\n serializeInner(node: Node): string {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n return serializeChildren(n as ServerElement | ServerFragment);\n }\n return '';\n }\n\n /** Serialize a node (including itself) to an HTML string. */\n serializeOuter(node: Node): string {\n return serializeServerNode(asServer(node));\n }\n}\n\n// `/* @__PURE__ */`: this singleton is a convenience export only (no internal\n// runtime path references it). Marking construction pure lets bundlers drop it —\n// and with it the whole server serializer chain (serializeServerNode/escape/\n// VOID_ELEMENTS) — out of client bundles that never import SSR. Without this,\n// the un-annotated `new` is treated as a side effect and retained everywhere.\nexport const serverDOMAdapter = /* @__PURE__ */ new ServerDOMAdapter();\n","/**\n * Focus helpers built on the {@link DOMAdapter} abstraction.\n *\n * These are the minimal, genuinely-useful focus operations an app needs:\n * focus a specific element (e.g. the first field when a route or modal opens)\n * or focus the first focusable element inside a container (e.g. move focus\n * into a dialog). Both go through the adapter, so they are no-ops on the server\n * (`ServerDOMAdapter.querySelector` returns null / `focus` does nothing) and\n * therefore safe to call from universal code.\n */\n\nimport type { DOMAdapter } from './adapter.js';\n\n/** Default selector for natively focusable / tabbable elements. */\nexport const FOCUSABLE_SELECTOR =\n 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Focus the element with the given id, scoped to `root`.\n * Returns true if an element was found and focused.\n */\nexport function focusById(dom: DOMAdapter, root: Element | Document, id: string): boolean {\n const el = dom.querySelector(root, `[id=\"${id}\"]`);\n if (el === null) return false;\n dom.focus(el);\n return true;\n}\n\n/**\n * Focus the first focusable element inside `container`.\n * Returns true if a focusable element was found and focused.\n */\nexport function focusFirst(\n dom: DOMAdapter,\n container: Element | Document,\n selector: string = FOCUSABLE_SELECTOR,\n): boolean {\n const el = dom.querySelector(container, selector);\n if (el === null) return false;\n dom.focus(el);\n return true;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/browser-adapter.ts","../src/server-node.ts","../src/server-adapter.ts","../src/focus.ts"],"sourcesContent":["export * from './adapter.js';\nexport * from './browser-adapter.js';\nexport * from './server-node.js';\nexport * from './server-adapter.js';\nexport * from './focus.js';\n","/**\n * Browser implementation of DOMAdapter — delegates directly to browser APIs.\n */\n\nimport type { DOMAdapter } from './adapter.js';\n\nexport class BrowserDOMAdapter implements DOMAdapter {\n createElement(tag: string, ns?: string): Element {\n if (ns !== undefined) {\n return document.createElementNS(ns, tag);\n }\n return document.createElement(tag);\n }\n\n createTextNode(data: string): Text {\n return document.createTextNode(data);\n }\n\n createComment(data: string): Comment {\n return document.createComment(data);\n }\n\n createFragment(): DocumentFragment {\n return document.createDocumentFragment();\n }\n\n appendChild(parent: Node, child: Node): void {\n parent.appendChild(child);\n }\n\n insertBefore(parent: Node, child: Node, reference: Node | null): void {\n parent.insertBefore(child, reference);\n }\n\n removeChild(parent: Node, child: Node): void {\n parent.removeChild(child);\n }\n\n replaceChild(parent: Node, newChild: Node, oldChild: Node): void {\n parent.replaceChild(newChild, oldChild);\n }\n\n setAttribute(element: Element, name: string, value: string): void {\n element.setAttribute(name, value);\n }\n\n removeAttribute(element: Element, name: string): void {\n element.removeAttribute(name);\n }\n\n getAttribute(element: Element, name: string): string | null {\n return element.getAttribute(name);\n }\n\n setProperty(element: Element, name: string, value: unknown): void {\n (element as unknown as Record<string, unknown>)[name] = value;\n }\n\n setTextContent(node: Node, text: string): void {\n node.textContent = text;\n }\n\n getTextContent(node: Node): string | null {\n return node.textContent;\n }\n\n addEventListener(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n ): void {\n target.addEventListener(type, handler, options);\n }\n\n removeEventListener(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: EventListenerOptions,\n ): void {\n target.removeEventListener(type, handler, options);\n }\n\n querySelector(root: Element | Document, selector: string): Element | null {\n return root.querySelector(selector);\n }\n\n querySelectorAll(root: Element | Document, selector: string): NodeListOf<Element> {\n return root.querySelectorAll(selector);\n }\n\n getElementById(id: string): Element | null {\n return document.getElementById(id);\n }\n\n focus(element: Element): void {\n (element as unknown as { focus?: () => void }).focus?.();\n }\n\n body(): Element | null {\n return document.body ?? null;\n }\n\n activeElement(): Element | null {\n return document.activeElement ?? null;\n }\n\n contains(ancestor: Element, node: Node): boolean {\n return ancestor.contains(node);\n }\n\n matches(element: Element, selector: string): boolean {\n return typeof element.matches === 'function' && element.matches(selector);\n }\n\n isElement(node: Node): node is Element {\n return node.nodeType === Node.ELEMENT_NODE;\n }\n\n isTextNode(node: Node): node is Text {\n return node.nodeType === Node.TEXT_NODE;\n }\n\n tagName(element: Element): string {\n return element.tagName.toLowerCase();\n }\n\n parentNode(node: Node): Node | null {\n return node.parentNode;\n }\n\n nextSibling(node: Node): Node | null {\n return node.nextSibling;\n }\n\n firstChild(node: Node): Node | null {\n return node.firstChild;\n }\n\n childNodes(node: Node): Node[] {\n return Array.from(node.childNodes);\n }\n}\n\n// `/* @__PURE__ */`: convenience singleton, unreferenced by internal runtime\n// paths. Marking construction pure lets bundlers drop it when unused instead of\n// retaining it (and the BrowserDOMAdapter class) as an import-time side effect.\nexport const browserDOMAdapter = /* @__PURE__ */ new BrowserDOMAdapter();\n","/**\n * Server-side DOM node model.\n *\n * A tiny, dependency-free tree of plain objects that mirrors just enough of the\n * browser DOM for StreetUI's renderer to build a tree on the server and\n * serialize it to an HTML string. There is NO browser global here — these are\n * ordinary classes usable in any JavaScript environment (Node, workers, tests).\n *\n * The renderer never touches these types directly; it goes through the\n * `DOMAdapter` interface, and `ServerDOMAdapter` translates adapter calls into\n * operations on this model.\n */\n\nexport type ServerNodeKind = 'element' | 'text' | 'comment' | 'fragment' | 'raw';\n\nexport interface ServerNode {\n readonly kind: ServerNodeKind;\n parent: ServerParent | null;\n}\n\nexport type ServerParent = ServerElement | ServerFragment;\n\n/** A minimal inline-style holder mirroring `element.style.setProperty`. */\nexport class ServerStyle {\n readonly declarations = new Map<string, string>();\n setProperty(name: string, value: string): void {\n this.declarations.set(name, value);\n }\n get isEmpty(): boolean {\n return this.declarations.size === 0;\n }\n toCss(): string {\n return [...this.declarations.entries()].map(([k, v]) => `${k}: ${v}`).join('; ');\n }\n}\n\nexport class ServerText implements ServerNode {\n readonly kind = 'text' as const;\n parent: ServerParent | null = null;\n data: string;\n constructor(data: string) {\n this.data = data;\n }\n}\n\nexport class ServerComment implements ServerNode {\n readonly kind = 'comment' as const;\n parent: ServerParent | null = null;\n data: string;\n constructor(data: string) {\n this.data = data;\n }\n}\n\nexport class ServerFragment implements ServerNode {\n readonly kind = 'fragment' as const;\n parent: ServerParent | null = null;\n readonly children: ServerNode[] = [];\n}\n\n/**\n * A pre-serialized, verbatim HTML fragment (v1.7 static SSR plan).\n *\n * Emitted for provably-static subtrees whose HTML the compiler-derived static\n * SSR plan already computed once. Serializing this node copies its stored\n * string directly — it allocates no ServerElement/ServerText, no attribute Map\n * and no children array for the collapsed subtree. The stored `html` is\n * produced by the exact same mount + serialize pipeline as the runtime path, so\n * the output is byte-identical (the v1.7 byte-identity gate proves this).\n *\n * This node is SSR-only: it is created solely via `ServerDOMAdapter.createRawHTML`\n * on the server render path and never appears in a browser build.\n */\nexport class ServerRawHTML implements ServerNode {\n readonly kind = 'raw' as const;\n parent: ServerParent | null = null;\n readonly html: string;\n constructor(html: string) {\n this.html = html;\n }\n}\n\nexport class ServerElement implements ServerNode {\n readonly kind = 'element' as const;\n parent: ServerParent | null = null;\n readonly tagName: string;\n readonly attributes = new Map<string, string>();\n readonly children: ServerNode[] = [];\n\n // Lazily-allocated stores. On the 10k-row SSR corpus ~0% of elements carry JS\n // properties or inline styles (measured, §5: 1 of 80,029 elements uses\n // `properties`, 0 use `style`), so eagerly allocating a `properties` Map plus\n // a `ServerStyle` (which itself holds a Map) per element wasted ~240k\n // allocations per /users render — all in the dominant mount phase. These are\n // created on first WRITE via the `properties`/`style` getters; the serializer\n // reads the raw `_properties`/`_style` fields so a READ never forces an\n // allocation. Output is byte-identical: an unset store previously serialized\n // to nothing (empty `properties.has(...)` / `style.isEmpty`), and a null store\n // is skipped the same way.\n _properties: Map<string, unknown> | null = null;\n _style: ServerStyle | null = null;\n\n constructor(tagName: string) {\n this.tagName = tagName.toLowerCase();\n }\n\n /** JS properties set via `setProperty` (e.g. input `value`, `checked`). Allocated on first access. */\n get properties(): Map<string, unknown> {\n return (this._properties ??= new Map());\n }\n\n /** Inline-style holder mirroring `element.style`. Allocated on first access. */\n get style(): ServerStyle {\n return (this._style ??= new ServerStyle());\n }\n}\n\n// ── HTML serialization ─────────────────────────────────────────────────────────\n\n/**\n * HTML \"void\" elements — self-closing, never given a closing tag or children.\n */\nconst VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n/**\n * Element properties that should be reflected into the serialized HTML so the\n * hydrated DOM carries the same initial state. `value`/`checked` matter for\n * form controls whose live state is a JS property, not an attribute.\n */\nconst SERIALIZED_PROPERTIES: Record<string, 'attr' | 'boolean'> = {\n value: 'attr',\n checked: 'boolean',\n selected: 'boolean',\n};\n\n/**\n * Precomputed `[name, kind]` pairs of SERIALIZED_PROPERTIES. Hoisted to module\n * scope so `serializeAttributes` does not allocate a fresh entries array for\n * every element serialized (measured hot: ~80k elements on the 10k-row route).\n */\nconst SERIALIZED_PROPERTY_ENTRIES: ReadonlyArray<readonly [string, 'attr' | 'boolean']> =\n Object.entries(SERIALIZED_PROPERTIES) as Array<[string, 'attr' | 'boolean']>;\n\n// Fast-path escaping. The chained `.replace(/…/g, …)` form makes 3–4 full\n// passes and allocates an intermediate string per pass even when nothing needs\n// escaping. These variants scan once and, in the overwhelmingly common case of\n// no special character, return the input unchanged (zero allocation). Output is\n// byte-identical to the chained form (verified over the real SSR corpus).\nconst TEXT_SPECIAL = /[&<>]/;\nconst ATTR_SPECIAL = /[&<>\"]/;\n\n/** Escape text node content. */\nexport function escapeHtmlText(value: string): string {\n if (!TEXT_SPECIAL.test(value)) return value;\n let out = '';\n let last = 0;\n for (let i = 0; i < value.length; i++) {\n let esc: string;\n switch (value.charCodeAt(i)) {\n case 38: esc = '&amp;'; break; // &\n case 60: esc = '&lt;'; break; // <\n case 62: esc = '&gt;'; break; // >\n default: continue;\n }\n out += value.slice(last, i) + esc;\n last = i + 1;\n }\n return out + value.slice(last);\n}\n\n/** Escape a double-quoted attribute value. */\nexport function escapeHtmlAttr(value: string): string {\n if (!ATTR_SPECIAL.test(value)) return value;\n let out = '';\n let last = 0;\n for (let i = 0; i < value.length; i++) {\n let esc: string;\n switch (value.charCodeAt(i)) {\n case 38: esc = '&amp;'; break; // &\n case 60: esc = '&lt;'; break; // <\n case 62: esc = '&gt;'; break; // >\n case 34: esc = '&quot;'; break; // \"\n default: continue;\n }\n out += value.slice(last, i) + esc;\n last = i + 1;\n }\n return out + value.slice(last);\n}\n\nfunction serializeAttributes(el: ServerElement): string {\n const parts: string[] = [];\n\n for (const [name, value] of el.attributes) {\n if (value === '') {\n parts.push(` ${name}`);\n } else {\n parts.push(` ${name}=\"${escapeHtmlAttr(value)}\"`);\n }\n }\n\n // Read the raw backing field (may be null): most elements have no JS\n // properties, so skipping the whole loop avoids touching a store that was\n // never allocated (§5).\n const props = el._properties;\n if (props !== null) {\n for (const [name, kind] of SERIALIZED_PROPERTY_ENTRIES) {\n if (!props.has(name)) continue;\n if (el.attributes.has(name)) continue; // an explicit attribute already won\n const raw = props.get(name);\n if (kind === 'boolean') {\n if (raw === true) parts.push(` ${name}`);\n } else {\n if (raw !== undefined && raw !== null) {\n parts.push(` ${name}=\"${escapeHtmlAttr(String(raw))}\"`);\n }\n }\n }\n }\n\n const style = el._style;\n if (style !== null && !style.isEmpty && !el.attributes.has('style')) {\n parts.push(` style=\"${escapeHtmlAttr(style.toCss())}\"`);\n }\n\n return parts.join('');\n}\n\n/** Serialize a single server node (element/text/comment/fragment/raw) to HTML. */\nexport function serializeServerNode(node: ServerNode): string {\n switch (node.kind) {\n case 'text':\n return escapeHtmlText((node as ServerText).data);\n case 'comment':\n return `<!--${(node as ServerComment).data}-->`;\n case 'fragment':\n return serializeChildren(node as ServerFragment);\n case 'raw':\n // Verbatim: the string was produced by this same serializer for a static\n // subtree, so it is already correctly escaped. Copy it as-is (§8).\n return (node as ServerRawHTML).html;\n case 'element': {\n const el = node as ServerElement;\n const tag = el.tagName;\n const attrs = serializeAttributes(el);\n if (VOID_ELEMENTS.has(tag)) {\n return `<${tag}${attrs}>`;\n }\n return `<${tag}${attrs}>${serializeChildren(el)}</${tag}>`;\n }\n }\n}\n\n/** Serialize the children of an element or fragment (its \"inner HTML\"). */\nexport function serializeChildren(node: ServerElement | ServerFragment): string {\n let out = '';\n for (const child of node.children) {\n out += serializeServerNode(child);\n }\n return out;\n}\n","/**\n * Server implementation of `DOMAdapter`.\n *\n * Builds a lightweight in-memory tree (see `server-node.ts`) instead of touching\n * a real browser DOM, then lets the caller serialize it to an HTML string. It is\n * completely free of browser globals, so the exact same renderer that runs in\n * the browser can produce HTML on the server.\n *\n * The `DOMAdapter` interface is typed against the lib DOM types (`Element`,\n * `Node`, `Text`, …). Our server nodes structurally stand in for those at\n * runtime, so the boundary uses `as unknown as` casts in one place. Everything\n * inside operates on the real server-node model.\n */\n\nimport type { DOMAdapter } from './adapter.js';\nimport {\n ServerElement,\n ServerText,\n ServerComment,\n ServerFragment,\n ServerRawHTML,\n serializeChildren,\n serializeServerNode,\n type ServerNode,\n type ServerParent,\n} from './server-node.js';\n\nfunction asServer(node: unknown): ServerNode {\n return node as unknown as ServerNode;\n}\nfunction asParent(node: unknown): ServerParent {\n return node as unknown as ServerParent;\n}\n\nexport class ServerDOMAdapter implements DOMAdapter {\n createElement(tag: string, _ns?: string): Element {\n return new ServerElement(tag) as unknown as Element;\n }\n\n createTextNode(data: string): Text {\n return new ServerText(data) as unknown as Text;\n }\n\n createComment(data: string): Comment {\n return new ServerComment(data) as unknown as Comment;\n }\n\n createFragment(): DocumentFragment {\n return new ServerFragment() as unknown as DocumentFragment;\n }\n\n /**\n * Create a verbatim pre-serialized HTML node (v1.7 static SSR plan, §6).\n * Server-only: the browser adapter does not implement this, and the renderer\n * fast path only invokes it when a static SSR plan is present (SSR). The\n * stored HTML was produced by this same serializer, so it is emitted as-is.\n */\n createRawHTML(html: string): Node {\n return new ServerRawHTML(html) as unknown as Node;\n }\n\n appendChild(parent: Node, child: Node): void {\n const p = asParent(parent);\n const c = asServer(child);\n this._detach(c);\n c.parent = p;\n p.children.push(c);\n }\n\n insertBefore(parent: Node, child: Node, reference: Node | null): void {\n const p = asParent(parent);\n const c = asServer(child);\n this._detach(c);\n c.parent = p;\n if (reference === null) {\n p.children.push(c);\n return;\n }\n const ref = asServer(reference);\n const idx = p.children.indexOf(ref);\n if (idx === -1) p.children.push(c);\n else p.children.splice(idx, 0, c);\n }\n\n removeChild(parent: Node, child: Node): void {\n const p = asParent(parent);\n const c = asServer(child);\n const idx = p.children.indexOf(c);\n if (idx !== -1) {\n p.children.splice(idx, 1);\n c.parent = null;\n }\n }\n\n replaceChild(parent: Node, newChild: Node, oldChild: Node): void {\n const p = asParent(parent);\n const nc = asServer(newChild);\n const oc = asServer(oldChild);\n const idx = p.children.indexOf(oc);\n if (idx === -1) return;\n this._detach(nc);\n nc.parent = p;\n p.children.splice(idx, 1, nc);\n oc.parent = null;\n }\n\n private _detach(node: ServerNode): void {\n if (node.parent !== null) {\n const siblings = node.parent.children;\n const idx = siblings.indexOf(node);\n if (idx !== -1) siblings.splice(idx, 1);\n node.parent = null;\n }\n }\n\n setAttribute(element: Element, name: string, value: string): void {\n (element as unknown as ServerElement).attributes.set(name, value);\n }\n\n removeAttribute(element: Element, name: string): void {\n (element as unknown as ServerElement).attributes.delete(name);\n }\n\n getAttribute(element: Element, name: string): string | null {\n return (element as unknown as ServerElement).attributes.get(name) ?? null;\n }\n\n setProperty(element: Element, name: string, value: unknown): void {\n (element as unknown as ServerElement).properties.set(name, value);\n }\n\n setTextContent(node: Node, text: string): void {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n const el = n as ServerElement | ServerFragment;\n el.children.length = 0;\n const t = new ServerText(text);\n t.parent = el;\n el.children.push(t);\n } else if (n.kind === 'text') {\n (n as ServerText).data = text;\n }\n }\n\n getTextContent(node: Node): string | null {\n const n = asServer(node);\n if (n.kind === 'text') return (n as ServerText).data;\n if (n.kind === 'element' || n.kind === 'fragment') {\n let out = '';\n for (const c of (n as ServerElement | ServerFragment).children) {\n out += this.getTextContent(c as unknown as Node) ?? '';\n }\n return out;\n }\n return null;\n }\n\n // Server nodes never dispatch events — listeners are a no-op on the server.\n addEventListener(): void {\n /* no-op on the server */\n }\n removeEventListener(): void {\n /* no-op on the server */\n }\n\n querySelector(): Element | null {\n return null;\n }\n querySelectorAll(): NodeListOf<Element> {\n return [] as unknown as NodeListOf<Element>;\n }\n getElementById(): Element | null {\n return null;\n }\n\n focus(): void {\n // No focus concept on the server — intentional no-op (SSR-safe).\n }\n\n body(): Element | null {\n // No document on the server — portals degrade to inline rendering.\n return null;\n }\n\n activeElement(): Element | null {\n return null;\n }\n\n contains(_ancestor: Element, _node: Node): boolean {\n return false;\n }\n\n matches(_element: Element, _selector: string): boolean {\n return false;\n }\n\n isElement(node: Node): node is Element {\n return asServer(node).kind === 'element';\n }\n\n isTextNode(node: Node): node is Text {\n return asServer(node).kind === 'text';\n }\n\n tagName(element: Element): string {\n return (element as unknown as ServerElement).tagName;\n }\n\n parentNode(node: Node): Node | null {\n return (asServer(node).parent as unknown as Node | null) ?? null;\n }\n\n nextSibling(node: Node): Node | null {\n const n = asServer(node);\n const parent = n.parent;\n if (parent === null) return null;\n const idx = parent.children.indexOf(n);\n if (idx === -1 || idx + 1 >= parent.children.length) return null;\n return parent.children[idx + 1] as unknown as Node;\n }\n\n firstChild(node: Node): Node | null {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n const el = n as ServerElement | ServerFragment;\n return (el.children[0] as unknown as Node) ?? null;\n }\n return null;\n }\n\n childNodes(node: Node): Node[] {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n return (n as ServerElement | ServerFragment).children as unknown as Node[];\n }\n return [];\n }\n\n // ── Server-only ────────────────────────────────────────────────────────────\n\n /** Serialize a node's children (\"inner HTML\") to an HTML string. */\n serializeInner(node: Node): string {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n return serializeChildren(n as ServerElement | ServerFragment);\n }\n return '';\n }\n\n /** Serialize a node (including itself) to an HTML string. */\n serializeOuter(node: Node): string {\n return serializeServerNode(asServer(node));\n }\n}\n\n// `/* @__PURE__ */`: this singleton is a convenience export only (no internal\n// runtime path references it). Marking construction pure lets bundlers drop it —\n// and with it the whole server serializer chain (serializeServerNode/escape/\n// VOID_ELEMENTS) — out of client bundles that never import SSR. Without this,\n// the un-annotated `new` is treated as a side effect and retained everywhere.\nexport const serverDOMAdapter = /* @__PURE__ */ new ServerDOMAdapter();\n","/**\n * Focus helpers built on the {@link DOMAdapter} abstraction.\n *\n * These are the minimal, genuinely-useful focus operations an app needs:\n * focus a specific element (e.g. the first field when a route or modal opens)\n * or focus the first focusable element inside a container (e.g. move focus\n * into a dialog). Both go through the adapter, so they are no-ops on the server\n * (`ServerDOMAdapter.querySelector` returns null / `focus` does nothing) and\n * therefore safe to call from universal code.\n */\n\nimport type { DOMAdapter } from './adapter.js';\n\n/** Default selector for natively focusable / tabbable elements. */\nexport const FOCUSABLE_SELECTOR =\n 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Focus the element with the given id, scoped to `root`.\n * Returns true if an element was found and focused.\n */\nexport function focusById(dom: DOMAdapter, root: Element | Document, id: string): boolean {\n const el = dom.querySelector(root, `[id=\"${id}\"]`);\n if (el === null) return false;\n dom.focus(el);\n return true;\n}\n\n/**\n * Focus the first focusable element inside `container`.\n * Returns true if a focusable element was found and focused.\n */\nexport function focusFirst(\n dom: DOMAdapter,\n container: Element | Document,\n selector: string = FOCUSABLE_SELECTOR,\n): boolean {\n const el = dom.querySelector(container, selector);\n if (el === null) return false;\n dom.focus(el);\n return true;\n}\n\n/**\n * Ordered list of focusable/tabbable descendants of `container`.\n * Re-checks each candidate against the selector so elements disabled after the\n * initial query (e.g. a button toggled to `disabled`) are excluded.\n */\nexport function getFocusable(\n dom: DOMAdapter,\n container: Element,\n selector: string = FOCUSABLE_SELECTOR,\n): Element[] {\n return Array.from(dom.querySelectorAll(container, selector)).filter((el) =>\n dom.matches(el, selector),\n );\n}\n\n/**\n * Capture the currently-focused element so it can be restored later (e.g. when\n * a dialog closes). Returns null on the server or when nothing is focused.\n */\nexport function saveFocus(dom: DOMAdapter): Element | null {\n return dom.activeElement();\n}\n\n/** Restore focus to a previously {@link saveFocus}-d element. No-op if null. */\nexport function restoreFocus(dom: DOMAdapter, saved: Element | null): void {\n if (saved !== null) dom.focus(saved);\n}\n\n/**\n * Move focus into `container` on open: the element with id `initialFocusId` if\n * given and present, otherwise the first focusable element. Server-safe no-op.\n */\nexport function focusInitial(\n dom: DOMAdapter,\n container: Element,\n initialFocusId?: string,\n): void {\n if (initialFocusId !== undefined && focusById(dom, container, initialFocusId)) return;\n focusFirst(dom, container);\n}\n\n/**\n * Trap Tab / Shift+Tab focus within `container` (wrap-around at both ends).\n * Attaches a keydown listener to the container and returns a cleanup function\n * that detaches it. Server-safe: `addEventListener` is a no-op, and the returned\n * cleanup is still callable.\n */\nexport function trapFocus(dom: DOMAdapter, container: Element): () => void {\n const onKeydown = ((event: KeyboardEvent): void => {\n if (event.key !== 'Tab') return;\n const items = getFocusable(dom, container);\n if (items.length === 0) {\n event.preventDefault();\n return;\n }\n const first = items[0]!;\n const last = items[items.length - 1]!;\n const active = dom.activeElement();\n if (active === null || !dom.contains(container, active)) {\n event.preventDefault();\n dom.focus(first);\n } else if (event.shiftKey && active === first) {\n event.preventDefault();\n dom.focus(last);\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n dom.focus(first);\n }\n }) as EventListener;\n dom.addEventListener(container, 'keydown', onKeydown);\n return () => dom.removeEventListener(container, 'keydown', onKeydown);\n}\n\n/**\n * Modal containment: if focus moves to an element outside `container`, redirect\n * it back inside. Listens on the document body (focusin bubbles there) and\n * returns a cleanup function. Server-safe no-op (body() is null).\n */\nexport function containFocus(dom: DOMAdapter, container: Element): () => void {\n const body = dom.body();\n if (body === null) return () => {};\n const onFocusIn = ((event: FocusEvent): void => {\n const target = event.target as Node | null;\n if (target !== null && !dom.contains(container, target)) {\n focusFirst(dom, container);\n }\n }) as EventListener;\n dom.addEventListener(body, 'focusin', onFocusIn);\n return () => dom.removeEventListener(body, 'focusin', onFocusIn);\n}\n\n/**\n * Invoke `handler` when Escape is pressed while focus is within `target`.\n * Returns a cleanup function. Server-safe no-op.\n */\nexport function onEscape(dom: DOMAdapter, target: Element, handler: () => void): () => void {\n const onKeydown = ((event: KeyboardEvent): void => {\n if (event.key === 'Escape') handler();\n }) as EventListener;\n dom.addEventListener(target, 'keydown', onKeydown);\n return () => dom.removeEventListener(target, 'keydown', onKeydown);\n}\n\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -9,6 +9,13 @@ interface DOMAdapter {
9
9
  createTextNode(data: string): Text;
10
10
  createComment(data: string): Comment;
11
11
  createFragment(): DocumentFragment;
12
+ /**
13
+ * Optional, server-only: create a verbatim pre-serialized HTML node used by
14
+ * the v1.7 static SSR plan. The browser adapter does not implement it; the
15
+ * renderer only calls it when a static SSR plan is active (i.e. during SSR),
16
+ * so client builds never reach this path and it stays tree-shakeable.
17
+ */
18
+ createRawHTML?(html: string): Node;
12
19
  appendChild(parent: Node, child: Node): void;
13
20
  insertBefore(parent: Node, child: Node, reference: Node | null): void;
14
21
  removeChild(parent: Node, child: Node): void;
@@ -29,6 +36,18 @@ interface DOMAdapter {
29
36
  * focus) this is a safe no-op, keeping focus management SSR-compatible.
30
37
  */
31
38
  focus(element: Element): void;
39
+ /**
40
+ * The document body — the default mount target for portals/overlays. Returns
41
+ * null on the server (no document), which is what makes portal SSR degrade to
42
+ * inline rendering and focus management degrade to a no-op.
43
+ */
44
+ body(): Element | null;
45
+ /** The currently focused element, or null on the server / when none is focused. */
46
+ activeElement(): Element | null;
47
+ /** True if `ancestor` contains `node` (inclusive). Always false on the server. */
48
+ contains(ancestor: Element, node: Node): boolean;
49
+ /** True if `element` matches the given CSS selector. Always false on the server. */
50
+ matches(element: Element, selector: string): boolean;
32
51
  isElement(node: Node): node is Element;
33
52
  isTextNode(node: Node): node is Text;
34
53
  /** Lower-cased tag name of an element (e.g. "div", "h1"). */
@@ -66,6 +85,10 @@ declare class BrowserDOMAdapter implements DOMAdapter {
66
85
  querySelectorAll(root: Element | Document, selector: string): NodeListOf<Element>;
67
86
  getElementById(id: string): Element | null;
68
87
  focus(element: Element): void;
88
+ body(): Element | null;
89
+ activeElement(): Element | null;
90
+ contains(ancestor: Element, node: Node): boolean;
91
+ matches(element: Element, selector: string): boolean;
69
92
  isElement(node: Node): node is Element;
70
93
  isTextNode(node: Node): node is Text;
71
94
  tagName(element: Element): string;
@@ -88,7 +111,7 @@ declare const browserDOMAdapter: BrowserDOMAdapter;
88
111
  * `DOMAdapter` interface, and `ServerDOMAdapter` translates adapter calls into
89
112
  * operations on this model.
90
113
  */
91
- type ServerNodeKind = 'element' | 'text' | 'comment' | 'fragment';
114
+ type ServerNodeKind = 'element' | 'text' | 'comment' | 'fragment' | 'raw';
92
115
  interface ServerNode {
93
116
  readonly kind: ServerNodeKind;
94
117
  parent: ServerParent | null;
@@ -118,6 +141,25 @@ declare class ServerFragment implements ServerNode {
118
141
  parent: ServerParent | null;
119
142
  readonly children: ServerNode[];
120
143
  }
144
+ /**
145
+ * A pre-serialized, verbatim HTML fragment (v1.7 static SSR plan).
146
+ *
147
+ * Emitted for provably-static subtrees whose HTML the compiler-derived static
148
+ * SSR plan already computed once. Serializing this node copies its stored
149
+ * string directly — it allocates no ServerElement/ServerText, no attribute Map
150
+ * and no children array for the collapsed subtree. The stored `html` is
151
+ * produced by the exact same mount + serialize pipeline as the runtime path, so
152
+ * the output is byte-identical (the v1.7 byte-identity gate proves this).
153
+ *
154
+ * This node is SSR-only: it is created solely via `ServerDOMAdapter.createRawHTML`
155
+ * on the server render path and never appears in a browser build.
156
+ */
157
+ declare class ServerRawHTML implements ServerNode {
158
+ readonly kind: "raw";
159
+ parent: ServerParent | null;
160
+ readonly html: string;
161
+ constructor(html: string);
162
+ }
121
163
  declare class ServerElement implements ServerNode {
122
164
  readonly kind: "element";
123
165
  parent: ServerParent | null;
@@ -136,7 +178,7 @@ declare class ServerElement implements ServerNode {
136
178
  declare function escapeHtmlText(value: string): string;
137
179
  /** Escape a double-quoted attribute value. */
138
180
  declare function escapeHtmlAttr(value: string): string;
139
- /** Serialize a single server node (element/text/comment/fragment) to HTML. */
181
+ /** Serialize a single server node (element/text/comment/fragment/raw) to HTML. */
140
182
  declare function serializeServerNode(node: ServerNode): string;
141
183
  /** Serialize the children of an element or fragment (its "inner HTML"). */
142
184
  declare function serializeChildren(node: ServerElement | ServerFragment): string;
@@ -160,6 +202,13 @@ declare class ServerDOMAdapter implements DOMAdapter {
160
202
  createTextNode(data: string): Text;
161
203
  createComment(data: string): Comment;
162
204
  createFragment(): DocumentFragment;
205
+ /**
206
+ * Create a verbatim pre-serialized HTML node (v1.7 static SSR plan, §6).
207
+ * Server-only: the browser adapter does not implement this, and the renderer
208
+ * fast path only invokes it when a static SSR plan is present (SSR). The
209
+ * stored HTML was produced by this same serializer, so it is emitted as-is.
210
+ */
211
+ createRawHTML(html: string): Node;
163
212
  appendChild(parent: Node, child: Node): void;
164
213
  insertBefore(parent: Node, child: Node, reference: Node | null): void;
165
214
  removeChild(parent: Node, child: Node): void;
@@ -177,6 +226,10 @@ declare class ServerDOMAdapter implements DOMAdapter {
177
226
  querySelectorAll(): NodeListOf<Element>;
178
227
  getElementById(): Element | null;
179
228
  focus(): void;
229
+ body(): Element | null;
230
+ activeElement(): Element | null;
231
+ contains(_ancestor: Element, _node: Node): boolean;
232
+ matches(_element: Element, _selector: string): boolean;
180
233
  isElement(node: Node): node is Element;
181
234
  isTextNode(node: Node): node is Text;
182
235
  tagName(element: Element): string;
@@ -214,5 +267,41 @@ declare function focusById(dom: DOMAdapter, root: Element | Document, id: string
214
267
  * Returns true if a focusable element was found and focused.
215
268
  */
216
269
  declare function focusFirst(dom: DOMAdapter, container: Element | Document, selector?: string): boolean;
270
+ /**
271
+ * Ordered list of focusable/tabbable descendants of `container`.
272
+ * Re-checks each candidate against the selector so elements disabled after the
273
+ * initial query (e.g. a button toggled to `disabled`) are excluded.
274
+ */
275
+ declare function getFocusable(dom: DOMAdapter, container: Element, selector?: string): Element[];
276
+ /**
277
+ * Capture the currently-focused element so it can be restored later (e.g. when
278
+ * a dialog closes). Returns null on the server or when nothing is focused.
279
+ */
280
+ declare function saveFocus(dom: DOMAdapter): Element | null;
281
+ /** Restore focus to a previously {@link saveFocus}-d element. No-op if null. */
282
+ declare function restoreFocus(dom: DOMAdapter, saved: Element | null): void;
283
+ /**
284
+ * Move focus into `container` on open: the element with id `initialFocusId` if
285
+ * given and present, otherwise the first focusable element. Server-safe no-op.
286
+ */
287
+ declare function focusInitial(dom: DOMAdapter, container: Element, initialFocusId?: string): void;
288
+ /**
289
+ * Trap Tab / Shift+Tab focus within `container` (wrap-around at both ends).
290
+ * Attaches a keydown listener to the container and returns a cleanup function
291
+ * that detaches it. Server-safe: `addEventListener` is a no-op, and the returned
292
+ * cleanup is still callable.
293
+ */
294
+ declare function trapFocus(dom: DOMAdapter, container: Element): () => void;
295
+ /**
296
+ * Modal containment: if focus moves to an element outside `container`, redirect
297
+ * it back inside. Listens on the document body (focusin bubbles there) and
298
+ * returns a cleanup function. Server-safe no-op (body() is null).
299
+ */
300
+ declare function containFocus(dom: DOMAdapter, container: Element): () => void;
301
+ /**
302
+ * Invoke `handler` when Escape is pressed while focus is within `target`.
303
+ * Returns a cleanup function. Server-safe no-op.
304
+ */
305
+ declare function onEscape(dom: DOMAdapter, target: Element, handler: () => void): () => void;
217
306
 
218
- export { BrowserDOMAdapter, type DOMAdapter, FOCUSABLE_SELECTOR, ServerComment, ServerDOMAdapter, ServerElement, ServerFragment, type ServerNode, type ServerNodeKind, type ServerParent, ServerStyle, ServerText, browserDOMAdapter, escapeHtmlAttr, escapeHtmlText, focusById, focusFirst, serializeChildren, serializeServerNode, serverDOMAdapter };
307
+ export { BrowserDOMAdapter, type DOMAdapter, FOCUSABLE_SELECTOR, ServerComment, ServerDOMAdapter, ServerElement, ServerFragment, type ServerNode, type ServerNodeKind, type ServerParent, ServerRawHTML, ServerStyle, ServerText, browserDOMAdapter, containFocus, escapeHtmlAttr, escapeHtmlText, focusById, focusFirst, focusInitial, getFocusable, onEscape, restoreFocus, saveFocus, serializeChildren, serializeServerNode, serverDOMAdapter, trapFocus };
package/dist/index.d.ts CHANGED
@@ -9,6 +9,13 @@ interface DOMAdapter {
9
9
  createTextNode(data: string): Text;
10
10
  createComment(data: string): Comment;
11
11
  createFragment(): DocumentFragment;
12
+ /**
13
+ * Optional, server-only: create a verbatim pre-serialized HTML node used by
14
+ * the v1.7 static SSR plan. The browser adapter does not implement it; the
15
+ * renderer only calls it when a static SSR plan is active (i.e. during SSR),
16
+ * so client builds never reach this path and it stays tree-shakeable.
17
+ */
18
+ createRawHTML?(html: string): Node;
12
19
  appendChild(parent: Node, child: Node): void;
13
20
  insertBefore(parent: Node, child: Node, reference: Node | null): void;
14
21
  removeChild(parent: Node, child: Node): void;
@@ -29,6 +36,18 @@ interface DOMAdapter {
29
36
  * focus) this is a safe no-op, keeping focus management SSR-compatible.
30
37
  */
31
38
  focus(element: Element): void;
39
+ /**
40
+ * The document body — the default mount target for portals/overlays. Returns
41
+ * null on the server (no document), which is what makes portal SSR degrade to
42
+ * inline rendering and focus management degrade to a no-op.
43
+ */
44
+ body(): Element | null;
45
+ /** The currently focused element, or null on the server / when none is focused. */
46
+ activeElement(): Element | null;
47
+ /** True if `ancestor` contains `node` (inclusive). Always false on the server. */
48
+ contains(ancestor: Element, node: Node): boolean;
49
+ /** True if `element` matches the given CSS selector. Always false on the server. */
50
+ matches(element: Element, selector: string): boolean;
32
51
  isElement(node: Node): node is Element;
33
52
  isTextNode(node: Node): node is Text;
34
53
  /** Lower-cased tag name of an element (e.g. "div", "h1"). */
@@ -66,6 +85,10 @@ declare class BrowserDOMAdapter implements DOMAdapter {
66
85
  querySelectorAll(root: Element | Document, selector: string): NodeListOf<Element>;
67
86
  getElementById(id: string): Element | null;
68
87
  focus(element: Element): void;
88
+ body(): Element | null;
89
+ activeElement(): Element | null;
90
+ contains(ancestor: Element, node: Node): boolean;
91
+ matches(element: Element, selector: string): boolean;
69
92
  isElement(node: Node): node is Element;
70
93
  isTextNode(node: Node): node is Text;
71
94
  tagName(element: Element): string;
@@ -88,7 +111,7 @@ declare const browserDOMAdapter: BrowserDOMAdapter;
88
111
  * `DOMAdapter` interface, and `ServerDOMAdapter` translates adapter calls into
89
112
  * operations on this model.
90
113
  */
91
- type ServerNodeKind = 'element' | 'text' | 'comment' | 'fragment';
114
+ type ServerNodeKind = 'element' | 'text' | 'comment' | 'fragment' | 'raw';
92
115
  interface ServerNode {
93
116
  readonly kind: ServerNodeKind;
94
117
  parent: ServerParent | null;
@@ -118,6 +141,25 @@ declare class ServerFragment implements ServerNode {
118
141
  parent: ServerParent | null;
119
142
  readonly children: ServerNode[];
120
143
  }
144
+ /**
145
+ * A pre-serialized, verbatim HTML fragment (v1.7 static SSR plan).
146
+ *
147
+ * Emitted for provably-static subtrees whose HTML the compiler-derived static
148
+ * SSR plan already computed once. Serializing this node copies its stored
149
+ * string directly — it allocates no ServerElement/ServerText, no attribute Map
150
+ * and no children array for the collapsed subtree. The stored `html` is
151
+ * produced by the exact same mount + serialize pipeline as the runtime path, so
152
+ * the output is byte-identical (the v1.7 byte-identity gate proves this).
153
+ *
154
+ * This node is SSR-only: it is created solely via `ServerDOMAdapter.createRawHTML`
155
+ * on the server render path and never appears in a browser build.
156
+ */
157
+ declare class ServerRawHTML implements ServerNode {
158
+ readonly kind: "raw";
159
+ parent: ServerParent | null;
160
+ readonly html: string;
161
+ constructor(html: string);
162
+ }
121
163
  declare class ServerElement implements ServerNode {
122
164
  readonly kind: "element";
123
165
  parent: ServerParent | null;
@@ -136,7 +178,7 @@ declare class ServerElement implements ServerNode {
136
178
  declare function escapeHtmlText(value: string): string;
137
179
  /** Escape a double-quoted attribute value. */
138
180
  declare function escapeHtmlAttr(value: string): string;
139
- /** Serialize a single server node (element/text/comment/fragment) to HTML. */
181
+ /** Serialize a single server node (element/text/comment/fragment/raw) to HTML. */
140
182
  declare function serializeServerNode(node: ServerNode): string;
141
183
  /** Serialize the children of an element or fragment (its "inner HTML"). */
142
184
  declare function serializeChildren(node: ServerElement | ServerFragment): string;
@@ -160,6 +202,13 @@ declare class ServerDOMAdapter implements DOMAdapter {
160
202
  createTextNode(data: string): Text;
161
203
  createComment(data: string): Comment;
162
204
  createFragment(): DocumentFragment;
205
+ /**
206
+ * Create a verbatim pre-serialized HTML node (v1.7 static SSR plan, §6).
207
+ * Server-only: the browser adapter does not implement this, and the renderer
208
+ * fast path only invokes it when a static SSR plan is present (SSR). The
209
+ * stored HTML was produced by this same serializer, so it is emitted as-is.
210
+ */
211
+ createRawHTML(html: string): Node;
163
212
  appendChild(parent: Node, child: Node): void;
164
213
  insertBefore(parent: Node, child: Node, reference: Node | null): void;
165
214
  removeChild(parent: Node, child: Node): void;
@@ -177,6 +226,10 @@ declare class ServerDOMAdapter implements DOMAdapter {
177
226
  querySelectorAll(): NodeListOf<Element>;
178
227
  getElementById(): Element | null;
179
228
  focus(): void;
229
+ body(): Element | null;
230
+ activeElement(): Element | null;
231
+ contains(_ancestor: Element, _node: Node): boolean;
232
+ matches(_element: Element, _selector: string): boolean;
180
233
  isElement(node: Node): node is Element;
181
234
  isTextNode(node: Node): node is Text;
182
235
  tagName(element: Element): string;
@@ -214,5 +267,41 @@ declare function focusById(dom: DOMAdapter, root: Element | Document, id: string
214
267
  * Returns true if a focusable element was found and focused.
215
268
  */
216
269
  declare function focusFirst(dom: DOMAdapter, container: Element | Document, selector?: string): boolean;
270
+ /**
271
+ * Ordered list of focusable/tabbable descendants of `container`.
272
+ * Re-checks each candidate against the selector so elements disabled after the
273
+ * initial query (e.g. a button toggled to `disabled`) are excluded.
274
+ */
275
+ declare function getFocusable(dom: DOMAdapter, container: Element, selector?: string): Element[];
276
+ /**
277
+ * Capture the currently-focused element so it can be restored later (e.g. when
278
+ * a dialog closes). Returns null on the server or when nothing is focused.
279
+ */
280
+ declare function saveFocus(dom: DOMAdapter): Element | null;
281
+ /** Restore focus to a previously {@link saveFocus}-d element. No-op if null. */
282
+ declare function restoreFocus(dom: DOMAdapter, saved: Element | null): void;
283
+ /**
284
+ * Move focus into `container` on open: the element with id `initialFocusId` if
285
+ * given and present, otherwise the first focusable element. Server-safe no-op.
286
+ */
287
+ declare function focusInitial(dom: DOMAdapter, container: Element, initialFocusId?: string): void;
288
+ /**
289
+ * Trap Tab / Shift+Tab focus within `container` (wrap-around at both ends).
290
+ * Attaches a keydown listener to the container and returns a cleanup function
291
+ * that detaches it. Server-safe: `addEventListener` is a no-op, and the returned
292
+ * cleanup is still callable.
293
+ */
294
+ declare function trapFocus(dom: DOMAdapter, container: Element): () => void;
295
+ /**
296
+ * Modal containment: if focus moves to an element outside `container`, redirect
297
+ * it back inside. Listens on the document body (focusin bubbles there) and
298
+ * returns a cleanup function. Server-safe no-op (body() is null).
299
+ */
300
+ declare function containFocus(dom: DOMAdapter, container: Element): () => void;
301
+ /**
302
+ * Invoke `handler` when Escape is pressed while focus is within `target`.
303
+ * Returns a cleanup function. Server-safe no-op.
304
+ */
305
+ declare function onEscape(dom: DOMAdapter, target: Element, handler: () => void): () => void;
217
306
 
218
- export { BrowserDOMAdapter, type DOMAdapter, FOCUSABLE_SELECTOR, ServerComment, ServerDOMAdapter, ServerElement, ServerFragment, type ServerNode, type ServerNodeKind, type ServerParent, ServerStyle, ServerText, browserDOMAdapter, escapeHtmlAttr, escapeHtmlText, focusById, focusFirst, serializeChildren, serializeServerNode, serverDOMAdapter };
307
+ export { BrowserDOMAdapter, type DOMAdapter, FOCUSABLE_SELECTOR, ServerComment, ServerDOMAdapter, ServerElement, ServerFragment, type ServerNode, type ServerNodeKind, type ServerParent, ServerRawHTML, ServerStyle, ServerText, browserDOMAdapter, containFocus, escapeHtmlAttr, escapeHtmlText, focusById, focusFirst, focusInitial, getFocusable, onEscape, restoreFocus, saveFocus, serializeChildren, serializeServerNode, serverDOMAdapter, trapFocus };
package/dist/index.js CHANGED
@@ -63,6 +63,18 @@ var BrowserDOMAdapter = class {
63
63
  focus(element) {
64
64
  element.focus?.();
65
65
  }
66
+ body() {
67
+ return document.body ?? null;
68
+ }
69
+ activeElement() {
70
+ return document.activeElement ?? null;
71
+ }
72
+ contains(ancestor, node) {
73
+ return ancestor.contains(node);
74
+ }
75
+ matches(element, selector) {
76
+ return typeof element.matches === "function" && element.matches(selector);
77
+ }
66
78
  isElement(node) {
67
79
  return node.nodeType === Node.ELEMENT_NODE;
68
80
  }
@@ -121,6 +133,14 @@ var ServerFragment = class {
121
133
  parent = null;
122
134
  children = [];
123
135
  };
136
+ var ServerRawHTML = class {
137
+ kind = "raw";
138
+ parent = null;
139
+ html;
140
+ constructor(html) {
141
+ this.html = html;
142
+ }
143
+ };
124
144
  var ServerElement = class {
125
145
  kind = "element";
126
146
  parent = null;
@@ -271,6 +291,8 @@ function serializeServerNode(node) {
271
291
  return `<!--${node.data}-->`;
272
292
  case "fragment":
273
293
  return serializeChildren(node);
294
+ case "raw":
295
+ return node.html;
274
296
  case "element": {
275
297
  const el = node;
276
298
  const tag = el.tagName;
@@ -310,6 +332,15 @@ var ServerDOMAdapter = class {
310
332
  createFragment() {
311
333
  return new ServerFragment();
312
334
  }
335
+ /**
336
+ * Create a verbatim pre-serialized HTML node (v1.7 static SSR plan, §6).
337
+ * Server-only: the browser adapter does not implement this, and the renderer
338
+ * fast path only invokes it when a static SSR plan is present (SSR). The
339
+ * stored HTML was produced by this same serializer, so it is emitted as-is.
340
+ */
341
+ createRawHTML(html) {
342
+ return new ServerRawHTML(html);
343
+ }
313
344
  appendChild(parent, child) {
314
345
  const p = asParent(parent);
315
346
  const c = asServer(child);
@@ -411,6 +442,18 @@ var ServerDOMAdapter = class {
411
442
  }
412
443
  focus() {
413
444
  }
445
+ body() {
446
+ return null;
447
+ }
448
+ activeElement() {
449
+ return null;
450
+ }
451
+ contains(_ancestor, _node) {
452
+ return false;
453
+ }
454
+ matches(_element, _selector) {
455
+ return false;
456
+ }
414
457
  isElement(node) {
415
458
  return asServer(node).kind === "element";
416
459
  }
@@ -476,6 +519,66 @@ function focusFirst(dom, container, selector = FOCUSABLE_SELECTOR) {
476
519
  dom.focus(el);
477
520
  return true;
478
521
  }
522
+ function getFocusable(dom, container, selector = FOCUSABLE_SELECTOR) {
523
+ return Array.from(dom.querySelectorAll(container, selector)).filter(
524
+ (el) => dom.matches(el, selector)
525
+ );
526
+ }
527
+ function saveFocus(dom) {
528
+ return dom.activeElement();
529
+ }
530
+ function restoreFocus(dom, saved) {
531
+ if (saved !== null) dom.focus(saved);
532
+ }
533
+ function focusInitial(dom, container, initialFocusId) {
534
+ if (initialFocusId !== void 0 && focusById(dom, container, initialFocusId)) return;
535
+ focusFirst(dom, container);
536
+ }
537
+ function trapFocus(dom, container) {
538
+ const onKeydown = (event) => {
539
+ if (event.key !== "Tab") return;
540
+ const items = getFocusable(dom, container);
541
+ if (items.length === 0) {
542
+ event.preventDefault();
543
+ return;
544
+ }
545
+ const first = items[0];
546
+ const last = items[items.length - 1];
547
+ const active = dom.activeElement();
548
+ if (active === null || !dom.contains(container, active)) {
549
+ event.preventDefault();
550
+ dom.focus(first);
551
+ } else if (event.shiftKey && active === first) {
552
+ event.preventDefault();
553
+ dom.focus(last);
554
+ } else if (!event.shiftKey && active === last) {
555
+ event.preventDefault();
556
+ dom.focus(first);
557
+ }
558
+ };
559
+ dom.addEventListener(container, "keydown", onKeydown);
560
+ return () => dom.removeEventListener(container, "keydown", onKeydown);
561
+ }
562
+ function containFocus(dom, container) {
563
+ const body = dom.body();
564
+ if (body === null) return () => {
565
+ };
566
+ const onFocusIn = (event) => {
567
+ const target = event.target;
568
+ if (target !== null && !dom.contains(container, target)) {
569
+ focusFirst(dom, container);
570
+ }
571
+ };
572
+ dom.addEventListener(body, "focusin", onFocusIn);
573
+ return () => dom.removeEventListener(body, "focusin", onFocusIn);
574
+ }
575
+ function onEscape(dom, target, handler) {
576
+ const onKeydown = (event) => {
577
+ if (event.key === "Escape") handler();
578
+ };
579
+ dom.addEventListener(target, "keydown", onKeydown);
580
+ return () => dom.removeEventListener(target, "keydown", onKeydown);
581
+ }
479
582
  export {
480
583
  BrowserDOMAdapter,
481
584
  FOCUSABLE_SELECTOR,
@@ -483,15 +586,23 @@ export {
483
586
  ServerDOMAdapter,
484
587
  ServerElement,
485
588
  ServerFragment,
589
+ ServerRawHTML,
486
590
  ServerStyle,
487
591
  ServerText,
488
592
  browserDOMAdapter,
593
+ containFocus,
489
594
  escapeHtmlAttr,
490
595
  escapeHtmlText,
491
596
  focusById,
492
597
  focusFirst,
598
+ focusInitial,
599
+ getFocusable,
600
+ onEscape,
601
+ restoreFocus,
602
+ saveFocus,
493
603
  serializeChildren,
494
604
  serializeServerNode,
495
- serverDOMAdapter
605
+ serverDOMAdapter,
606
+ trapFocus
496
607
  };
497
608
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/browser-adapter.ts","../src/server-node.ts","../src/server-adapter.ts","../src/focus.ts"],"sourcesContent":["/**\n * Browser implementation of DOMAdapter — delegates directly to browser APIs.\n */\n\nimport type { DOMAdapter } from './adapter.js';\n\nexport class BrowserDOMAdapter implements DOMAdapter {\n createElement(tag: string, ns?: string): Element {\n if (ns !== undefined) {\n return document.createElementNS(ns, tag);\n }\n return document.createElement(tag);\n }\n\n createTextNode(data: string): Text {\n return document.createTextNode(data);\n }\n\n createComment(data: string): Comment {\n return document.createComment(data);\n }\n\n createFragment(): DocumentFragment {\n return document.createDocumentFragment();\n }\n\n appendChild(parent: Node, child: Node): void {\n parent.appendChild(child);\n }\n\n insertBefore(parent: Node, child: Node, reference: Node | null): void {\n parent.insertBefore(child, reference);\n }\n\n removeChild(parent: Node, child: Node): void {\n parent.removeChild(child);\n }\n\n replaceChild(parent: Node, newChild: Node, oldChild: Node): void {\n parent.replaceChild(newChild, oldChild);\n }\n\n setAttribute(element: Element, name: string, value: string): void {\n element.setAttribute(name, value);\n }\n\n removeAttribute(element: Element, name: string): void {\n element.removeAttribute(name);\n }\n\n getAttribute(element: Element, name: string): string | null {\n return element.getAttribute(name);\n }\n\n setProperty(element: Element, name: string, value: unknown): void {\n (element as unknown as Record<string, unknown>)[name] = value;\n }\n\n setTextContent(node: Node, text: string): void {\n node.textContent = text;\n }\n\n getTextContent(node: Node): string | null {\n return node.textContent;\n }\n\n addEventListener(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n ): void {\n target.addEventListener(type, handler, options);\n }\n\n removeEventListener(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: EventListenerOptions,\n ): void {\n target.removeEventListener(type, handler, options);\n }\n\n querySelector(root: Element | Document, selector: string): Element | null {\n return root.querySelector(selector);\n }\n\n querySelectorAll(root: Element | Document, selector: string): NodeListOf<Element> {\n return root.querySelectorAll(selector);\n }\n\n getElementById(id: string): Element | null {\n return document.getElementById(id);\n }\n\n focus(element: Element): void {\n (element as unknown as { focus?: () => void }).focus?.();\n }\n\n isElement(node: Node): node is Element {\n return node.nodeType === Node.ELEMENT_NODE;\n }\n\n isTextNode(node: Node): node is Text {\n return node.nodeType === Node.TEXT_NODE;\n }\n\n tagName(element: Element): string {\n return element.tagName.toLowerCase();\n }\n\n parentNode(node: Node): Node | null {\n return node.parentNode;\n }\n\n nextSibling(node: Node): Node | null {\n return node.nextSibling;\n }\n\n firstChild(node: Node): Node | null {\n return node.firstChild;\n }\n\n childNodes(node: Node): Node[] {\n return Array.from(node.childNodes);\n }\n}\n\n// `/* @__PURE__ */`: convenience singleton, unreferenced by internal runtime\n// paths. Marking construction pure lets bundlers drop it when unused instead of\n// retaining it (and the BrowserDOMAdapter class) as an import-time side effect.\nexport const browserDOMAdapter = /* @__PURE__ */ new BrowserDOMAdapter();\n","/**\n * Server-side DOM node model.\n *\n * A tiny, dependency-free tree of plain objects that mirrors just enough of the\n * browser DOM for StreetUI's renderer to build a tree on the server and\n * serialize it to an HTML string. There is NO browser global here — these are\n * ordinary classes usable in any JavaScript environment (Node, workers, tests).\n *\n * The renderer never touches these types directly; it goes through the\n * `DOMAdapter` interface, and `ServerDOMAdapter` translates adapter calls into\n * operations on this model.\n */\n\nexport type ServerNodeKind = 'element' | 'text' | 'comment' | 'fragment';\n\nexport interface ServerNode {\n readonly kind: ServerNodeKind;\n parent: ServerParent | null;\n}\n\nexport type ServerParent = ServerElement | ServerFragment;\n\n/** A minimal inline-style holder mirroring `element.style.setProperty`. */\nexport class ServerStyle {\n readonly declarations = new Map<string, string>();\n setProperty(name: string, value: string): void {\n this.declarations.set(name, value);\n }\n get isEmpty(): boolean {\n return this.declarations.size === 0;\n }\n toCss(): string {\n return [...this.declarations.entries()].map(([k, v]) => `${k}: ${v}`).join('; ');\n }\n}\n\nexport class ServerText implements ServerNode {\n readonly kind = 'text' as const;\n parent: ServerParent | null = null;\n data: string;\n constructor(data: string) {\n this.data = data;\n }\n}\n\nexport class ServerComment implements ServerNode {\n readonly kind = 'comment' as const;\n parent: ServerParent | null = null;\n data: string;\n constructor(data: string) {\n this.data = data;\n }\n}\n\nexport class ServerFragment implements ServerNode {\n readonly kind = 'fragment' as const;\n parent: ServerParent | null = null;\n readonly children: ServerNode[] = [];\n}\n\nexport class ServerElement implements ServerNode {\n readonly kind = 'element' as const;\n parent: ServerParent | null = null;\n readonly tagName: string;\n readonly attributes = new Map<string, string>();\n readonly children: ServerNode[] = [];\n\n // Lazily-allocated stores. On the 10k-row SSR corpus ~0% of elements carry JS\n // properties or inline styles (measured, §5: 1 of 80,029 elements uses\n // `properties`, 0 use `style`), so eagerly allocating a `properties` Map plus\n // a `ServerStyle` (which itself holds a Map) per element wasted ~240k\n // allocations per /users render — all in the dominant mount phase. These are\n // created on first WRITE via the `properties`/`style` getters; the serializer\n // reads the raw `_properties`/`_style` fields so a READ never forces an\n // allocation. Output is byte-identical: an unset store previously serialized\n // to nothing (empty `properties.has(...)` / `style.isEmpty`), and a null store\n // is skipped the same way.\n _properties: Map<string, unknown> | null = null;\n _style: ServerStyle | null = null;\n\n constructor(tagName: string) {\n this.tagName = tagName.toLowerCase();\n }\n\n /** JS properties set via `setProperty` (e.g. input `value`, `checked`). Allocated on first access. */\n get properties(): Map<string, unknown> {\n return (this._properties ??= new Map());\n }\n\n /** Inline-style holder mirroring `element.style`. Allocated on first access. */\n get style(): ServerStyle {\n return (this._style ??= new ServerStyle());\n }\n}\n\n// ── HTML serialization ─────────────────────────────────────────────────────────\n\n/**\n * HTML \"void\" elements — self-closing, never given a closing tag or children.\n */\nconst VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n/**\n * Element properties that should be reflected into the serialized HTML so the\n * hydrated DOM carries the same initial state. `value`/`checked` matter for\n * form controls whose live state is a JS property, not an attribute.\n */\nconst SERIALIZED_PROPERTIES: Record<string, 'attr' | 'boolean'> = {\n value: 'attr',\n checked: 'boolean',\n selected: 'boolean',\n};\n\n/**\n * Precomputed `[name, kind]` pairs of SERIALIZED_PROPERTIES. Hoisted to module\n * scope so `serializeAttributes` does not allocate a fresh entries array for\n * every element serialized (measured hot: ~80k elements on the 10k-row route).\n */\nconst SERIALIZED_PROPERTY_ENTRIES: ReadonlyArray<readonly [string, 'attr' | 'boolean']> =\n Object.entries(SERIALIZED_PROPERTIES) as Array<[string, 'attr' | 'boolean']>;\n\n// Fast-path escaping. The chained `.replace(/…/g, …)` form makes 3–4 full\n// passes and allocates an intermediate string per pass even when nothing needs\n// escaping. These variants scan once and, in the overwhelmingly common case of\n// no special character, return the input unchanged (zero allocation). Output is\n// byte-identical to the chained form (verified over the real SSR corpus).\nconst TEXT_SPECIAL = /[&<>]/;\nconst ATTR_SPECIAL = /[&<>\"]/;\n\n/** Escape text node content. */\nexport function escapeHtmlText(value: string): string {\n if (!TEXT_SPECIAL.test(value)) return value;\n let out = '';\n let last = 0;\n for (let i = 0; i < value.length; i++) {\n let esc: string;\n switch (value.charCodeAt(i)) {\n case 38: esc = '&amp;'; break; // &\n case 60: esc = '&lt;'; break; // <\n case 62: esc = '&gt;'; break; // >\n default: continue;\n }\n out += value.slice(last, i) + esc;\n last = i + 1;\n }\n return out + value.slice(last);\n}\n\n/** Escape a double-quoted attribute value. */\nexport function escapeHtmlAttr(value: string): string {\n if (!ATTR_SPECIAL.test(value)) return value;\n let out = '';\n let last = 0;\n for (let i = 0; i < value.length; i++) {\n let esc: string;\n switch (value.charCodeAt(i)) {\n case 38: esc = '&amp;'; break; // &\n case 60: esc = '&lt;'; break; // <\n case 62: esc = '&gt;'; break; // >\n case 34: esc = '&quot;'; break; // \"\n default: continue;\n }\n out += value.slice(last, i) + esc;\n last = i + 1;\n }\n return out + value.slice(last);\n}\n\nfunction serializeAttributes(el: ServerElement): string {\n const parts: string[] = [];\n\n for (const [name, value] of el.attributes) {\n if (value === '') {\n parts.push(` ${name}`);\n } else {\n parts.push(` ${name}=\"${escapeHtmlAttr(value)}\"`);\n }\n }\n\n // Read the raw backing field (may be null): most elements have no JS\n // properties, so skipping the whole loop avoids touching a store that was\n // never allocated (§5).\n const props = el._properties;\n if (props !== null) {\n for (const [name, kind] of SERIALIZED_PROPERTY_ENTRIES) {\n if (!props.has(name)) continue;\n if (el.attributes.has(name)) continue; // an explicit attribute already won\n const raw = props.get(name);\n if (kind === 'boolean') {\n if (raw === true) parts.push(` ${name}`);\n } else {\n if (raw !== undefined && raw !== null) {\n parts.push(` ${name}=\"${escapeHtmlAttr(String(raw))}\"`);\n }\n }\n }\n }\n\n const style = el._style;\n if (style !== null && !style.isEmpty && !el.attributes.has('style')) {\n parts.push(` style=\"${escapeHtmlAttr(style.toCss())}\"`);\n }\n\n return parts.join('');\n}\n\n/** Serialize a single server node (element/text/comment/fragment) to HTML. */\nexport function serializeServerNode(node: ServerNode): string {\n switch (node.kind) {\n case 'text':\n return escapeHtmlText((node as ServerText).data);\n case 'comment':\n return `<!--${(node as ServerComment).data}-->`;\n case 'fragment':\n return serializeChildren(node as ServerFragment);\n case 'element': {\n const el = node as ServerElement;\n const tag = el.tagName;\n const attrs = serializeAttributes(el);\n if (VOID_ELEMENTS.has(tag)) {\n return `<${tag}${attrs}>`;\n }\n return `<${tag}${attrs}>${serializeChildren(el)}</${tag}>`;\n }\n }\n}\n\n/** Serialize the children of an element or fragment (its \"inner HTML\"). */\nexport function serializeChildren(node: ServerElement | ServerFragment): string {\n let out = '';\n for (const child of node.children) {\n out += serializeServerNode(child);\n }\n return out;\n}\n","/**\n * Server implementation of `DOMAdapter`.\n *\n * Builds a lightweight in-memory tree (see `server-node.ts`) instead of touching\n * a real browser DOM, then lets the caller serialize it to an HTML string. It is\n * completely free of browser globals, so the exact same renderer that runs in\n * the browser can produce HTML on the server.\n *\n * The `DOMAdapter` interface is typed against the lib DOM types (`Element`,\n * `Node`, `Text`, …). Our server nodes structurally stand in for those at\n * runtime, so the boundary uses `as unknown as` casts in one place. Everything\n * inside operates on the real server-node model.\n */\n\nimport type { DOMAdapter } from './adapter.js';\nimport {\n ServerElement,\n ServerText,\n ServerComment,\n ServerFragment,\n serializeChildren,\n serializeServerNode,\n type ServerNode,\n type ServerParent,\n} from './server-node.js';\n\nfunction asServer(node: unknown): ServerNode {\n return node as unknown as ServerNode;\n}\nfunction asParent(node: unknown): ServerParent {\n return node as unknown as ServerParent;\n}\n\nexport class ServerDOMAdapter implements DOMAdapter {\n createElement(tag: string, _ns?: string): Element {\n return new ServerElement(tag) as unknown as Element;\n }\n\n createTextNode(data: string): Text {\n return new ServerText(data) as unknown as Text;\n }\n\n createComment(data: string): Comment {\n return new ServerComment(data) as unknown as Comment;\n }\n\n createFragment(): DocumentFragment {\n return new ServerFragment() as unknown as DocumentFragment;\n }\n\n appendChild(parent: Node, child: Node): void {\n const p = asParent(parent);\n const c = asServer(child);\n this._detach(c);\n c.parent = p;\n p.children.push(c);\n }\n\n insertBefore(parent: Node, child: Node, reference: Node | null): void {\n const p = asParent(parent);\n const c = asServer(child);\n this._detach(c);\n c.parent = p;\n if (reference === null) {\n p.children.push(c);\n return;\n }\n const ref = asServer(reference);\n const idx = p.children.indexOf(ref);\n if (idx === -1) p.children.push(c);\n else p.children.splice(idx, 0, c);\n }\n\n removeChild(parent: Node, child: Node): void {\n const p = asParent(parent);\n const c = asServer(child);\n const idx = p.children.indexOf(c);\n if (idx !== -1) {\n p.children.splice(idx, 1);\n c.parent = null;\n }\n }\n\n replaceChild(parent: Node, newChild: Node, oldChild: Node): void {\n const p = asParent(parent);\n const nc = asServer(newChild);\n const oc = asServer(oldChild);\n const idx = p.children.indexOf(oc);\n if (idx === -1) return;\n this._detach(nc);\n nc.parent = p;\n p.children.splice(idx, 1, nc);\n oc.parent = null;\n }\n\n private _detach(node: ServerNode): void {\n if (node.parent !== null) {\n const siblings = node.parent.children;\n const idx = siblings.indexOf(node);\n if (idx !== -1) siblings.splice(idx, 1);\n node.parent = null;\n }\n }\n\n setAttribute(element: Element, name: string, value: string): void {\n (element as unknown as ServerElement).attributes.set(name, value);\n }\n\n removeAttribute(element: Element, name: string): void {\n (element as unknown as ServerElement).attributes.delete(name);\n }\n\n getAttribute(element: Element, name: string): string | null {\n return (element as unknown as ServerElement).attributes.get(name) ?? null;\n }\n\n setProperty(element: Element, name: string, value: unknown): void {\n (element as unknown as ServerElement).properties.set(name, value);\n }\n\n setTextContent(node: Node, text: string): void {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n const el = n as ServerElement | ServerFragment;\n el.children.length = 0;\n const t = new ServerText(text);\n t.parent = el;\n el.children.push(t);\n } else if (n.kind === 'text') {\n (n as ServerText).data = text;\n }\n }\n\n getTextContent(node: Node): string | null {\n const n = asServer(node);\n if (n.kind === 'text') return (n as ServerText).data;\n if (n.kind === 'element' || n.kind === 'fragment') {\n let out = '';\n for (const c of (n as ServerElement | ServerFragment).children) {\n out += this.getTextContent(c as unknown as Node) ?? '';\n }\n return out;\n }\n return null;\n }\n\n // Server nodes never dispatch events — listeners are a no-op on the server.\n addEventListener(): void {\n /* no-op on the server */\n }\n removeEventListener(): void {\n /* no-op on the server */\n }\n\n querySelector(): Element | null {\n return null;\n }\n querySelectorAll(): NodeListOf<Element> {\n return [] as unknown as NodeListOf<Element>;\n }\n getElementById(): Element | null {\n return null;\n }\n\n focus(): void {\n // No focus concept on the server — intentional no-op (SSR-safe).\n }\n\n isElement(node: Node): node is Element {\n return asServer(node).kind === 'element';\n }\n\n isTextNode(node: Node): node is Text {\n return asServer(node).kind === 'text';\n }\n\n tagName(element: Element): string {\n return (element as unknown as ServerElement).tagName;\n }\n\n parentNode(node: Node): Node | null {\n return (asServer(node).parent as unknown as Node | null) ?? null;\n }\n\n nextSibling(node: Node): Node | null {\n const n = asServer(node);\n const parent = n.parent;\n if (parent === null) return null;\n const idx = parent.children.indexOf(n);\n if (idx === -1 || idx + 1 >= parent.children.length) return null;\n return parent.children[idx + 1] as unknown as Node;\n }\n\n firstChild(node: Node): Node | null {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n const el = n as ServerElement | ServerFragment;\n return (el.children[0] as unknown as Node) ?? null;\n }\n return null;\n }\n\n childNodes(node: Node): Node[] {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n return (n as ServerElement | ServerFragment).children as unknown as Node[];\n }\n return [];\n }\n\n // ── Server-only ────────────────────────────────────────────────────────────\n\n /** Serialize a node's children (\"inner HTML\") to an HTML string. */\n serializeInner(node: Node): string {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n return serializeChildren(n as ServerElement | ServerFragment);\n }\n return '';\n }\n\n /** Serialize a node (including itself) to an HTML string. */\n serializeOuter(node: Node): string {\n return serializeServerNode(asServer(node));\n }\n}\n\n// `/* @__PURE__ */`: this singleton is a convenience export only (no internal\n// runtime path references it). Marking construction pure lets bundlers drop it —\n// and with it the whole server serializer chain (serializeServerNode/escape/\n// VOID_ELEMENTS) — out of client bundles that never import SSR. Without this,\n// the un-annotated `new` is treated as a side effect and retained everywhere.\nexport const serverDOMAdapter = /* @__PURE__ */ new ServerDOMAdapter();\n","/**\n * Focus helpers built on the {@link DOMAdapter} abstraction.\n *\n * These are the minimal, genuinely-useful focus operations an app needs:\n * focus a specific element (e.g. the first field when a route or modal opens)\n * or focus the first focusable element inside a container (e.g. move focus\n * into a dialog). Both go through the adapter, so they are no-ops on the server\n * (`ServerDOMAdapter.querySelector` returns null / `focus` does nothing) and\n * therefore safe to call from universal code.\n */\n\nimport type { DOMAdapter } from './adapter.js';\n\n/** Default selector for natively focusable / tabbable elements. */\nexport const FOCUSABLE_SELECTOR =\n 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Focus the element with the given id, scoped to `root`.\n * Returns true if an element was found and focused.\n */\nexport function focusById(dom: DOMAdapter, root: Element | Document, id: string): boolean {\n const el = dom.querySelector(root, `[id=\"${id}\"]`);\n if (el === null) return false;\n dom.focus(el);\n return true;\n}\n\n/**\n * Focus the first focusable element inside `container`.\n * Returns true if a focusable element was found and focused.\n */\nexport function focusFirst(\n dom: DOMAdapter,\n container: Element | Document,\n selector: string = FOCUSABLE_SELECTOR,\n): boolean {\n const el = dom.querySelector(container, selector);\n if (el === null) return false;\n dom.focus(el);\n return true;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/browser-adapter.ts","../src/server-node.ts","../src/server-adapter.ts","../src/focus.ts"],"sourcesContent":["/**\n * Browser implementation of DOMAdapter — delegates directly to browser APIs.\n */\n\nimport type { DOMAdapter } from './adapter.js';\n\nexport class BrowserDOMAdapter implements DOMAdapter {\n createElement(tag: string, ns?: string): Element {\n if (ns !== undefined) {\n return document.createElementNS(ns, tag);\n }\n return document.createElement(tag);\n }\n\n createTextNode(data: string): Text {\n return document.createTextNode(data);\n }\n\n createComment(data: string): Comment {\n return document.createComment(data);\n }\n\n createFragment(): DocumentFragment {\n return document.createDocumentFragment();\n }\n\n appendChild(parent: Node, child: Node): void {\n parent.appendChild(child);\n }\n\n insertBefore(parent: Node, child: Node, reference: Node | null): void {\n parent.insertBefore(child, reference);\n }\n\n removeChild(parent: Node, child: Node): void {\n parent.removeChild(child);\n }\n\n replaceChild(parent: Node, newChild: Node, oldChild: Node): void {\n parent.replaceChild(newChild, oldChild);\n }\n\n setAttribute(element: Element, name: string, value: string): void {\n element.setAttribute(name, value);\n }\n\n removeAttribute(element: Element, name: string): void {\n element.removeAttribute(name);\n }\n\n getAttribute(element: Element, name: string): string | null {\n return element.getAttribute(name);\n }\n\n setProperty(element: Element, name: string, value: unknown): void {\n (element as unknown as Record<string, unknown>)[name] = value;\n }\n\n setTextContent(node: Node, text: string): void {\n node.textContent = text;\n }\n\n getTextContent(node: Node): string | null {\n return node.textContent;\n }\n\n addEventListener(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: AddEventListenerOptions,\n ): void {\n target.addEventListener(type, handler, options);\n }\n\n removeEventListener(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: EventListenerOptions,\n ): void {\n target.removeEventListener(type, handler, options);\n }\n\n querySelector(root: Element | Document, selector: string): Element | null {\n return root.querySelector(selector);\n }\n\n querySelectorAll(root: Element | Document, selector: string): NodeListOf<Element> {\n return root.querySelectorAll(selector);\n }\n\n getElementById(id: string): Element | null {\n return document.getElementById(id);\n }\n\n focus(element: Element): void {\n (element as unknown as { focus?: () => void }).focus?.();\n }\n\n body(): Element | null {\n return document.body ?? null;\n }\n\n activeElement(): Element | null {\n return document.activeElement ?? null;\n }\n\n contains(ancestor: Element, node: Node): boolean {\n return ancestor.contains(node);\n }\n\n matches(element: Element, selector: string): boolean {\n return typeof element.matches === 'function' && element.matches(selector);\n }\n\n isElement(node: Node): node is Element {\n return node.nodeType === Node.ELEMENT_NODE;\n }\n\n isTextNode(node: Node): node is Text {\n return node.nodeType === Node.TEXT_NODE;\n }\n\n tagName(element: Element): string {\n return element.tagName.toLowerCase();\n }\n\n parentNode(node: Node): Node | null {\n return node.parentNode;\n }\n\n nextSibling(node: Node): Node | null {\n return node.nextSibling;\n }\n\n firstChild(node: Node): Node | null {\n return node.firstChild;\n }\n\n childNodes(node: Node): Node[] {\n return Array.from(node.childNodes);\n }\n}\n\n// `/* @__PURE__ */`: convenience singleton, unreferenced by internal runtime\n// paths. Marking construction pure lets bundlers drop it when unused instead of\n// retaining it (and the BrowserDOMAdapter class) as an import-time side effect.\nexport const browserDOMAdapter = /* @__PURE__ */ new BrowserDOMAdapter();\n","/**\n * Server-side DOM node model.\n *\n * A tiny, dependency-free tree of plain objects that mirrors just enough of the\n * browser DOM for StreetUI's renderer to build a tree on the server and\n * serialize it to an HTML string. There is NO browser global here — these are\n * ordinary classes usable in any JavaScript environment (Node, workers, tests).\n *\n * The renderer never touches these types directly; it goes through the\n * `DOMAdapter` interface, and `ServerDOMAdapter` translates adapter calls into\n * operations on this model.\n */\n\nexport type ServerNodeKind = 'element' | 'text' | 'comment' | 'fragment' | 'raw';\n\nexport interface ServerNode {\n readonly kind: ServerNodeKind;\n parent: ServerParent | null;\n}\n\nexport type ServerParent = ServerElement | ServerFragment;\n\n/** A minimal inline-style holder mirroring `element.style.setProperty`. */\nexport class ServerStyle {\n readonly declarations = new Map<string, string>();\n setProperty(name: string, value: string): void {\n this.declarations.set(name, value);\n }\n get isEmpty(): boolean {\n return this.declarations.size === 0;\n }\n toCss(): string {\n return [...this.declarations.entries()].map(([k, v]) => `${k}: ${v}`).join('; ');\n }\n}\n\nexport class ServerText implements ServerNode {\n readonly kind = 'text' as const;\n parent: ServerParent | null = null;\n data: string;\n constructor(data: string) {\n this.data = data;\n }\n}\n\nexport class ServerComment implements ServerNode {\n readonly kind = 'comment' as const;\n parent: ServerParent | null = null;\n data: string;\n constructor(data: string) {\n this.data = data;\n }\n}\n\nexport class ServerFragment implements ServerNode {\n readonly kind = 'fragment' as const;\n parent: ServerParent | null = null;\n readonly children: ServerNode[] = [];\n}\n\n/**\n * A pre-serialized, verbatim HTML fragment (v1.7 static SSR plan).\n *\n * Emitted for provably-static subtrees whose HTML the compiler-derived static\n * SSR plan already computed once. Serializing this node copies its stored\n * string directly — it allocates no ServerElement/ServerText, no attribute Map\n * and no children array for the collapsed subtree. The stored `html` is\n * produced by the exact same mount + serialize pipeline as the runtime path, so\n * the output is byte-identical (the v1.7 byte-identity gate proves this).\n *\n * This node is SSR-only: it is created solely via `ServerDOMAdapter.createRawHTML`\n * on the server render path and never appears in a browser build.\n */\nexport class ServerRawHTML implements ServerNode {\n readonly kind = 'raw' as const;\n parent: ServerParent | null = null;\n readonly html: string;\n constructor(html: string) {\n this.html = html;\n }\n}\n\nexport class ServerElement implements ServerNode {\n readonly kind = 'element' as const;\n parent: ServerParent | null = null;\n readonly tagName: string;\n readonly attributes = new Map<string, string>();\n readonly children: ServerNode[] = [];\n\n // Lazily-allocated stores. On the 10k-row SSR corpus ~0% of elements carry JS\n // properties or inline styles (measured, §5: 1 of 80,029 elements uses\n // `properties`, 0 use `style`), so eagerly allocating a `properties` Map plus\n // a `ServerStyle` (which itself holds a Map) per element wasted ~240k\n // allocations per /users render — all in the dominant mount phase. These are\n // created on first WRITE via the `properties`/`style` getters; the serializer\n // reads the raw `_properties`/`_style` fields so a READ never forces an\n // allocation. Output is byte-identical: an unset store previously serialized\n // to nothing (empty `properties.has(...)` / `style.isEmpty`), and a null store\n // is skipped the same way.\n _properties: Map<string, unknown> | null = null;\n _style: ServerStyle | null = null;\n\n constructor(tagName: string) {\n this.tagName = tagName.toLowerCase();\n }\n\n /** JS properties set via `setProperty` (e.g. input `value`, `checked`). Allocated on first access. */\n get properties(): Map<string, unknown> {\n return (this._properties ??= new Map());\n }\n\n /** Inline-style holder mirroring `element.style`. Allocated on first access. */\n get style(): ServerStyle {\n return (this._style ??= new ServerStyle());\n }\n}\n\n// ── HTML serialization ─────────────────────────────────────────────────────────\n\n/**\n * HTML \"void\" elements — self-closing, never given a closing tag or children.\n */\nconst VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n/**\n * Element properties that should be reflected into the serialized HTML so the\n * hydrated DOM carries the same initial state. `value`/`checked` matter for\n * form controls whose live state is a JS property, not an attribute.\n */\nconst SERIALIZED_PROPERTIES: Record<string, 'attr' | 'boolean'> = {\n value: 'attr',\n checked: 'boolean',\n selected: 'boolean',\n};\n\n/**\n * Precomputed `[name, kind]` pairs of SERIALIZED_PROPERTIES. Hoisted to module\n * scope so `serializeAttributes` does not allocate a fresh entries array for\n * every element serialized (measured hot: ~80k elements on the 10k-row route).\n */\nconst SERIALIZED_PROPERTY_ENTRIES: ReadonlyArray<readonly [string, 'attr' | 'boolean']> =\n Object.entries(SERIALIZED_PROPERTIES) as Array<[string, 'attr' | 'boolean']>;\n\n// Fast-path escaping. The chained `.replace(/…/g, …)` form makes 3–4 full\n// passes and allocates an intermediate string per pass even when nothing needs\n// escaping. These variants scan once and, in the overwhelmingly common case of\n// no special character, return the input unchanged (zero allocation). Output is\n// byte-identical to the chained form (verified over the real SSR corpus).\nconst TEXT_SPECIAL = /[&<>]/;\nconst ATTR_SPECIAL = /[&<>\"]/;\n\n/** Escape text node content. */\nexport function escapeHtmlText(value: string): string {\n if (!TEXT_SPECIAL.test(value)) return value;\n let out = '';\n let last = 0;\n for (let i = 0; i < value.length; i++) {\n let esc: string;\n switch (value.charCodeAt(i)) {\n case 38: esc = '&amp;'; break; // &\n case 60: esc = '&lt;'; break; // <\n case 62: esc = '&gt;'; break; // >\n default: continue;\n }\n out += value.slice(last, i) + esc;\n last = i + 1;\n }\n return out + value.slice(last);\n}\n\n/** Escape a double-quoted attribute value. */\nexport function escapeHtmlAttr(value: string): string {\n if (!ATTR_SPECIAL.test(value)) return value;\n let out = '';\n let last = 0;\n for (let i = 0; i < value.length; i++) {\n let esc: string;\n switch (value.charCodeAt(i)) {\n case 38: esc = '&amp;'; break; // &\n case 60: esc = '&lt;'; break; // <\n case 62: esc = '&gt;'; break; // >\n case 34: esc = '&quot;'; break; // \"\n default: continue;\n }\n out += value.slice(last, i) + esc;\n last = i + 1;\n }\n return out + value.slice(last);\n}\n\nfunction serializeAttributes(el: ServerElement): string {\n const parts: string[] = [];\n\n for (const [name, value] of el.attributes) {\n if (value === '') {\n parts.push(` ${name}`);\n } else {\n parts.push(` ${name}=\"${escapeHtmlAttr(value)}\"`);\n }\n }\n\n // Read the raw backing field (may be null): most elements have no JS\n // properties, so skipping the whole loop avoids touching a store that was\n // never allocated (§5).\n const props = el._properties;\n if (props !== null) {\n for (const [name, kind] of SERIALIZED_PROPERTY_ENTRIES) {\n if (!props.has(name)) continue;\n if (el.attributes.has(name)) continue; // an explicit attribute already won\n const raw = props.get(name);\n if (kind === 'boolean') {\n if (raw === true) parts.push(` ${name}`);\n } else {\n if (raw !== undefined && raw !== null) {\n parts.push(` ${name}=\"${escapeHtmlAttr(String(raw))}\"`);\n }\n }\n }\n }\n\n const style = el._style;\n if (style !== null && !style.isEmpty && !el.attributes.has('style')) {\n parts.push(` style=\"${escapeHtmlAttr(style.toCss())}\"`);\n }\n\n return parts.join('');\n}\n\n/** Serialize a single server node (element/text/comment/fragment/raw) to HTML. */\nexport function serializeServerNode(node: ServerNode): string {\n switch (node.kind) {\n case 'text':\n return escapeHtmlText((node as ServerText).data);\n case 'comment':\n return `<!--${(node as ServerComment).data}-->`;\n case 'fragment':\n return serializeChildren(node as ServerFragment);\n case 'raw':\n // Verbatim: the string was produced by this same serializer for a static\n // subtree, so it is already correctly escaped. Copy it as-is (§8).\n return (node as ServerRawHTML).html;\n case 'element': {\n const el = node as ServerElement;\n const tag = el.tagName;\n const attrs = serializeAttributes(el);\n if (VOID_ELEMENTS.has(tag)) {\n return `<${tag}${attrs}>`;\n }\n return `<${tag}${attrs}>${serializeChildren(el)}</${tag}>`;\n }\n }\n}\n\n/** Serialize the children of an element or fragment (its \"inner HTML\"). */\nexport function serializeChildren(node: ServerElement | ServerFragment): string {\n let out = '';\n for (const child of node.children) {\n out += serializeServerNode(child);\n }\n return out;\n}\n","/**\n * Server implementation of `DOMAdapter`.\n *\n * Builds a lightweight in-memory tree (see `server-node.ts`) instead of touching\n * a real browser DOM, then lets the caller serialize it to an HTML string. It is\n * completely free of browser globals, so the exact same renderer that runs in\n * the browser can produce HTML on the server.\n *\n * The `DOMAdapter` interface is typed against the lib DOM types (`Element`,\n * `Node`, `Text`, …). Our server nodes structurally stand in for those at\n * runtime, so the boundary uses `as unknown as` casts in one place. Everything\n * inside operates on the real server-node model.\n */\n\nimport type { DOMAdapter } from './adapter.js';\nimport {\n ServerElement,\n ServerText,\n ServerComment,\n ServerFragment,\n ServerRawHTML,\n serializeChildren,\n serializeServerNode,\n type ServerNode,\n type ServerParent,\n} from './server-node.js';\n\nfunction asServer(node: unknown): ServerNode {\n return node as unknown as ServerNode;\n}\nfunction asParent(node: unknown): ServerParent {\n return node as unknown as ServerParent;\n}\n\nexport class ServerDOMAdapter implements DOMAdapter {\n createElement(tag: string, _ns?: string): Element {\n return new ServerElement(tag) as unknown as Element;\n }\n\n createTextNode(data: string): Text {\n return new ServerText(data) as unknown as Text;\n }\n\n createComment(data: string): Comment {\n return new ServerComment(data) as unknown as Comment;\n }\n\n createFragment(): DocumentFragment {\n return new ServerFragment() as unknown as DocumentFragment;\n }\n\n /**\n * Create a verbatim pre-serialized HTML node (v1.7 static SSR plan, §6).\n * Server-only: the browser adapter does not implement this, and the renderer\n * fast path only invokes it when a static SSR plan is present (SSR). The\n * stored HTML was produced by this same serializer, so it is emitted as-is.\n */\n createRawHTML(html: string): Node {\n return new ServerRawHTML(html) as unknown as Node;\n }\n\n appendChild(parent: Node, child: Node): void {\n const p = asParent(parent);\n const c = asServer(child);\n this._detach(c);\n c.parent = p;\n p.children.push(c);\n }\n\n insertBefore(parent: Node, child: Node, reference: Node | null): void {\n const p = asParent(parent);\n const c = asServer(child);\n this._detach(c);\n c.parent = p;\n if (reference === null) {\n p.children.push(c);\n return;\n }\n const ref = asServer(reference);\n const idx = p.children.indexOf(ref);\n if (idx === -1) p.children.push(c);\n else p.children.splice(idx, 0, c);\n }\n\n removeChild(parent: Node, child: Node): void {\n const p = asParent(parent);\n const c = asServer(child);\n const idx = p.children.indexOf(c);\n if (idx !== -1) {\n p.children.splice(idx, 1);\n c.parent = null;\n }\n }\n\n replaceChild(parent: Node, newChild: Node, oldChild: Node): void {\n const p = asParent(parent);\n const nc = asServer(newChild);\n const oc = asServer(oldChild);\n const idx = p.children.indexOf(oc);\n if (idx === -1) return;\n this._detach(nc);\n nc.parent = p;\n p.children.splice(idx, 1, nc);\n oc.parent = null;\n }\n\n private _detach(node: ServerNode): void {\n if (node.parent !== null) {\n const siblings = node.parent.children;\n const idx = siblings.indexOf(node);\n if (idx !== -1) siblings.splice(idx, 1);\n node.parent = null;\n }\n }\n\n setAttribute(element: Element, name: string, value: string): void {\n (element as unknown as ServerElement).attributes.set(name, value);\n }\n\n removeAttribute(element: Element, name: string): void {\n (element as unknown as ServerElement).attributes.delete(name);\n }\n\n getAttribute(element: Element, name: string): string | null {\n return (element as unknown as ServerElement).attributes.get(name) ?? null;\n }\n\n setProperty(element: Element, name: string, value: unknown): void {\n (element as unknown as ServerElement).properties.set(name, value);\n }\n\n setTextContent(node: Node, text: string): void {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n const el = n as ServerElement | ServerFragment;\n el.children.length = 0;\n const t = new ServerText(text);\n t.parent = el;\n el.children.push(t);\n } else if (n.kind === 'text') {\n (n as ServerText).data = text;\n }\n }\n\n getTextContent(node: Node): string | null {\n const n = asServer(node);\n if (n.kind === 'text') return (n as ServerText).data;\n if (n.kind === 'element' || n.kind === 'fragment') {\n let out = '';\n for (const c of (n as ServerElement | ServerFragment).children) {\n out += this.getTextContent(c as unknown as Node) ?? '';\n }\n return out;\n }\n return null;\n }\n\n // Server nodes never dispatch events — listeners are a no-op on the server.\n addEventListener(): void {\n /* no-op on the server */\n }\n removeEventListener(): void {\n /* no-op on the server */\n }\n\n querySelector(): Element | null {\n return null;\n }\n querySelectorAll(): NodeListOf<Element> {\n return [] as unknown as NodeListOf<Element>;\n }\n getElementById(): Element | null {\n return null;\n }\n\n focus(): void {\n // No focus concept on the server — intentional no-op (SSR-safe).\n }\n\n body(): Element | null {\n // No document on the server — portals degrade to inline rendering.\n return null;\n }\n\n activeElement(): Element | null {\n return null;\n }\n\n contains(_ancestor: Element, _node: Node): boolean {\n return false;\n }\n\n matches(_element: Element, _selector: string): boolean {\n return false;\n }\n\n isElement(node: Node): node is Element {\n return asServer(node).kind === 'element';\n }\n\n isTextNode(node: Node): node is Text {\n return asServer(node).kind === 'text';\n }\n\n tagName(element: Element): string {\n return (element as unknown as ServerElement).tagName;\n }\n\n parentNode(node: Node): Node | null {\n return (asServer(node).parent as unknown as Node | null) ?? null;\n }\n\n nextSibling(node: Node): Node | null {\n const n = asServer(node);\n const parent = n.parent;\n if (parent === null) return null;\n const idx = parent.children.indexOf(n);\n if (idx === -1 || idx + 1 >= parent.children.length) return null;\n return parent.children[idx + 1] as unknown as Node;\n }\n\n firstChild(node: Node): Node | null {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n const el = n as ServerElement | ServerFragment;\n return (el.children[0] as unknown as Node) ?? null;\n }\n return null;\n }\n\n childNodes(node: Node): Node[] {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n return (n as ServerElement | ServerFragment).children as unknown as Node[];\n }\n return [];\n }\n\n // ── Server-only ────────────────────────────────────────────────────────────\n\n /** Serialize a node's children (\"inner HTML\") to an HTML string. */\n serializeInner(node: Node): string {\n const n = asServer(node);\n if (n.kind === 'element' || n.kind === 'fragment') {\n return serializeChildren(n as ServerElement | ServerFragment);\n }\n return '';\n }\n\n /** Serialize a node (including itself) to an HTML string. */\n serializeOuter(node: Node): string {\n return serializeServerNode(asServer(node));\n }\n}\n\n// `/* @__PURE__ */`: this singleton is a convenience export only (no internal\n// runtime path references it). Marking construction pure lets bundlers drop it —\n// and with it the whole server serializer chain (serializeServerNode/escape/\n// VOID_ELEMENTS) — out of client bundles that never import SSR. Without this,\n// the un-annotated `new` is treated as a side effect and retained everywhere.\nexport const serverDOMAdapter = /* @__PURE__ */ new ServerDOMAdapter();\n","/**\n * Focus helpers built on the {@link DOMAdapter} abstraction.\n *\n * These are the minimal, genuinely-useful focus operations an app needs:\n * focus a specific element (e.g. the first field when a route or modal opens)\n * or focus the first focusable element inside a container (e.g. move focus\n * into a dialog). Both go through the adapter, so they are no-ops on the server\n * (`ServerDOMAdapter.querySelector` returns null / `focus` does nothing) and\n * therefore safe to call from universal code.\n */\n\nimport type { DOMAdapter } from './adapter.js';\n\n/** Default selector for natively focusable / tabbable elements. */\nexport const FOCUSABLE_SELECTOR =\n 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Focus the element with the given id, scoped to `root`.\n * Returns true if an element was found and focused.\n */\nexport function focusById(dom: DOMAdapter, root: Element | Document, id: string): boolean {\n const el = dom.querySelector(root, `[id=\"${id}\"]`);\n if (el === null) return false;\n dom.focus(el);\n return true;\n}\n\n/**\n * Focus the first focusable element inside `container`.\n * Returns true if a focusable element was found and focused.\n */\nexport function focusFirst(\n dom: DOMAdapter,\n container: Element | Document,\n selector: string = FOCUSABLE_SELECTOR,\n): boolean {\n const el = dom.querySelector(container, selector);\n if (el === null) return false;\n dom.focus(el);\n return true;\n}\n\n/**\n * Ordered list of focusable/tabbable descendants of `container`.\n * Re-checks each candidate against the selector so elements disabled after the\n * initial query (e.g. a button toggled to `disabled`) are excluded.\n */\nexport function getFocusable(\n dom: DOMAdapter,\n container: Element,\n selector: string = FOCUSABLE_SELECTOR,\n): Element[] {\n return Array.from(dom.querySelectorAll(container, selector)).filter((el) =>\n dom.matches(el, selector),\n );\n}\n\n/**\n * Capture the currently-focused element so it can be restored later (e.g. when\n * a dialog closes). Returns null on the server or when nothing is focused.\n */\nexport function saveFocus(dom: DOMAdapter): Element | null {\n return dom.activeElement();\n}\n\n/** Restore focus to a previously {@link saveFocus}-d element. No-op if null. */\nexport function restoreFocus(dom: DOMAdapter, saved: Element | null): void {\n if (saved !== null) dom.focus(saved);\n}\n\n/**\n * Move focus into `container` on open: the element with id `initialFocusId` if\n * given and present, otherwise the first focusable element. Server-safe no-op.\n */\nexport function focusInitial(\n dom: DOMAdapter,\n container: Element,\n initialFocusId?: string,\n): void {\n if (initialFocusId !== undefined && focusById(dom, container, initialFocusId)) return;\n focusFirst(dom, container);\n}\n\n/**\n * Trap Tab / Shift+Tab focus within `container` (wrap-around at both ends).\n * Attaches a keydown listener to the container and returns a cleanup function\n * that detaches it. Server-safe: `addEventListener` is a no-op, and the returned\n * cleanup is still callable.\n */\nexport function trapFocus(dom: DOMAdapter, container: Element): () => void {\n const onKeydown = ((event: KeyboardEvent): void => {\n if (event.key !== 'Tab') return;\n const items = getFocusable(dom, container);\n if (items.length === 0) {\n event.preventDefault();\n return;\n }\n const first = items[0]!;\n const last = items[items.length - 1]!;\n const active = dom.activeElement();\n if (active === null || !dom.contains(container, active)) {\n event.preventDefault();\n dom.focus(first);\n } else if (event.shiftKey && active === first) {\n event.preventDefault();\n dom.focus(last);\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n dom.focus(first);\n }\n }) as EventListener;\n dom.addEventListener(container, 'keydown', onKeydown);\n return () => dom.removeEventListener(container, 'keydown', onKeydown);\n}\n\n/**\n * Modal containment: if focus moves to an element outside `container`, redirect\n * it back inside. Listens on the document body (focusin bubbles there) and\n * returns a cleanup function. Server-safe no-op (body() is null).\n */\nexport function containFocus(dom: DOMAdapter, container: Element): () => void {\n const body = dom.body();\n if (body === null) return () => {};\n const onFocusIn = ((event: FocusEvent): void => {\n const target = event.target as Node | null;\n if (target !== null && !dom.contains(container, target)) {\n focusFirst(dom, container);\n }\n }) as EventListener;\n dom.addEventListener(body, 'focusin', onFocusIn);\n return () => dom.removeEventListener(body, 'focusin', onFocusIn);\n}\n\n/**\n * Invoke `handler` when Escape is pressed while focus is within `target`.\n * Returns a cleanup function. Server-safe no-op.\n */\nexport function onEscape(dom: DOMAdapter, target: Element, handler: () => void): () => void {\n const onKeydown = ((event: KeyboardEvent): void => {\n if (event.key === 'Escape') handler();\n }) as EventListener;\n dom.addEventListener(target, 'keydown', onKeydown);\n return () => dom.removeEventListener(target, 'keydown', onKeydown);\n}\n\n"],"mappings":";AAMO,IAAM,oBAAN,MAA8C;AAAA,EACnD,cAAc,KAAa,IAAsB;AAC/C,QAAI,OAAO,QAAW;AACpB,aAAO,SAAS,gBAAgB,IAAI,GAAG;AAAA,IACzC;AACA,WAAO,SAAS,cAAc,GAAG;AAAA,EACnC;AAAA,EAEA,eAAe,MAAoB;AACjC,WAAO,SAAS,eAAe,IAAI;AAAA,EACrC;AAAA,EAEA,cAAc,MAAuB;AACnC,WAAO,SAAS,cAAc,IAAI;AAAA,EACpC;AAAA,EAEA,iBAAmC;AACjC,WAAO,SAAS,uBAAuB;AAAA,EACzC;AAAA,EAEA,YAAY,QAAc,OAAmB;AAC3C,WAAO,YAAY,KAAK;AAAA,EAC1B;AAAA,EAEA,aAAa,QAAc,OAAa,WAA8B;AACpE,WAAO,aAAa,OAAO,SAAS;AAAA,EACtC;AAAA,EAEA,YAAY,QAAc,OAAmB;AAC3C,WAAO,YAAY,KAAK;AAAA,EAC1B;AAAA,EAEA,aAAa,QAAc,UAAgB,UAAsB;AAC/D,WAAO,aAAa,UAAU,QAAQ;AAAA,EACxC;AAAA,EAEA,aAAa,SAAkB,MAAc,OAAqB;AAChE,YAAQ,aAAa,MAAM,KAAK;AAAA,EAClC;AAAA,EAEA,gBAAgB,SAAkB,MAAoB;AACpD,YAAQ,gBAAgB,IAAI;AAAA,EAC9B;AAAA,EAEA,aAAa,SAAkB,MAA6B;AAC1D,WAAO,QAAQ,aAAa,IAAI;AAAA,EAClC;AAAA,EAEA,YAAY,SAAkB,MAAc,OAAsB;AAChE,IAAC,QAA+C,IAAI,IAAI;AAAA,EAC1D;AAAA,EAEA,eAAe,MAAY,MAAoB;AAC7C,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,eAAe,MAA2B;AACxC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,iBACE,QACA,MACA,SACA,SACM;AACN,WAAO,iBAAiB,MAAM,SAAS,OAAO;AAAA,EAChD;AAAA,EAEA,oBACE,QACA,MACA,SACA,SACM;AACN,WAAO,oBAAoB,MAAM,SAAS,OAAO;AAAA,EACnD;AAAA,EAEA,cAAc,MAA0B,UAAkC;AACxE,WAAO,KAAK,cAAc,QAAQ;AAAA,EACpC;AAAA,EAEA,iBAAiB,MAA0B,UAAuC;AAChF,WAAO,KAAK,iBAAiB,QAAQ;AAAA,EACvC;AAAA,EAEA,eAAe,IAA4B;AACzC,WAAO,SAAS,eAAe,EAAE;AAAA,EACnC;AAAA,EAEA,MAAM,SAAwB;AAC5B,IAAC,QAA8C,QAAQ;AAAA,EACzD;AAAA,EAEA,OAAuB;AACrB,WAAO,SAAS,QAAQ;AAAA,EAC1B;AAAA,EAEA,gBAAgC;AAC9B,WAAO,SAAS,iBAAiB;AAAA,EACnC;AAAA,EAEA,SAAS,UAAmB,MAAqB;AAC/C,WAAO,SAAS,SAAS,IAAI;AAAA,EAC/B;AAAA,EAEA,QAAQ,SAAkB,UAA2B;AACnD,WAAO,OAAO,QAAQ,YAAY,cAAc,QAAQ,QAAQ,QAAQ;AAAA,EAC1E;AAAA,EAEA,UAAU,MAA6B;AACrC,WAAO,KAAK,aAAa,KAAK;AAAA,EAChC;AAAA,EAEA,WAAW,MAA0B;AACnC,WAAO,KAAK,aAAa,KAAK;AAAA,EAChC;AAAA,EAEA,QAAQ,SAA0B;AAChC,WAAO,QAAQ,QAAQ,YAAY;AAAA,EACrC;AAAA,EAEA,WAAW,MAAyB;AAClC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAY,MAAyB;AACnC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAW,MAAyB;AAClC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAW,MAAoB;AAC7B,WAAO,MAAM,KAAK,KAAK,UAAU;AAAA,EACnC;AACF;AAKO,IAAM,oBAAoC,oBAAI,kBAAkB;;;AC7HhE,IAAM,cAAN,MAAkB;AAAA,EACd,eAAe,oBAAI,IAAoB;AAAA,EAChD,YAAY,MAAc,OAAqB;AAC7C,SAAK,aAAa,IAAI,MAAM,KAAK;AAAA,EACnC;AAAA,EACA,IAAI,UAAmB;AACrB,WAAO,KAAK,aAAa,SAAS;AAAA,EACpC;AAAA,EACA,QAAgB;AACd,WAAO,CAAC,GAAG,KAAK,aAAa,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI;AAAA,EACjF;AACF;AAEO,IAAM,aAAN,MAAuC;AAAA,EACnC,OAAO;AAAA,EAChB,SAA8B;AAAA,EAC9B;AAAA,EACA,YAAY,MAAc;AACxB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,gBAAN,MAA0C;AAAA,EACtC,OAAO;AAAA,EAChB,SAA8B;AAAA,EAC9B;AAAA,EACA,YAAY,MAAc;AACxB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,iBAAN,MAA2C;AAAA,EACvC,OAAO;AAAA,EAChB,SAA8B;AAAA,EACrB,WAAyB,CAAC;AACrC;AAeO,IAAM,gBAAN,MAA0C;AAAA,EACtC,OAAO;AAAA,EAChB,SAA8B;AAAA,EACrB;AAAA,EACT,YAAY,MAAc;AACxB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,gBAAN,MAA0C;AAAA,EACtC,OAAO;AAAA,EAChB,SAA8B;AAAA,EACrB;AAAA,EACA,aAAa,oBAAI,IAAoB;AAAA,EACrC,WAAyB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYnC,cAA2C;AAAA,EAC3C,SAA6B;AAAA,EAE7B,YAAY,SAAiB;AAC3B,SAAK,UAAU,QAAQ,YAAY;AAAA,EACrC;AAAA;AAAA,EAGA,IAAI,aAAmC;AACrC,WAAQ,KAAK,gBAAgB,oBAAI,IAAI;AAAA,EACvC;AAAA;AAAA,EAGA,IAAI,QAAqB;AACvB,WAAQ,KAAK,WAAW,IAAI,YAAY;AAAA,EAC1C;AACF;AAOA,IAAM,gBAAgB,oBAAI,IAAI;AAAA,EAC5B;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAO;AAAA,EAAS;AAAA,EAAM;AAAA,EAAO;AAAA,EACnD;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAU;AAAA,EAAS;AAC9C,CAAC;AAOD,IAAM,wBAA4D;AAAA,EAChE,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AACZ;AAOA,IAAM,8BACJ,OAAO,QAAQ,qBAAqB;AAOtC,IAAM,eAAe;AACrB,IAAM,eAAe;AAGd,SAAS,eAAe,OAAuB;AACpD,MAAI,CAAC,aAAa,KAAK,KAAK,EAAG,QAAO;AACtC,MAAI,MAAM;AACV,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,QAAI;AACJ,YAAQ,MAAM,WAAW,CAAC,GAAG;AAAA,MAC3B,KAAK;AAAI,cAAM;AAAS;AAAA;AAAA,MACxB,KAAK;AAAI,cAAM;AAAQ;AAAA;AAAA,MACvB,KAAK;AAAI,cAAM;AAAQ;AAAA;AAAA,MACvB;AAAS;AAAA,IACX;AACA,WAAO,MAAM,MAAM,MAAM,CAAC,IAAI;AAC9B,WAAO,IAAI;AAAA,EACb;AACA,SAAO,MAAM,MAAM,MAAM,IAAI;AAC/B;AAGO,SAAS,eAAe,OAAuB;AACpD,MAAI,CAAC,aAAa,KAAK,KAAK,EAAG,QAAO;AACtC,MAAI,MAAM;AACV,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,QAAI;AACJ,YAAQ,MAAM,WAAW,CAAC,GAAG;AAAA,MAC3B,KAAK;AAAI,cAAM;AAAS;AAAA;AAAA,MACxB,KAAK;AAAI,cAAM;AAAQ;AAAA;AAAA,MACvB,KAAK;AAAI,cAAM;AAAQ;AAAA;AAAA,MACvB,KAAK;AAAI,cAAM;AAAU;AAAA;AAAA,MACzB;AAAS;AAAA,IACX;AACA,WAAO,MAAM,MAAM,MAAM,CAAC,IAAI;AAC9B,WAAO,IAAI;AAAA,EACb;AACA,SAAO,MAAM,MAAM,MAAM,IAAI;AAC/B;AAEA,SAAS,oBAAoB,IAA2B;AACtD,QAAM,QAAkB,CAAC;AAEzB,aAAW,CAAC,MAAM,KAAK,KAAK,GAAG,YAAY;AACzC,QAAI,UAAU,IAAI;AAChB,YAAM,KAAK,IAAI,IAAI,EAAE;AAAA,IACvB,OAAO;AACL,YAAM,KAAK,IAAI,IAAI,KAAK,eAAe,KAAK,CAAC,GAAG;AAAA,IAClD;AAAA,EACF;AAKA,QAAM,QAAQ,GAAG;AACjB,MAAI,UAAU,MAAM;AAClB,eAAW,CAAC,MAAM,IAAI,KAAK,6BAA6B;AACtD,UAAI,CAAC,MAAM,IAAI,IAAI,EAAG;AACtB,UAAI,GAAG,WAAW,IAAI,IAAI,EAAG;AAC7B,YAAM,MAAM,MAAM,IAAI,IAAI;AAC1B,UAAI,SAAS,WAAW;AACtB,YAAI,QAAQ,KAAM,OAAM,KAAK,IAAI,IAAI,EAAE;AAAA,MACzC,OAAO;AACL,YAAI,QAAQ,UAAa,QAAQ,MAAM;AACrC,gBAAM,KAAK,IAAI,IAAI,KAAK,eAAe,OAAO,GAAG,CAAC,CAAC,GAAG;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,GAAG;AACjB,MAAI,UAAU,QAAQ,CAAC,MAAM,WAAW,CAAC,GAAG,WAAW,IAAI,OAAO,GAAG;AACnE,UAAM,KAAK,WAAW,eAAe,MAAM,MAAM,CAAC,CAAC,GAAG;AAAA,EACxD;AAEA,SAAO,MAAM,KAAK,EAAE;AACtB;AAGO,SAAS,oBAAoB,MAA0B;AAC5D,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,eAAgB,KAAoB,IAAI;AAAA,IACjD,KAAK;AACH,aAAO,OAAQ,KAAuB,IAAI;AAAA,IAC5C,KAAK;AACH,aAAO,kBAAkB,IAAsB;AAAA,IACjD,KAAK;AAGH,aAAQ,KAAuB;AAAA,IACjC,KAAK,WAAW;AACd,YAAM,KAAK;AACX,YAAM,MAAM,GAAG;AACf,YAAM,QAAQ,oBAAoB,EAAE;AACpC,UAAI,cAAc,IAAI,GAAG,GAAG;AAC1B,eAAO,IAAI,GAAG,GAAG,KAAK;AAAA,MACxB;AACA,aAAO,IAAI,GAAG,GAAG,KAAK,IAAI,kBAAkB,EAAE,CAAC,KAAK,GAAG;AAAA,IACzD;AAAA,EACF;AACF;AAGO,SAAS,kBAAkB,MAA8C;AAC9E,MAAI,MAAM;AACV,aAAW,SAAS,KAAK,UAAU;AACjC,WAAO,oBAAoB,KAAK;AAAA,EAClC;AACA,SAAO;AACT;;;AC5OA,SAAS,SAAS,MAA2B;AAC3C,SAAO;AACT;AACA,SAAS,SAAS,MAA6B;AAC7C,SAAO;AACT;AAEO,IAAM,mBAAN,MAA6C;AAAA,EAClD,cAAc,KAAa,KAAuB;AAChD,WAAO,IAAI,cAAc,GAAG;AAAA,EAC9B;AAAA,EAEA,eAAe,MAAoB;AACjC,WAAO,IAAI,WAAW,IAAI;AAAA,EAC5B;AAAA,EAEA,cAAc,MAAuB;AACnC,WAAO,IAAI,cAAc,IAAI;AAAA,EAC/B;AAAA,EAEA,iBAAmC;AACjC,WAAO,IAAI,eAAe;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,cAAc,MAAoB;AAChC,WAAO,IAAI,cAAc,IAAI;AAAA,EAC/B;AAAA,EAEA,YAAY,QAAc,OAAmB;AAC3C,UAAM,IAAI,SAAS,MAAM;AACzB,UAAM,IAAI,SAAS,KAAK;AACxB,SAAK,QAAQ,CAAC;AACd,MAAE,SAAS;AACX,MAAE,SAAS,KAAK,CAAC;AAAA,EACnB;AAAA,EAEA,aAAa,QAAc,OAAa,WAA8B;AACpE,UAAM,IAAI,SAAS,MAAM;AACzB,UAAM,IAAI,SAAS,KAAK;AACxB,SAAK,QAAQ,CAAC;AACd,MAAE,SAAS;AACX,QAAI,cAAc,MAAM;AACtB,QAAE,SAAS,KAAK,CAAC;AACjB;AAAA,IACF;AACA,UAAM,MAAM,SAAS,SAAS;AAC9B,UAAM,MAAM,EAAE,SAAS,QAAQ,GAAG;AAClC,QAAI,QAAQ,GAAI,GAAE,SAAS,KAAK,CAAC;AAAA,QAC5B,GAAE,SAAS,OAAO,KAAK,GAAG,CAAC;AAAA,EAClC;AAAA,EAEA,YAAY,QAAc,OAAmB;AAC3C,UAAM,IAAI,SAAS,MAAM;AACzB,UAAM,IAAI,SAAS,KAAK;AACxB,UAAM,MAAM,EAAE,SAAS,QAAQ,CAAC;AAChC,QAAI,QAAQ,IAAI;AACd,QAAE,SAAS,OAAO,KAAK,CAAC;AACxB,QAAE,SAAS;AAAA,IACb;AAAA,EACF;AAAA,EAEA,aAAa,QAAc,UAAgB,UAAsB;AAC/D,UAAM,IAAI,SAAS,MAAM;AACzB,UAAM,KAAK,SAAS,QAAQ;AAC5B,UAAM,KAAK,SAAS,QAAQ;AAC5B,UAAM,MAAM,EAAE,SAAS,QAAQ,EAAE;AACjC,QAAI,QAAQ,GAAI;AAChB,SAAK,QAAQ,EAAE;AACf,OAAG,SAAS;AACZ,MAAE,SAAS,OAAO,KAAK,GAAG,EAAE;AAC5B,OAAG,SAAS;AAAA,EACd;AAAA,EAEQ,QAAQ,MAAwB;AACtC,QAAI,KAAK,WAAW,MAAM;AACxB,YAAM,WAAW,KAAK,OAAO;AAC7B,YAAM,MAAM,SAAS,QAAQ,IAAI;AACjC,UAAI,QAAQ,GAAI,UAAS,OAAO,KAAK,CAAC;AACtC,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,aAAa,SAAkB,MAAc,OAAqB;AAChE,IAAC,QAAqC,WAAW,IAAI,MAAM,KAAK;AAAA,EAClE;AAAA,EAEA,gBAAgB,SAAkB,MAAoB;AACpD,IAAC,QAAqC,WAAW,OAAO,IAAI;AAAA,EAC9D;AAAA,EAEA,aAAa,SAAkB,MAA6B;AAC1D,WAAQ,QAAqC,WAAW,IAAI,IAAI,KAAK;AAAA,EACvE;AAAA,EAEA,YAAY,SAAkB,MAAc,OAAsB;AAChE,IAAC,QAAqC,WAAW,IAAI,MAAM,KAAK;AAAA,EAClE;AAAA,EAEA,eAAe,MAAY,MAAoB;AAC7C,UAAM,IAAI,SAAS,IAAI;AACvB,QAAI,EAAE,SAAS,aAAa,EAAE,SAAS,YAAY;AACjD,YAAM,KAAK;AACX,SAAG,SAAS,SAAS;AACrB,YAAM,IAAI,IAAI,WAAW,IAAI;AAC7B,QAAE,SAAS;AACX,SAAG,SAAS,KAAK,CAAC;AAAA,IACpB,WAAW,EAAE,SAAS,QAAQ;AAC5B,MAAC,EAAiB,OAAO;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,eAAe,MAA2B;AACxC,UAAM,IAAI,SAAS,IAAI;AACvB,QAAI,EAAE,SAAS,OAAQ,QAAQ,EAAiB;AAChD,QAAI,EAAE,SAAS,aAAa,EAAE,SAAS,YAAY;AACjD,UAAI,MAAM;AACV,iBAAW,KAAM,EAAqC,UAAU;AAC9D,eAAO,KAAK,eAAe,CAAoB,KAAK;AAAA,MACtD;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,mBAAyB;AAAA,EAEzB;AAAA,EACA,sBAA4B;AAAA,EAE5B;AAAA,EAEA,gBAAgC;AAC9B,WAAO;AAAA,EACT;AAAA,EACA,mBAAwC;AACtC,WAAO,CAAC;AAAA,EACV;AAAA,EACA,iBAAiC;AAC/B,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AAAA,EAEd;AAAA,EAEA,OAAuB;AAErB,WAAO;AAAA,EACT;AAAA,EAEA,gBAAgC;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,WAAoB,OAAsB;AACjD,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,UAAmB,WAA4B;AACrD,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,MAA6B;AACrC,WAAO,SAAS,IAAI,EAAE,SAAS;AAAA,EACjC;AAAA,EAEA,WAAW,MAA0B;AACnC,WAAO,SAAS,IAAI,EAAE,SAAS;AAAA,EACjC;AAAA,EAEA,QAAQ,SAA0B;AAChC,WAAQ,QAAqC;AAAA,EAC/C;AAAA,EAEA,WAAW,MAAyB;AAClC,WAAQ,SAAS,IAAI,EAAE,UAAqC;AAAA,EAC9D;AAAA,EAEA,YAAY,MAAyB;AACnC,UAAM,IAAI,SAAS,IAAI;AACvB,UAAM,SAAS,EAAE;AACjB,QAAI,WAAW,KAAM,QAAO;AAC5B,UAAM,MAAM,OAAO,SAAS,QAAQ,CAAC;AACrC,QAAI,QAAQ,MAAM,MAAM,KAAK,OAAO,SAAS,OAAQ,QAAO;AAC5D,WAAO,OAAO,SAAS,MAAM,CAAC;AAAA,EAChC;AAAA,EAEA,WAAW,MAAyB;AAClC,UAAM,IAAI,SAAS,IAAI;AACvB,QAAI,EAAE,SAAS,aAAa,EAAE,SAAS,YAAY;AACjD,YAAM,KAAK;AACX,aAAQ,GAAG,SAAS,CAAC,KAAyB;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,WAAW,MAAoB;AAC7B,UAAM,IAAI,SAAS,IAAI;AACvB,QAAI,EAAE,SAAS,aAAa,EAAE,SAAS,YAAY;AACjD,aAAQ,EAAqC;AAAA,IAC/C;AACA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA;AAAA,EAKA,eAAe,MAAoB;AACjC,UAAM,IAAI,SAAS,IAAI;AACvB,QAAI,EAAE,SAAS,aAAa,EAAE,SAAS,YAAY;AACjD,aAAO,kBAAkB,CAAmC;AAAA,IAC9D;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,eAAe,MAAoB;AACjC,WAAO,oBAAoB,SAAS,IAAI,CAAC;AAAA,EAC3C;AACF;AAOO,IAAM,mBAAmC,oBAAI,iBAAiB;;;ACtP9D,IAAM,qBACX;AAMK,SAAS,UAAU,KAAiB,MAA0B,IAAqB;AACxF,QAAM,KAAK,IAAI,cAAc,MAAM,QAAQ,EAAE,IAAI;AACjD,MAAI,OAAO,KAAM,QAAO;AACxB,MAAI,MAAM,EAAE;AACZ,SAAO;AACT;AAMO,SAAS,WACd,KACA,WACA,WAAmB,oBACV;AACT,QAAM,KAAK,IAAI,cAAc,WAAW,QAAQ;AAChD,MAAI,OAAO,KAAM,QAAO;AACxB,MAAI,MAAM,EAAE;AACZ,SAAO;AACT;AAOO,SAAS,aACd,KACA,WACA,WAAmB,oBACR;AACX,SAAO,MAAM,KAAK,IAAI,iBAAiB,WAAW,QAAQ,CAAC,EAAE;AAAA,IAAO,CAAC,OACnE,IAAI,QAAQ,IAAI,QAAQ;AAAA,EAC1B;AACF;AAMO,SAAS,UAAU,KAAiC;AACzD,SAAO,IAAI,cAAc;AAC3B;AAGO,SAAS,aAAa,KAAiB,OAA6B;AACzE,MAAI,UAAU,KAAM,KAAI,MAAM,KAAK;AACrC;AAMO,SAAS,aACd,KACA,WACA,gBACM;AACN,MAAI,mBAAmB,UAAa,UAAU,KAAK,WAAW,cAAc,EAAG;AAC/E,aAAW,KAAK,SAAS;AAC3B;AAQO,SAAS,UAAU,KAAiB,WAAgC;AACzE,QAAM,YAAa,CAAC,UAA+B;AACjD,QAAI,MAAM,QAAQ,MAAO;AACzB,UAAM,QAAQ,aAAa,KAAK,SAAS;AACzC,QAAI,MAAM,WAAW,GAAG;AACtB,YAAM,eAAe;AACrB;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,CAAC;AACrB,UAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,UAAM,SAAS,IAAI,cAAc;AACjC,QAAI,WAAW,QAAQ,CAAC,IAAI,SAAS,WAAW,MAAM,GAAG;AACvD,YAAM,eAAe;AACrB,UAAI,MAAM,KAAK;AAAA,IACjB,WAAW,MAAM,YAAY,WAAW,OAAO;AAC7C,YAAM,eAAe;AACrB,UAAI,MAAM,IAAI;AAAA,IAChB,WAAW,CAAC,MAAM,YAAY,WAAW,MAAM;AAC7C,YAAM,eAAe;AACrB,UAAI,MAAM,KAAK;AAAA,IACjB;AAAA,EACF;AACA,MAAI,iBAAiB,WAAW,WAAW,SAAS;AACpD,SAAO,MAAM,IAAI,oBAAoB,WAAW,WAAW,SAAS;AACtE;AAOO,SAAS,aAAa,KAAiB,WAAgC;AAC5E,QAAM,OAAO,IAAI,KAAK;AACtB,MAAI,SAAS,KAAM,QAAO,MAAM;AAAA,EAAC;AACjC,QAAM,YAAa,CAAC,UAA4B;AAC9C,UAAM,SAAS,MAAM;AACrB,QAAI,WAAW,QAAQ,CAAC,IAAI,SAAS,WAAW,MAAM,GAAG;AACvD,iBAAW,KAAK,SAAS;AAAA,IAC3B;AAAA,EACF;AACA,MAAI,iBAAiB,MAAM,WAAW,SAAS;AAC/C,SAAO,MAAM,IAAI,oBAAoB,MAAM,WAAW,SAAS;AACjE;AAMO,SAAS,SAAS,KAAiB,QAAiB,SAAiC;AAC1F,QAAM,YAAa,CAAC,UAA+B;AACjD,QAAI,MAAM,QAAQ,SAAU,SAAQ;AAAA,EACtC;AACA,MAAI,iBAAiB,QAAQ,WAAW,SAAS;AACjD,SAAO,MAAM,IAAI,oBAAoB,QAAQ,WAAW,SAAS;AACnE;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streetui/dom",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "StreetUI DOM abstraction layer",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",