@askrjs/askr 0.0.41 → 0.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/benchmark.js +2 -2
  2. package/dist/boot/index.d.ts.map +1 -1
  3. package/dist/boot/index.js +2 -0
  4. package/dist/boot/index.js.map +1 -1
  5. package/dist/common/route-activity.js +30 -0
  6. package/dist/common/route-activity.js.map +1 -0
  7. package/dist/common/router.d.ts +4 -2
  8. package/dist/common/router.d.ts.map +1 -1
  9. package/dist/data/index.d.ts +21 -2
  10. package/dist/data/index.d.ts.map +1 -1
  11. package/dist/data/index.js +56 -3
  12. package/dist/data/index.js.map +1 -1
  13. package/dist/data/invalidation-listeners.js +15 -0
  14. package/dist/data/invalidation-listeners.js.map +1 -0
  15. package/dist/renderer/dom.js +35 -20
  16. package/dist/renderer/dom.js.map +1 -1
  17. package/dist/renderer/evaluate.js +27 -17
  18. package/dist/renderer/evaluate.js.map +1 -1
  19. package/dist/renderer/for-commit.js +9 -0
  20. package/dist/renderer/for-commit.js.map +1 -1
  21. package/dist/resources/index.d.ts +2 -2
  22. package/dist/resources/index.js +2 -2
  23. package/dist/router/match.js +31 -6
  24. package/dist/router/match.js.map +1 -1
  25. package/dist/router/navigate.d.ts.map +1 -1
  26. package/dist/router/navigate.js +30 -4
  27. package/dist/router/navigate.js.map +1 -1
  28. package/dist/router/route.d.ts +6 -1
  29. package/dist/router/route.d.ts.map +1 -1
  30. package/dist/router/route.js +35 -8
  31. package/dist/router/route.js.map +1 -1
  32. package/dist/runtime/child-scope.js +1 -0
  33. package/dist/runtime/child-scope.js.map +1 -1
  34. package/dist/runtime/component.d.ts +8 -1
  35. package/dist/runtime/component.d.ts.map +1 -1
  36. package/dist/runtime/component.js +207 -44
  37. package/dist/runtime/component.js.map +1 -1
  38. package/dist/runtime/fastlane.js +6 -0
  39. package/dist/runtime/fastlane.js.map +1 -1
  40. package/dist/runtime/for.d.ts.map +1 -1
  41. package/dist/runtime/for.js +18 -1
  42. package/dist/runtime/for.js.map +1 -1
  43. package/dist/runtime/operations.d.ts +11 -3
  44. package/dist/runtime/operations.d.ts.map +1 -1
  45. package/dist/runtime/operations.js +161 -22
  46. package/dist/runtime/operations.js.map +1 -1
  47. package/dist/runtime/readable.d.ts +1 -0
  48. package/dist/runtime/readable.d.ts.map +1 -1
  49. package/dist/runtime/readable.js +7 -4
  50. package/dist/runtime/readable.js.map +1 -1
  51. package/dist/testing/index.d.ts +53 -0
  52. package/dist/testing/index.d.ts.map +1 -0
  53. package/dist/testing/index.js +172 -0
  54. package/dist/testing/index.js.map +1 -0
  55. package/package.json +11 -3
@@ -7,7 +7,7 @@ import { incDevCounter, setDevValue } from "../runtime/dev-namespace.js";
7
7
  import "./env.js";
8
8
  import { createWrappedHandler, extractKey, getEventListenerKey, getEventListenerOptions, parseEventProp, setRenderedAttribute, tagNamesEqualIgnoreCase as tagNamesEqualIgnoreCase$1, writeElementClassName } from "./utils.js";
9
9
  import { keyedElements } from "./keyed.js";
10
- import { cleanupInstanceIfPresent, elementListeners, removeAllListeners } from "./cleanup.js";
10
+ import { elementListeners, removeAllListeners, teardownNodeSubtree } from "./cleanup.js";
11
11
  import { evaluateForState } from "../runtime/for.js";
12
12
  import { evaluateCaseState, evaluateShowState } from "../runtime/control.js";
13
13
  import { reconcileKeyedChildren } from "./reconcile.js";
@@ -23,6 +23,18 @@ function createElementForNamespace(type, parentNamespace) {
23
23
  return namespace ? document.createElementNS(namespace, type) : document.createElement(type);
24
24
  }
25
25
  const domRanges = /* @__PURE__ */ new WeakMap();
26
+ function getRetainedHostOwnerChain(host, owner) {
27
+ const instances = host.__ASKR_INSTANCES ?? [];
28
+ const ownerIndex = instances.indexOf(owner);
29
+ return ownerIndex >= 0 ? instances.slice(ownerIndex) : [owner];
30
+ }
31
+ function retainHostOwnerChain(host, owner, retainedInstances) {
32
+ const componentHost = host;
33
+ const nextInstances = [...componentHost.__ASKR_INSTANCES ?? []];
34
+ for (const instance of retainedInstances) if (!nextInstances.includes(instance)) nextInstances.push(instance);
35
+ componentHost.__ASKR_INSTANCES = nextInstances;
36
+ componentHost.__ASKR_INSTANCE = owner;
37
+ }
26
38
  function tagNamesEqualIgnoreCase(a, b) {
27
39
  return tagNamesEqualIgnoreCase$1(a, b);
28
40
  }
@@ -175,10 +187,7 @@ function updateElementChildren(element, vnodeChildren) {
175
187
  if (vnodeChildren === null || vnodeChildren === void 0) {
176
188
  for (let n = element.firstChild; n;) {
177
189
  const next = n.nextSibling;
178
- if (n instanceof Element) {
179
- removeAllListeners(n);
180
- cleanupInstanceIfPresent(n);
181
- }
190
+ cleanupRangeNode(n);
182
191
  n = next;
183
192
  }
184
193
  element.textContent = "";
@@ -196,10 +205,7 @@ function updateElementChildren(element, vnodeChildren) {
196
205
  if (!Array.isArray(vnodeChildren)) {
197
206
  for (let n = element.firstChild; n;) {
198
207
  const next = n.nextSibling;
199
- if (n instanceof Element) {
200
- removeAllListeners(n);
201
- cleanupInstanceIfPresent(n);
202
- }
208
+ cleanupRangeNode(n);
203
209
  n = next;
204
210
  }
205
211
  element.textContent = "";
@@ -252,10 +258,7 @@ function processFragmentChildren(target, childArray) {
252
258
  updateElementChildren(target, childArray);
253
259
  }
254
260
  function cleanupRangeNode(node) {
255
- if (node instanceof Element) {
256
- removeAllListeners(node);
257
- cleanupInstanceIfPresent(node);
258
- }
261
+ teardownNodeSubtree(node);
259
262
  }
260
263
  function updateDOMRange(target, range, children) {
261
264
  let current = range.start.nextSibling;
@@ -381,7 +384,7 @@ function getFragmentChildren(vnode) {
381
384
  const fragmentChildren = vnode.props?.children ?? vnode.children ?? [];
382
385
  return Array.isArray(fragmentChildren) ? fragmentChildren : [fragmentChildren];
383
386
  }
384
- function evaluate(node, target, context) {
387
+ function evaluate(node, target, context, retainedOwner) {
385
388
  if (!target) return;
386
389
  if (typeof document === "undefined") {
387
390
  if (getRuntimeEnv().NODE_ENV !== "production") try {
@@ -419,12 +422,14 @@ function evaluate(node, target, context) {
419
422
  updateElementChildren(target, vnode);
420
423
  return;
421
424
  }
422
- const targetInstance = target.__ASKR_INSTANCE;
425
+ const targetWithInstance = target;
426
+ const targetInstance = retainedOwner?.target === target ? retainedOwner : targetWithInstance.__ASKR_INSTANCE;
423
427
  if (targetInstance && targetInstance.target === target) {
428
+ const retainedHostInstances = getRetainedHostOwnerChain(targetWithInstance, targetInstance);
424
429
  if (_isDOMElement(vnode) && typeof vnode.type === "function") {
425
- const syncedDom = syncComponentElement(target, vnode, vnode.type, (vnode.props ?? {}) || {});
430
+ const syncedDom = syncComponentElement(target, vnode, vnode.type, (vnode.props ?? {}) || {}, void 0, false, retainedHostInstances);
426
431
  if (syncedDom instanceof Element) {
427
- syncedDom.__ASKR_INSTANCE = targetInstance;
432
+ retainHostOwnerChain(syncedDom, targetInstance, retainedHostInstances);
428
433
  targetInstance.target = syncedDom;
429
434
  return;
430
435
  }
@@ -447,6 +452,11 @@ function evaluate(node, target, context) {
447
452
  const firstChild = target.children[0];
448
453
  if (firstChild && _isDOMElement(vnode) && typeof vnode.type === "string" && tagNamesEqualIgnoreCase(firstChild.tagName, vnode.type)) smartUpdateElement(firstChild, vnode);
449
454
  else {
455
+ for (let node = target.firstChild; node;) {
456
+ const next = node.nextSibling;
457
+ cleanupRangeNode(node);
458
+ node = next;
459
+ }
450
460
  target.textContent = "";
451
461
  if (_isDOMElement(vnode) && typeof vnode.type === "string" && tryFirstRenderKeyedChildren(target, vnode)) return;
452
462
  const dom = createDOMNode(vnode);
@@ -1 +1 @@
1
- {"version":3,"file":"evaluate.js","names":["sharedTagNamesEqualIgnoreCase"],"sources":["../../src/renderer/evaluate.ts"],"sourcesContent":["import { logger } from '../dev/logger';\nimport { getRuntimeEnv } from './env';\nimport type { Props } from '../common/props';\nimport type { ComponentInstance } from '../runtime/component';\nimport type { ComponentFunction } from '../runtime/component';\nimport {\n elementListeners,\n removeAllListeners,\n cleanupInstanceIfPresent,\n} from './cleanup';\nimport { keyedElements } from './keyed';\nimport { reconcileKeyedChildren } from './reconcile';\nimport { _isDOMElement, type DOMElement, type VNode } from './types';\nimport { __FOR_BOUNDARY__ } from '../common/vnode';\nimport { evaluateForState } from '../runtime/for';\nimport {\n evaluateCaseState,\n evaluateShowState,\n type ControlBoundaryState,\n} from '../runtime/control';\nimport {\n createDOMNode,\n commitForBoundaryChildren,\n syncComponentElement,\n updateElementFromVnode,\n updateUnkeyedChildren,\n performBulkPositionalKeyedTextUpdate,\n performBulkTextReplace,\n isBulkTextFastPathEligible,\n} from './dom';\nimport { setDevValue, incDevCounter } from '../runtime/dev-namespace';\nimport { Fragment } from '../common/jsx';\nimport {\n createWrappedHandler,\n extractKey,\n getEventListenerKey,\n getEventListenerOptions,\n parseEventProp,\n setRenderedAttribute,\n tagNamesEqualIgnoreCase as sharedTagNamesEqualIgnoreCase,\n writeElementClassName,\n} from './utils';\n\n/**\n * Internal marker for component-owned DOM ranges\n * Allows efficient partial DOM updates instead of clearing entire target\n */\ninterface DOMRange {\n start: Node; // Start marker (comment node)\n end: Node; // End marker (comment node)\n}\n\nexport const IS_DOM_AVAILABLE = typeof document !== 'undefined';\n\nconst SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n\nfunction resolveChildNamespace(\n type: string,\n parentNamespace?: string\n): string | undefined {\n if (type === 'svg') return SVG_NAMESPACE;\n if (parentNamespace === SVG_NAMESPACE && type !== 'foreignObject') {\n return SVG_NAMESPACE;\n }\n return undefined;\n}\n\nfunction createElementForNamespace(\n type: string,\n parentNamespace?: string\n): Element {\n const namespace = resolveChildNamespace(type, parentNamespace);\n return namespace\n ? document.createElementNS(namespace, type)\n : document.createElement(type);\n}\n\nconst domRanges = new WeakMap<object, DOMRange>();\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helper Types & Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface SimpleTextResult {\n isSimple: true;\n text: string;\n}\n\ninterface NotSimpleTextResult {\n isSimple: false;\n text?: undefined;\n}\n\ntype TextCheckResult = SimpleTextResult | NotSimpleTextResult;\n\nfunction tagNamesEqualIgnoreCase(a: string, b: string): boolean {\n return sharedTagNamesEqualIgnoreCase(a, b);\n}\n\n/**\n * Check if vnode children represent a simple text value\n */\nfunction checkSimpleText(vnodeChildren: unknown): TextCheckResult {\n if (!Array.isArray(vnodeChildren)) {\n if (\n typeof vnodeChildren === 'string' ||\n typeof vnodeChildren === 'number'\n ) {\n return { isSimple: true, text: String(vnodeChildren) };\n }\n } else if (vnodeChildren.length === 1) {\n const child = vnodeChildren[0];\n if (typeof child === 'string' || typeof child === 'number') {\n return { isSimple: true, text: String(child) };\n }\n }\n return { isSimple: false };\n}\n\n/**\n * Try to update a single text node in place\n * Returns true if update was performed, false otherwise\n */\nfunction tryUpdateTextInPlace(element: Element, text: string): boolean {\n if (\n element.childNodes.length === 1 &&\n element.firstChild?.nodeType === 3 // TEXT_NODE\n ) {\n (element.firstChild as Text).data = text;\n return true;\n }\n return false;\n}\n\n/**\n * Build a key map from existing DOM children\n */\nfunction buildKeyMapFromDOM(parent: Element): Map<string | number, Element> {\n const keyMap = new Map<string | number, Element>();\n for (\n let child = parent.firstElementChild;\n child;\n child = child.nextElementSibling\n ) {\n const k = child.getAttribute('data-key');\n if (k !== null) {\n keyMap.set(k, child);\n const n = Number(k);\n if (!Number.isNaN(n)) keyMap.set(n, child);\n }\n }\n return keyMap;\n}\n\n/**\n * Get or initialize key map for an element\n */\nfunction getOrBuildKeyMap(\n parent: Element\n): Map<string | number, Element> | undefined {\n let keyMap = keyedElements.get(parent);\n if (!keyMap) {\n keyMap = buildKeyMapFromDOM(parent);\n if (keyMap.size > 0) {\n keyedElements.set(parent, keyMap);\n }\n }\n return keyMap.size > 0 ? keyMap : undefined;\n}\n\n/**\n * Check if children array contains keyed elements\n */\nfunction hasKeyedChildren(children: unknown[]): boolean {\n for (let i = 0; i < children.length; i++) {\n if (extractKey(children[i]) !== undefined) return true;\n }\n return false;\n}\n\n/**\n * Track bulk text fast-path stats (dev only)\n */\nfunction trackBulkTextStats(\n stats: ReturnType<typeof performBulkTextReplace>\n): void {\n if (getRuntimeEnv().NODE_ENV !== 'production') {\n try {\n setDevValue('__LAST_BULK_TEXT_FASTPATH_STATS', stats);\n incDevCounter('bulkTextHits');\n } catch {\n // ignore\n }\n }\n}\n\n/**\n * Track bulk text miss (dev only)\n */\nfunction trackBulkTextMiss(): void {\n if (getRuntimeEnv().NODE_ENV !== 'production') {\n try {\n incDevCounter('bulkTextMisses');\n } catch {\n // ignore\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Child Reconciliation Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Reconcile keyed children with optional forced bulk path\n */\nfunction reconcileKeyed(\n parent: Element,\n children: VNode[],\n oldKeyMap: Map<string | number, Element> | undefined\n): void {\n // Optional forced positional bulk path for large keyed lists\n if (getRuntimeEnv().ASKR_FORCE_BULK_POSREUSE === '1') {\n const result = tryForcedBulkKeyedPath(parent, children);\n if (result) return;\n }\n\n // Standard keyed reconciliation\n const newKeyMap = reconcileKeyedChildren(parent, children, oldKeyMap);\n keyedElements.set(parent, newKeyMap);\n}\n\n/**\n * Try the forced bulk keyed positional path\n * Returns true if applied, false to fall back to normal reconciliation\n */\nfunction tryForcedBulkKeyedPath(parent: Element, children: VNode[]): boolean {\n try {\n const keyedVnodes: Array<{ key: string | number; vnode: VNode }> = [];\n for (const child of children) {\n const key = extractKey(child);\n if (_isDOMElement(child) && key !== undefined) {\n keyedVnodes.push({\n key,\n vnode: child,\n });\n }\n }\n\n // Only apply when all children are keyed and count matches\n if (keyedVnodes.length === 0 || keyedVnodes.length !== children.length) {\n return false;\n }\n\n const fastPathEnv = getRuntimeEnv();\n if (\n fastPathEnv.ASKR_FASTPATH_DEBUG === '1' ||\n fastPathEnv.ASKR_FASTPATH_DEBUG === 'true'\n ) {\n logger.warn(\n '[Askr][FASTPATH] forced positional bulk keyed reuse (evaluate-level)'\n );\n }\n\n const stats = performBulkPositionalKeyedTextUpdate(parent, keyedVnodes);\n\n const statsEnv = getRuntimeEnv();\n if (\n statsEnv.NODE_ENV !== 'production' ||\n statsEnv.ASKR_FASTPATH_DEBUG === '1'\n ) {\n try {\n setDevValue('__LAST_FASTPATH_STATS', stats);\n setDevValue('__LAST_FASTPATH_COMMIT_COUNT', 1);\n incDevCounter('bulkKeyedPositionalForced');\n } catch {\n // ignore\n }\n }\n\n // Rebuild keyed map from DOM\n const newMap = buildKeyMapFromDOM(parent);\n keyedElements.set(parent, newMap);\n return true;\n } catch (err) {\n const fallbackEnv = getRuntimeEnv();\n if (\n fallbackEnv.ASKR_FASTPATH_DEBUG === '1' ||\n fallbackEnv.ASKR_FASTPATH_DEBUG === 'true'\n ) {\n logger.warn(\n '[Askr][FASTPATH] forced bulk path failed, falling back',\n err\n );\n }\n return false;\n }\n}\n\n/**\n * Reconcile unkeyed children, using bulk fast-path when eligible\n */\nfunction reconcileUnkeyed(parent: Element, children: VNode[]): void {\n if (isBulkTextFastPathEligible(parent, children)) {\n const stats = performBulkTextReplace(parent, children);\n trackBulkTextStats(stats);\n } else {\n trackBulkTextMiss();\n updateUnkeyedChildren(parent, children);\n }\n keyedElements.delete(parent);\n}\n\n/**\n * Update element children from a For boundary vnode\n * Evaluates the For and reconciles the keyed children with existing DOM\n */\nfunction updateForBoundaryChildren(\n element: Element,\n forVnode: DOMElement\n): void {\n const controlState =\n forVnode._controlState ??\n (forVnode._forState as ControlBoundaryState | undefined);\n if (!controlState) return;\n\n const childrenVNodes =\n controlState.kind === 'for'\n ? evaluateForState(controlState)\n : controlState.kind === 'show'\n ? evaluateShowState(controlState)\n : evaluateCaseState(controlState);\n commitForBoundaryChildren(element, controlState, childrenVNodes);\n}\n\n/**\n * Update element children (handles keyed, unkeyed, For boundaries, and non-array cases)\n */\nfunction updateElementChildren(element: Element, vnodeChildren: unknown): void {\n // CRITICAL: Check for null/undefined explicitly, not falsy values\n // because 0, false, and '' are valid children\n if (vnodeChildren === null || vnodeChildren === undefined) {\n // Clean up all children before clearing\n for (let n = element.firstChild; n; ) {\n const next = n.nextSibling;\n if (n instanceof Element) {\n removeAllListeners(n);\n cleanupInstanceIfPresent(n);\n }\n n = next;\n }\n element.textContent = '';\n keyedElements.delete(element);\n return;\n }\n\n // Handle For boundary as a special single-child case (non-array)\n if (\n !Array.isArray(vnodeChildren) &&\n _isDOMElement(vnodeChildren) &&\n (vnodeChildren as DOMElement).type === __FOR_BOUNDARY__\n ) {\n updateForBoundaryChildren(element, vnodeChildren as DOMElement);\n return;\n }\n\n if (!Array.isArray(vnodeChildren) && isFragment(vnodeChildren)) {\n updateElementChildren(element, getFragmentChildren(vnodeChildren));\n return;\n }\n\n if (!Array.isArray(vnodeChildren)) {\n // Clean up all children before clearing\n for (let n = element.firstChild; n; ) {\n const next = n.nextSibling;\n if (n instanceof Element) {\n removeAllListeners(n);\n cleanupInstanceIfPresent(n);\n }\n n = next;\n }\n element.textContent = '';\n const dom = createDOMNode(vnodeChildren);\n if (dom) element.appendChild(dom);\n keyedElements.delete(element);\n return;\n }\n\n // Handle For boundary wrapped in a single-element array [forVnode]\n // This is common when JSX transpiles children as arrays\n if (\n vnodeChildren.length === 1 &&\n _isDOMElement(vnodeChildren[0]) &&\n (vnodeChildren[0] as DOMElement).type === __FOR_BOUNDARY__\n ) {\n updateForBoundaryChildren(element, vnodeChildren[0] as DOMElement);\n return;\n }\n\n if (hasKeyedChildren(vnodeChildren)) {\n const oldKeyMap = getOrBuildKeyMap(element);\n try {\n reconcileKeyed(element, vnodeChildren, oldKeyMap);\n } catch {\n // Fall back on error\n const newKeyMap = reconcileKeyedChildren(\n element,\n vnodeChildren,\n oldKeyMap\n );\n keyedElements.set(element, newKeyMap);\n }\n } else {\n reconcileUnkeyed(element, vnodeChildren);\n }\n}\n\n/**\n * Perform a smart update on an existing element\n * Tries text-in-place update first, then full child reconciliation\n */\nfunction smartUpdateElement(element: Element, vnode: DOMElement): void {\n if (vnode.key == null && element.hasAttribute('data-key')) {\n const existingKey = element.getAttribute('data-key');\n if (existingKey !== null) {\n const numericKey = Number(existingKey);\n vnode.key = Number.isNaN(numericKey) ? existingKey : numericKey;\n }\n }\n\n let vnodeChildren = vnode.props?.children ?? vnode.children;\n\n // CRITICAL: For boundary vnodes must NOT be wrapped into an array\n // They need special handling in updateElementChildren to call updateForBoundaryChildren\n if (\n vnodeChildren &&\n _isDOMElement(vnodeChildren) &&\n (vnodeChildren as DOMElement).type === __FOR_BOUNDARY__\n ) {\n // Pass For boundary directly without wrapping\n updateElementChildren(element, vnodeChildren);\n updateElementFromVnode(element, vnode, false);\n return;\n }\n\n // Normalize: if children is a single vnode (not an array), wrap it\n if (vnodeChildren && !Array.isArray(vnodeChildren)) {\n vnodeChildren = [vnodeChildren];\n }\n\n const textCheck = checkSimpleText(vnodeChildren);\n\n if (textCheck.isSimple && tryUpdateTextInPlace(element, textCheck.text)) {\n // Text updated in place, nothing more to do for children\n } else {\n updateElementChildren(element, vnodeChildren);\n }\n\n updateElementFromVnode(element, vnode, false);\n}\n\n/**\n * Process Fragment children with smart updates for each child\n */\nfunction processFragmentChildren(target: Element, childArray: unknown[]): void {\n updateElementChildren(target, childArray);\n}\n\nfunction cleanupRangeNode(node: Node): void {\n if (node instanceof Element) {\n removeAllListeners(node);\n cleanupInstanceIfPresent(node);\n }\n}\n\nfunction updateDOMRange(\n target: Element,\n range: DOMRange,\n children: unknown[]\n): void {\n let current: Node | null = range.start.nextSibling;\n\n for (let i = 0; i < children.length; i++) {\n const nextChild = children[i];\n const currentNode = current === range.end ? null : current;\n const nextCurrent = currentNode?.nextSibling ?? null;\n\n if (nextChild === null || nextChild === undefined || nextChild === false) {\n if (currentNode) {\n cleanupRangeNode(currentNode);\n target.removeChild(currentNode);\n }\n current = nextCurrent;\n continue;\n }\n\n if (typeof nextChild === 'string' || typeof nextChild === 'number') {\n if (currentNode?.nodeType === 3) {\n (currentNode as Text).data = String(nextChild);\n } else {\n const textNode = document.createTextNode(String(nextChild));\n if (currentNode) {\n cleanupRangeNode(currentNode);\n target.replaceChild(textNode, currentNode);\n } else {\n target.insertBefore(textNode, range.end);\n }\n }\n current = nextCurrent;\n continue;\n }\n\n if (\n currentNode instanceof Element &&\n _isDOMElement(nextChild) &&\n typeof nextChild.type === 'string' &&\n tagNamesEqualIgnoreCase(currentNode.tagName, nextChild.type)\n ) {\n smartUpdateElement(currentNode, nextChild);\n current = nextCurrent;\n continue;\n }\n\n const newDom = createDOMNode(nextChild);\n if (!newDom) {\n if (currentNode) {\n cleanupRangeNode(currentNode);\n target.removeChild(currentNode);\n }\n current = nextCurrent;\n continue;\n }\n\n if (currentNode) {\n cleanupRangeNode(currentNode);\n target.replaceChild(newDom, currentNode);\n } else {\n target.insertBefore(newDom, range.end);\n }\n\n current = nextCurrent;\n }\n\n while (current && current !== range.end) {\n const next = current.nextSibling;\n cleanupRangeNode(current);\n target.removeChild(current);\n current = next;\n }\n}\n\n/**\n * Apply props/attributes to an element (used for first render with keyed children)\n */\nfunction applyPropsToElement(el: Element, props: Props): void {\n for (const [key, value] of Object.entries(props)) {\n if (key === 'children' || key === 'key') continue;\n if (value === undefined || value === null || value === false) continue;\n\n if (key === 'ref') {\n applyRef(el, value);\n continue;\n }\n\n const eventProp = parseEventProp(key);\n if (eventProp) {\n const { eventName, capture } = eventProp;\n const wrappedHandler = createWrappedHandler(value as EventListener, true);\n const options = getEventListenerOptions(eventName, capture);\n const listenerKey = getEventListenerKey(eventName, capture);\n\n if (options !== undefined)\n el.addEventListener(eventName, wrappedHandler, options);\n else el.addEventListener(eventName, wrappedHandler);\n\n if (!elementListeners.has(el)) elementListeners.set(el, new Map());\n elementListeners.get(el)!.set(listenerKey, {\n handler: wrappedHandler,\n original: value as EventListener,\n eventName,\n options,\n });\n continue;\n }\n\n if (key === 'class' || key === 'className') {\n writeElementClassName(el, String(value));\n } else if (key === 'value' || key === 'checked') {\n (el as HTMLElement & Props)[key] = value;\n } else {\n setRenderedAttribute(el, key, String(value));\n }\n }\n}\n\ntype Ref<T> =\n | ((value: T | null) => void)\n | { current: T | null }\n | null\n | undefined;\n\nfunction applyRef<T>(el: T, ref: unknown): void {\n const r = ref as Ref<T>;\n if (!r) return;\n if (typeof r === 'function') {\n r(el);\n return;\n }\n try {\n (r as { current: T | null }).current = el;\n } catch {\n // Ignore write failures\n }\n}\n\n/**\n * Try to handle first render of element with keyed children\n * Returns true if handled, false to fall back to default rendering\n */\nfunction tryFirstRenderKeyedChildren(\n target: Element,\n vnode: DOMElement\n): boolean {\n const children = vnode.children;\n if (!Array.isArray(children) || !hasKeyedChildren(children)) {\n return false;\n }\n\n const el = createElementForNamespace(\n vnode.type as string,\n target.namespaceURI === SVG_NAMESPACE ? SVG_NAMESPACE : undefined\n );\n target.appendChild(el);\n\n applyPropsToElement(el, vnode.props || {});\n\n const newKeyMap = reconcileKeyedChildren(el, children, undefined);\n keyedElements.set(el, newKeyMap);\n return true;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Fragment Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Check if a vnode is a Fragment\n */\nfunction isFragment(vnode: unknown): vnode is DOMElement {\n return (\n _isDOMElement(vnode) &&\n typeof (vnode as DOMElement).type === 'symbol' &&\n ((vnode as DOMElement).type === Fragment ||\n String((vnode as DOMElement).type) === 'Symbol(askr.fragment)')\n );\n}\n\n/**\n * Unwrap Fragment to get children array\n */\nfunction getFragmentChildren(vnode: DOMElement): unknown[] {\n const fragmentChildren = vnode.props?.children ?? vnode.children ?? [];\n return Array.isArray(fragmentChildren)\n ? fragmentChildren\n : [fragmentChildren];\n}\n\nexport function evaluate(\n node: unknown,\n target: Element | null,\n context?: object\n): void {\n if (!target) return;\n // SSR guard: avoid DOM ops when not in a browser-like environment\n if (typeof document === 'undefined') {\n if (getRuntimeEnv().NODE_ENV !== 'production') {\n try {\n // Keep this lightweight and non-throwing so test harnesses and SSR\n // imports don't crash at runtime; callers should avoid calling\n // `evaluate` in SSR, but we make it safe as a no-op.\n console.warn('[Askr] evaluate() called in non-DOM environment; no-op.');\n } catch (e) {\n void e;\n }\n }\n return;\n }\n // Debug tracing to help understand why initial mounts sometimes don't\n // result in DOM mutations during tests.\n\n // If context provided, use component-owned DOM range (only replace that range)\n if (context && domRanges.has(context)) {\n const range = domRanges.get(context)!;\n const normalizedChildren =\n node === null || node === undefined || node === false\n ? []\n : isFragment(node)\n ? getFragmentChildren(node as DOMElement)\n : Array.isArray(node)\n ? node\n : [node];\n\n updateDOMRange(target, range, normalizedChildren);\n } else if (context) {\n // First render with context: create range markers\n const start = document.createComment('component-start');\n const end = document.createComment('component-end');\n target.appendChild(start);\n target.appendChild(end);\n domRanges.set(context, { start, end });\n // Render into the range\n const dom = createDOMNode(node);\n if (dom) {\n target.insertBefore(dom, end);\n }\n } else {\n // Root render (no context): smart update strategy\n // If target has exactly one child of the same element type as the vnode,\n // reuse the element and just update its content.\n // This preserves the element reference and event handlers across renders.\n\n let vnode = node;\n\n // If vnode is a Fragment, unwrap it to get the actual content for the smart update path.\n // Fragments become invisible in the DOM - their children are placed directly in the parent.\n // So for smart updates, we need to compare against the Fragment's children, not the Fragment itself.\n if (isFragment(vnode)) {\n const childArray = getFragmentChildren(vnode as DOMElement);\n // If Fragment has exactly one child that's an element, unwrap to that child\n // This allows the smart update path to match against it\n if (\n childArray.length === 1 &&\n _isDOMElement(childArray[0]) &&\n typeof (childArray[0] as DOMElement).type === 'string'\n ) {\n vnode = childArray[0];\n } else {\n // Fragment with multiple children - process each child with full smart update logic\n processFragmentChildren(target, childArray);\n return;\n }\n }\n\n if (\n _isDOMElement(vnode) &&\n (vnode as DOMElement).type === __FOR_BOUNDARY__\n ) {\n updateForBoundaryChildren(target, vnode as DOMElement);\n return;\n }\n\n if (Array.isArray(vnode)) {\n updateElementChildren(target, vnode);\n return;\n }\n\n // CRITICAL FIX: Check if target itself matches the vnode type AND target is the component's own element\n // This handles inline-rendered components where target IS the component's element.\n // For root components, target is a container (not the component's rendered element), so we skip this path.\n // We detect this by checking if the instance's target property points to this same element.\n const targetWithInstance = target as Element & {\n __ASKR_INSTANCE?: ComponentInstance;\n };\n const targetInstance = targetWithInstance.__ASKR_INSTANCE;\n if (targetInstance && targetInstance.target === target) {\n // This is a nested component's own element.\n if (_isDOMElement(vnode) && typeof vnode.type === 'function') {\n const syncedDom = syncComponentElement(\n target,\n vnode as DOMElement,\n vnode.type as ComponentFunction,\n (((vnode as DOMElement).props ?? {}) as Record<string, unknown>) || {}\n );\n\n if (syncedDom instanceof Element) {\n (\n syncedDom as Element & { __ASKR_INSTANCE?: ComponentInstance }\n ).__ASKR_INSTANCE = targetInstance;\n targetInstance.target = syncedDom;\n return;\n }\n }\n\n if (\n _isDOMElement(vnode) &&\n typeof vnode.type === 'string' &&\n tagNamesEqualIgnoreCase(target.tagName, vnode.type)\n ) {\n // Tag names match - update in place\n smartUpdateElement(target, vnode as DOMElement);\n return;\n }\n\n // Tag names don't match - need to replace the element\n // Create new element and replace old one in parent\n const newDom = createDOMNode(vnode);\n if (newDom && target.parentNode) {\n // Transfer the component instance to the new element\n if (newDom instanceof Element) {\n (\n newDom as Element & { __ASKR_INSTANCE?: ComponentInstance }\n ).__ASKR_INSTANCE = targetInstance;\n targetInstance.target = newDom as Element;\n }\n // Clean up old element\n removeAllListeners(target);\n target.parentNode.replaceChild(newDom, target);\n return;\n }\n }\n\n const firstChild = target.children[0] as Element | undefined;\n\n if (\n firstChild &&\n _isDOMElement(vnode) &&\n typeof vnode.type === 'string' &&\n tagNamesEqualIgnoreCase(firstChild.tagName, vnode.type)\n ) {\n // Reuse the existing element - it's the same type\n smartUpdateElement(firstChild, vnode as DOMElement);\n } else {\n // Clear and rebuild (first render or structure changed)\n target.textContent = '';\n\n // Check if this is an element with keyed children even on first render\n if (\n _isDOMElement(vnode) &&\n typeof vnode.type === 'string' &&\n tryFirstRenderKeyedChildren(target, vnode as DOMElement)\n ) {\n return;\n }\n\n // Default: create whole tree\n const dom = createDOMNode(vnode);\n if (dom) {\n target.appendChild(dom);\n }\n }\n }\n}\n\nexport function clearDOMRange(context: object): void {\n domRanges.delete(context);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAsDA,MAAM,gBAAgB;AAEtB,SAAS,sBACP,MACA,iBACoB;CACpB,IAAI,SAAS,OAAO,OAAO;CAC3B,IAAI,oBAAoB,iBAAiB,SAAS,iBAChD,OAAO;AAGX;AAEA,SAAS,0BACP,MACA,iBACS;CACT,MAAM,YAAY,sBAAsB,MAAM,eAAe;CAC7D,OAAO,YACH,SAAS,gBAAgB,WAAW,IAAI,IACxC,SAAS,cAAc,IAAI;AACjC;AAEA,MAAM,4BAAY,IAAI,QAA0B;AAkBhD,SAAS,wBAAwB,GAAW,GAAoB;CAC9D,OAAOA,0BAA8B,GAAG,CAAC;AAC3C;;;;AAKA,SAAS,gBAAgB,eAAyC;CAChE,IAAI,CAAC,MAAM,QAAQ,aAAa,GAC9B;MACE,OAAO,kBAAkB,YACzB,OAAO,kBAAkB,UAEzB,OAAO;GAAE,UAAU;GAAM,MAAM,OAAO,aAAa;EAAE;CACvD,OACK,IAAI,cAAc,WAAW,GAAG;EACrC,MAAM,QAAQ,cAAc;EAC5B,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAChD,OAAO;GAAE,UAAU;GAAM,MAAM,OAAO,KAAK;EAAE;CAEjD;CACA,OAAO,EAAE,UAAU,MAAM;AAC3B;;;;;AAMA,SAAS,qBAAqB,SAAkB,MAAuB;CACrE,IACE,QAAQ,WAAW,WAAW,KAC9B,QAAQ,YAAY,aAAa,GACjC;EACA,QAAS,WAAoB,OAAO;EACpC,OAAO;CACT;CACA,OAAO;AACT;;;;AAKA,SAAS,mBAAmB,QAAgD;CAC1E,MAAM,yBAAS,IAAI,IAA8B;CACjD,KACE,IAAI,QAAQ,OAAO,mBACnB,OACA,QAAQ,MAAM,oBACd;EACA,MAAM,IAAI,MAAM,aAAa,UAAU;EACvC,IAAI,MAAM,MAAM;GACd,OAAO,IAAI,GAAG,KAAK;GACnB,MAAM,IAAI,OAAO,CAAC;GAClB,IAAI,CAAC,OAAO,MAAM,CAAC,GAAG,OAAO,IAAI,GAAG,KAAK;EAC3C;CACF;CACA,OAAO;AACT;;;;AAKA,SAAS,iBACP,QAC2C;CAC3C,IAAI,SAAS,cAAc,IAAI,MAAM;CACrC,IAAI,CAAC,QAAQ;EACX,SAAS,mBAAmB,MAAM;EAClC,IAAI,OAAO,OAAO,GAChB,cAAc,IAAI,QAAQ,MAAM;CAEpC;CACA,OAAO,OAAO,OAAO,IAAI,SAAS;AACpC;;;;AAKA,SAAS,iBAAiB,UAA8B;CACtD,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KACnC,IAAI,WAAW,SAAS,EAAE,MAAM,QAAW,OAAO;CAEpD,OAAO;AACT;;;;AAKA,SAAS,mBACP,OACM;CACN,IAAI,cAAc,EAAE,aAAa,cAC/B,IAAI;EACF,YAAY,mCAAmC,KAAK;EACpD,cAAc,cAAc;CAC9B,QAAQ,CAER;AAEJ;;;;AAKA,SAAS,oBAA0B;CACjC,IAAI,cAAc,EAAE,aAAa,cAC/B,IAAI;EACF,cAAc,gBAAgB;CAChC,QAAQ,CAER;AAEJ;;;;AASA,SAAS,eACP,QACA,UACA,WACM;CAEN,IAAI,cAAc,EAAE,6BAA6B,KAE/C;MADe,uBAAuB,QAAQ,QAC1C,GAAQ;CAAA;CAId,MAAM,YAAY,uBAAuB,QAAQ,UAAU,SAAS;CACpE,cAAc,IAAI,QAAQ,SAAS;AACrC;;;;;AAMA,SAAS,uBAAuB,QAAiB,UAA4B;CAC3E,IAAI;EACF,MAAM,cAA6D,CAAC;EACpE,KAAK,MAAM,SAAS,UAAU;GAC5B,MAAM,MAAM,WAAW,KAAK;GAC5B,IAAI,cAAc,KAAK,KAAK,QAAQ,QAClC,YAAY,KAAK;IACf;IACA,OAAO;GACT,CAAC;EAEL;EAGA,IAAI,YAAY,WAAW,KAAK,YAAY,WAAW,SAAS,QAC9D,OAAO;EAGT,MAAM,cAAc,cAAc;EAClC,IACE,YAAY,wBAAwB,OACpC,YAAY,wBAAwB,QAEpC,OAAO,KACL,sEACF;EAGF,MAAM,QAAQ,qCAAqC,QAAQ,WAAW;EAEtE,MAAM,WAAW,cAAc;EAC/B,IACE,SAAS,aAAa,gBACtB,SAAS,wBAAwB,KAEjC,IAAI;GACF,YAAY,yBAAyB,KAAK;GAC1C,YAAY,gCAAgC,CAAC;GAC7C,cAAc,2BAA2B;EAC3C,QAAQ,CAER;EAIF,MAAM,SAAS,mBAAmB,MAAM;EACxC,cAAc,IAAI,QAAQ,MAAM;EAChC,OAAO;CACT,SAAS,KAAK;EACZ,MAAM,cAAc,cAAc;EAClC,IACE,YAAY,wBAAwB,OACpC,YAAY,wBAAwB,QAEpC,OAAO,KACL,0DACA,GACF;EAEF,OAAO;CACT;AACF;;;;AAKA,SAAS,iBAAiB,QAAiB,UAAyB;CAClE,IAAI,2BAA2B,QAAQ,QAAQ,GAE7C,mBADc,uBAAuB,QAAQ,QAC1B,CAAK;MACnB;EACL,kBAAkB;EAClB,sBAAsB,QAAQ,QAAQ;CACxC;CACA,cAAc,OAAO,MAAM;AAC7B;;;;;AAMA,SAAS,0BACP,SACA,UACM;CACN,MAAM,eACJ,SAAS,iBACR,SAAS;CACZ,IAAI,CAAC,cAAc;CAQnB,0BAA0B,SAAS,cALjC,aAAa,SAAS,QAClB,iBAAiB,YAAY,IAC7B,aAAa,SAAS,SACpB,kBAAkB,YAAY,IAC9B,kBAAkB,YAAY,CACyB;AACjE;;;;AAKA,SAAS,sBAAsB,SAAkB,eAA8B;CAG7E,IAAI,kBAAkB,QAAQ,kBAAkB,QAAW;EAEzD,KAAK,IAAI,IAAI,QAAQ,YAAY,IAAK;GACpC,MAAM,OAAO,EAAE;GACf,IAAI,aAAa,SAAS;IACxB,mBAAmB,CAAC;IACpB,yBAAyB,CAAC;GAC5B;GACA,IAAI;EACN;EACA,QAAQ,cAAc;EACtB,cAAc,OAAO,OAAO;EAC5B;CACF;CAGA,IACE,CAAC,MAAM,QAAQ,aAAa,KAC5B,cAAc,aAAa,KAC1B,cAA6B,SAAS,kBACvC;EACA,0BAA0B,SAAS,aAA2B;EAC9D;CACF;CAEA,IAAI,CAAC,MAAM,QAAQ,aAAa,KAAK,WAAW,aAAa,GAAG;EAC9D,sBAAsB,SAAS,oBAAoB,aAAa,CAAC;EACjE;CACF;CAEA,IAAI,CAAC,MAAM,QAAQ,aAAa,GAAG;EAEjC,KAAK,IAAI,IAAI,QAAQ,YAAY,IAAK;GACpC,MAAM,OAAO,EAAE;GACf,IAAI,aAAa,SAAS;IACxB,mBAAmB,CAAC;IACpB,yBAAyB,CAAC;GAC5B;GACA,IAAI;EACN;EACA,QAAQ,cAAc;EACtB,MAAM,MAAM,cAAc,aAAa;EACvC,IAAI,KAAK,QAAQ,YAAY,GAAG;EAChC,cAAc,OAAO,OAAO;EAC5B;CACF;CAIA,IACE,cAAc,WAAW,KACzB,cAAc,cAAc,EAAE,KAC7B,cAAc,GAAkB,SAAS,kBAC1C;EACA,0BAA0B,SAAS,cAAc,EAAgB;EACjE;CACF;CAEA,IAAI,iBAAiB,aAAa,GAAG;EACnC,MAAM,YAAY,iBAAiB,OAAO;EAC1C,IAAI;GACF,eAAe,SAAS,eAAe,SAAS;EAClD,QAAQ;GAEN,MAAM,YAAY,uBAChB,SACA,eACA,SACF;GACA,cAAc,IAAI,SAAS,SAAS;EACtC;CACF,OACE,iBAAiB,SAAS,aAAa;AAE3C;;;;;AAMA,SAAS,mBAAmB,SAAkB,OAAyB;CACrE,IAAI,MAAM,OAAO,QAAQ,QAAQ,aAAa,UAAU,GAAG;EACzD,MAAM,cAAc,QAAQ,aAAa,UAAU;EACnD,IAAI,gBAAgB,MAAM;GACxB,MAAM,aAAa,OAAO,WAAW;GACrC,MAAM,MAAM,OAAO,MAAM,UAAU,IAAI,cAAc;EACvD;CACF;CAEA,IAAI,gBAAgB,MAAM,OAAO,YAAY,MAAM;CAInD,IACE,iBACA,cAAc,aAAa,KAC1B,cAA6B,SAAS,kBACvC;EAEA,sBAAsB,SAAS,aAAa;EAC5C,uBAAuB,SAAS,OAAO,KAAK;EAC5C;CACF;CAGA,IAAI,iBAAiB,CAAC,MAAM,QAAQ,aAAa,GAC/C,gBAAgB,CAAC,aAAa;CAGhC,MAAM,YAAY,gBAAgB,aAAa;CAE/C,IAAI,UAAU,YAAY,qBAAqB,SAAS,UAAU,IAAI,GAAG,CAEzE,OACE,sBAAsB,SAAS,aAAa;CAG9C,uBAAuB,SAAS,OAAO,KAAK;AAC9C;;;;AAKA,SAAS,wBAAwB,QAAiB,YAA6B;CAC7E,sBAAsB,QAAQ,UAAU;AAC1C;AAEA,SAAS,iBAAiB,MAAkB;CAC1C,IAAI,gBAAgB,SAAS;EAC3B,mBAAmB,IAAI;EACvB,yBAAyB,IAAI;CAC/B;AACF;AAEA,SAAS,eACP,QACA,OACA,UACM;CACN,IAAI,UAAuB,MAAM,MAAM;CAEvC,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,YAAY,SAAS;EAC3B,MAAM,cAAc,YAAY,MAAM,MAAM,OAAO;EACnD,MAAM,cAAc,aAAa,eAAe;EAEhD,IAAI,cAAc,QAAQ,cAAc,UAAa,cAAc,OAAO;GACxE,IAAI,aAAa;IACf,iBAAiB,WAAW;IAC5B,OAAO,YAAY,WAAW;GAChC;GACA,UAAU;GACV;EACF;EAEA,IAAI,OAAO,cAAc,YAAY,OAAO,cAAc,UAAU;GAClE,IAAI,aAAa,aAAa,GAC5B,YAAsB,OAAO,OAAO,SAAS;QACxC;IACL,MAAM,WAAW,SAAS,eAAe,OAAO,SAAS,CAAC;IAC1D,IAAI,aAAa;KACf,iBAAiB,WAAW;KAC5B,OAAO,aAAa,UAAU,WAAW;IAC3C,OACE,OAAO,aAAa,UAAU,MAAM,GAAG;GAE3C;GACA,UAAU;GACV;EACF;EAEA,IACE,uBAAuB,WACvB,cAAc,SAAS,KACvB,OAAO,UAAU,SAAS,YAC1B,wBAAwB,YAAY,SAAS,UAAU,IAAI,GAC3D;GACA,mBAAmB,aAAa,SAAS;GACzC,UAAU;GACV;EACF;EAEA,MAAM,SAAS,cAAc,SAAS;EACtC,IAAI,CAAC,QAAQ;GACX,IAAI,aAAa;IACf,iBAAiB,WAAW;IAC5B,OAAO,YAAY,WAAW;GAChC;GACA,UAAU;GACV;EACF;EAEA,IAAI,aAAa;GACf,iBAAiB,WAAW;GAC5B,OAAO,aAAa,QAAQ,WAAW;EACzC,OACE,OAAO,aAAa,QAAQ,MAAM,GAAG;EAGvC,UAAU;CACZ;CAEA,OAAO,WAAW,YAAY,MAAM,KAAK;EACvC,MAAM,OAAO,QAAQ;EACrB,iBAAiB,OAAO;EACxB,OAAO,YAAY,OAAO;EAC1B,UAAU;CACZ;AACF;;;;AAKA,SAAS,oBAAoB,IAAa,OAAoB;CAC5D,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAAG;EAChD,IAAI,QAAQ,cAAc,QAAQ,OAAO;EACzC,IAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,OAAO;EAE9D,IAAI,QAAQ,OAAO;GACjB,SAAS,IAAI,KAAK;GAClB;EACF;EAEA,MAAM,YAAY,eAAe,GAAG;EACpC,IAAI,WAAW;GACb,MAAM,EAAE,WAAW,YAAY;GAC/B,MAAM,iBAAiB,qBAAqB,OAAwB,IAAI;GACxE,MAAM,UAAU,wBAAwB,WAAW,OAAO;GAC1D,MAAM,cAAc,oBAAoB,WAAW,OAAO;GAE1D,IAAI,YAAY,QACd,GAAG,iBAAiB,WAAW,gBAAgB,OAAO;QACnD,GAAG,iBAAiB,WAAW,cAAc;GAElD,IAAI,CAAC,iBAAiB,IAAI,EAAE,GAAG,iBAAiB,IAAI,oBAAI,IAAI,IAAI,CAAC;GACjE,iBAAiB,IAAI,EAAE,EAAG,IAAI,aAAa;IACzC,SAAS;IACT,UAAU;IACV;IACA;GACF,CAAC;GACD;EACF;EAEA,IAAI,QAAQ,WAAW,QAAQ,aAC7B,sBAAsB,IAAI,OAAO,KAAK,CAAC;OAClC,IAAI,QAAQ,WAAW,QAAQ,WACpC,GAA4B,OAAO;OAEnC,qBAAqB,IAAI,KAAK,OAAO,KAAK,CAAC;CAE/C;AACF;AAQA,SAAS,SAAY,IAAO,KAAoB;CAC9C,MAAM,IAAI;CACV,IAAI,CAAC,GAAG;CACR,IAAI,OAAO,MAAM,YAAY;EAC3B,EAAE,EAAE;EACJ;CACF;CACA,IAAI;EACF,EAA6B,UAAU;CACzC,QAAQ,CAER;AACF;;;;;AAMA,SAAS,4BACP,QACA,OACS;CACT,MAAM,WAAW,MAAM;CACvB,IAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,CAAC,iBAAiB,QAAQ,GACxD,OAAO;CAGT,MAAM,KAAK,0BACT,MAAM,MACN,OAAO,iBAAiB,gBAAgB,gBAAgB,MAC1D;CACA,OAAO,YAAY,EAAE;CAErB,oBAAoB,IAAI,MAAM,SAAS,CAAC,CAAC;CAEzC,MAAM,YAAY,uBAAuB,IAAI,UAAU,MAAS;CAChE,cAAc,IAAI,IAAI,SAAS;CAC/B,OAAO;AACT;;;;AASA,SAAS,WAAW,OAAqC;CACvD,OACE,cAAc,KAAK,KACnB,OAAQ,MAAqB,SAAS,aACpC,MAAqB,SAAS,YAC9B,OAAQ,MAAqB,IAAI,MAAM;AAE7C;;;;AAKA,SAAS,oBAAoB,OAA8B;CACzD,MAAM,mBAAmB,MAAM,OAAO,YAAY,MAAM,YAAY,CAAC;CACrE,OAAO,MAAM,QAAQ,gBAAgB,IACjC,mBACA,CAAC,gBAAgB;AACvB;AAEA,SAAgB,SACd,MACA,QACA,SACM;CACN,IAAI,CAAC,QAAQ;CAEb,IAAI,OAAO,aAAa,aAAa;EACnC,IAAI,cAAc,EAAE,aAAa,cAC/B,IAAI;GAIF,QAAQ,KAAK,yDAAyD;EACxE,SAAS,GAAG,CAEZ;EAEF;CACF;CAKA,IAAI,WAAW,UAAU,IAAI,OAAO,GAWlC,eAAe,QAVD,UAAU,IAAI,OAUL,GARrB,SAAS,QAAQ,SAAS,UAAa,SAAS,QAC5C,CAAC,IACD,WAAW,IAAI,IACb,oBAAoB,IAAkB,IACtC,MAAM,QAAQ,IAAI,IAChB,OACA,CAAC,IAAI,CAEiC;MAC3C,IAAI,SAAS;EAElB,MAAM,QAAQ,SAAS,cAAc,iBAAiB;EACtD,MAAM,MAAM,SAAS,cAAc,eAAe;EAClD,OAAO,YAAY,KAAK;EACxB,OAAO,YAAY,GAAG;EACtB,UAAU,IAAI,SAAS;GAAE;GAAO;EAAI,CAAC;EAErC,MAAM,MAAM,cAAc,IAAI;EAC9B,IAAI,KACF,OAAO,aAAa,KAAK,GAAG;CAEhC,OAAO;EAML,IAAI,QAAQ;EAKZ,IAAI,WAAW,KAAK,GAAG;GACrB,MAAM,aAAa,oBAAoB,KAAmB;GAG1D,IACE,WAAW,WAAW,KACtB,cAAc,WAAW,EAAE,KAC3B,OAAQ,WAAW,GAAkB,SAAS,UAE9C,QAAQ,WAAW;QACd;IAEL,wBAAwB,QAAQ,UAAU;IAC1C;GACF;EACF;EAEA,IACE,cAAc,KAAK,KAClB,MAAqB,SAAS,kBAC/B;GACA,0BAA0B,QAAQ,KAAmB;GACrD;EACF;EAEA,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,sBAAsB,QAAQ,KAAK;GACnC;EACF;EASA,MAAM,iBAAiB,OAAmB;EAC1C,IAAI,kBAAkB,eAAe,WAAW,QAAQ;GAEtD,IAAI,cAAc,KAAK,KAAK,OAAO,MAAM,SAAS,YAAY;IAC5D,MAAM,YAAY,qBAChB,QACA,OACA,MAAM,OACH,MAAqB,SAAS,CAAC,MAAkC,CAAC,CACvE;IAEA,IAAI,qBAAqB,SAAS;KAChC,UAEE,kBAAkB;KACpB,eAAe,SAAS;KACxB;IACF;GACF;GAEA,IACE,cAAc,KAAK,KACnB,OAAO,MAAM,SAAS,YACtB,wBAAwB,OAAO,SAAS,MAAM,IAAI,GAClD;IAEA,mBAAmB,QAAQ,KAAmB;IAC9C;GACF;GAIA,MAAM,SAAS,cAAc,KAAK;GAClC,IAAI,UAAU,OAAO,YAAY;IAE/B,IAAI,kBAAkB,SAAS;KAC7B,OAEE,kBAAkB;KACpB,eAAe,SAAS;IAC1B;IAEA,mBAAmB,MAAM;IACzB,OAAO,WAAW,aAAa,QAAQ,MAAM;IAC7C;GACF;EACF;EAEA,MAAM,aAAa,OAAO,SAAS;EAEnC,IACE,cACA,cAAc,KAAK,KACnB,OAAO,MAAM,SAAS,YACtB,wBAAwB,WAAW,SAAS,MAAM,IAAI,GAGtD,mBAAmB,YAAY,KAAmB;OAC7C;GAEL,OAAO,cAAc;GAGrB,IACE,cAAc,KAAK,KACnB,OAAO,MAAM,SAAS,YACtB,4BAA4B,QAAQ,KAAmB,GAEvD;GAIF,MAAM,MAAM,cAAc,KAAK;GAC/B,IAAI,KACF,OAAO,YAAY,GAAG;EAE1B;CACF;AACF"}
1
+ {"version":3,"file":"evaluate.js","names":["sharedTagNamesEqualIgnoreCase"],"sources":["../../src/renderer/evaluate.ts"],"sourcesContent":["import { logger } from '../dev/logger';\nimport { getRuntimeEnv } from './env';\nimport type { Props } from '../common/props';\nimport type { ComponentInstance } from '../runtime/component';\nimport type { ComponentFunction } from '../runtime/component';\nimport {\n elementListeners,\n removeAllListeners,\n teardownNodeSubtree,\n} from './cleanup';\nimport { keyedElements } from './keyed';\nimport { reconcileKeyedChildren } from './reconcile';\nimport { _isDOMElement, type DOMElement, type VNode } from './types';\nimport { __FOR_BOUNDARY__ } from '../common/vnode';\nimport { evaluateForState } from '../runtime/for';\nimport {\n evaluateCaseState,\n evaluateShowState,\n type ControlBoundaryState,\n} from '../runtime/control';\nimport {\n createDOMNode,\n commitForBoundaryChildren,\n syncComponentElement,\n updateElementFromVnode,\n updateUnkeyedChildren,\n performBulkPositionalKeyedTextUpdate,\n performBulkTextReplace,\n isBulkTextFastPathEligible,\n} from './dom';\nimport { setDevValue, incDevCounter } from '../runtime/dev-namespace';\nimport { Fragment } from '../common/jsx';\nimport {\n createWrappedHandler,\n extractKey,\n getEventListenerKey,\n getEventListenerOptions,\n parseEventProp,\n setRenderedAttribute,\n tagNamesEqualIgnoreCase as sharedTagNamesEqualIgnoreCase,\n writeElementClassName,\n} from './utils';\n\n/**\n * Internal marker for component-owned DOM ranges\n * Allows efficient partial DOM updates instead of clearing entire target\n */\ninterface DOMRange {\n start: Node; // Start marker (comment node)\n end: Node; // End marker (comment node)\n}\n\nexport const IS_DOM_AVAILABLE = typeof document !== 'undefined';\n\nconst SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n\nfunction resolveChildNamespace(\n type: string,\n parentNamespace?: string\n): string | undefined {\n if (type === 'svg') return SVG_NAMESPACE;\n if (parentNamespace === SVG_NAMESPACE && type !== 'foreignObject') {\n return SVG_NAMESPACE;\n }\n return undefined;\n}\n\nfunction createElementForNamespace(\n type: string,\n parentNamespace?: string\n): Element {\n const namespace = resolveChildNamespace(type, parentNamespace);\n return namespace\n ? document.createElementNS(namespace, type)\n : document.createElement(type);\n}\n\nconst domRanges = new WeakMap<object, DOMRange>();\n\ntype ComponentHostElement = Element & {\n __ASKR_INSTANCE?: ComponentInstance;\n __ASKR_INSTANCES?: ComponentInstance[];\n};\n\nfunction getRetainedHostOwnerChain(\n host: ComponentHostElement,\n owner: ComponentInstance\n): ComponentInstance[] {\n const instances = host.__ASKR_INSTANCES ?? [];\n const ownerIndex = instances.indexOf(owner);\n\n return ownerIndex >= 0 ? instances.slice(ownerIndex) : [owner];\n}\n\nfunction retainHostOwnerChain(\n host: Element,\n owner: ComponentInstance,\n retainedInstances: ComponentInstance[]\n): void {\n const componentHost = host as ComponentHostElement;\n const existing = componentHost.__ASKR_INSTANCES ?? [];\n const nextInstances = [...existing];\n\n for (const instance of retainedInstances) {\n if (!nextInstances.includes(instance)) {\n nextInstances.push(instance);\n }\n }\n\n componentHost.__ASKR_INSTANCES = nextInstances;\n componentHost.__ASKR_INSTANCE = owner;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helper Types & Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface SimpleTextResult {\n isSimple: true;\n text: string;\n}\n\ninterface NotSimpleTextResult {\n isSimple: false;\n text?: undefined;\n}\n\ntype TextCheckResult = SimpleTextResult | NotSimpleTextResult;\n\nfunction tagNamesEqualIgnoreCase(a: string, b: string): boolean {\n return sharedTagNamesEqualIgnoreCase(a, b);\n}\n\n/**\n * Check if vnode children represent a simple text value\n */\nfunction checkSimpleText(vnodeChildren: unknown): TextCheckResult {\n if (!Array.isArray(vnodeChildren)) {\n if (\n typeof vnodeChildren === 'string' ||\n typeof vnodeChildren === 'number'\n ) {\n return { isSimple: true, text: String(vnodeChildren) };\n }\n } else if (vnodeChildren.length === 1) {\n const child = vnodeChildren[0];\n if (typeof child === 'string' || typeof child === 'number') {\n return { isSimple: true, text: String(child) };\n }\n }\n return { isSimple: false };\n}\n\n/**\n * Try to update a single text node in place\n * Returns true if update was performed, false otherwise\n */\nfunction tryUpdateTextInPlace(element: Element, text: string): boolean {\n if (\n element.childNodes.length === 1 &&\n element.firstChild?.nodeType === 3 // TEXT_NODE\n ) {\n (element.firstChild as Text).data = text;\n return true;\n }\n return false;\n}\n\n/**\n * Build a key map from existing DOM children\n */\nfunction buildKeyMapFromDOM(parent: Element): Map<string | number, Element> {\n const keyMap = new Map<string | number, Element>();\n for (\n let child = parent.firstElementChild;\n child;\n child = child.nextElementSibling\n ) {\n const k = child.getAttribute('data-key');\n if (k !== null) {\n keyMap.set(k, child);\n const n = Number(k);\n if (!Number.isNaN(n)) keyMap.set(n, child);\n }\n }\n return keyMap;\n}\n\n/**\n * Get or initialize key map for an element\n */\nfunction getOrBuildKeyMap(\n parent: Element\n): Map<string | number, Element> | undefined {\n let keyMap = keyedElements.get(parent);\n if (!keyMap) {\n keyMap = buildKeyMapFromDOM(parent);\n if (keyMap.size > 0) {\n keyedElements.set(parent, keyMap);\n }\n }\n return keyMap.size > 0 ? keyMap : undefined;\n}\n\n/**\n * Check if children array contains keyed elements\n */\nfunction hasKeyedChildren(children: unknown[]): boolean {\n for (let i = 0; i < children.length; i++) {\n if (extractKey(children[i]) !== undefined) return true;\n }\n return false;\n}\n\n/**\n * Track bulk text fast-path stats (dev only)\n */\nfunction trackBulkTextStats(\n stats: ReturnType<typeof performBulkTextReplace>\n): void {\n if (getRuntimeEnv().NODE_ENV !== 'production') {\n try {\n setDevValue('__LAST_BULK_TEXT_FASTPATH_STATS', stats);\n incDevCounter('bulkTextHits');\n } catch {\n // ignore\n }\n }\n}\n\n/**\n * Track bulk text miss (dev only)\n */\nfunction trackBulkTextMiss(): void {\n if (getRuntimeEnv().NODE_ENV !== 'production') {\n try {\n incDevCounter('bulkTextMisses');\n } catch {\n // ignore\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Child Reconciliation Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Reconcile keyed children with optional forced bulk path\n */\nfunction reconcileKeyed(\n parent: Element,\n children: VNode[],\n oldKeyMap: Map<string | number, Element> | undefined\n): void {\n // Optional forced positional bulk path for large keyed lists\n if (getRuntimeEnv().ASKR_FORCE_BULK_POSREUSE === '1') {\n const result = tryForcedBulkKeyedPath(parent, children);\n if (result) return;\n }\n\n // Standard keyed reconciliation\n const newKeyMap = reconcileKeyedChildren(parent, children, oldKeyMap);\n keyedElements.set(parent, newKeyMap);\n}\n\n/**\n * Try the forced bulk keyed positional path\n * Returns true if applied, false to fall back to normal reconciliation\n */\nfunction tryForcedBulkKeyedPath(parent: Element, children: VNode[]): boolean {\n try {\n const keyedVnodes: Array<{ key: string | number; vnode: VNode }> = [];\n for (const child of children) {\n const key = extractKey(child);\n if (_isDOMElement(child) && key !== undefined) {\n keyedVnodes.push({\n key,\n vnode: child,\n });\n }\n }\n\n // Only apply when all children are keyed and count matches\n if (keyedVnodes.length === 0 || keyedVnodes.length !== children.length) {\n return false;\n }\n\n const fastPathEnv = getRuntimeEnv();\n if (\n fastPathEnv.ASKR_FASTPATH_DEBUG === '1' ||\n fastPathEnv.ASKR_FASTPATH_DEBUG === 'true'\n ) {\n logger.warn(\n '[Askr][FASTPATH] forced positional bulk keyed reuse (evaluate-level)'\n );\n }\n\n const stats = performBulkPositionalKeyedTextUpdate(parent, keyedVnodes);\n\n const statsEnv = getRuntimeEnv();\n if (\n statsEnv.NODE_ENV !== 'production' ||\n statsEnv.ASKR_FASTPATH_DEBUG === '1'\n ) {\n try {\n setDevValue('__LAST_FASTPATH_STATS', stats);\n setDevValue('__LAST_FASTPATH_COMMIT_COUNT', 1);\n incDevCounter('bulkKeyedPositionalForced');\n } catch {\n // ignore\n }\n }\n\n // Rebuild keyed map from DOM\n const newMap = buildKeyMapFromDOM(parent);\n keyedElements.set(parent, newMap);\n return true;\n } catch (err) {\n const fallbackEnv = getRuntimeEnv();\n if (\n fallbackEnv.ASKR_FASTPATH_DEBUG === '1' ||\n fallbackEnv.ASKR_FASTPATH_DEBUG === 'true'\n ) {\n logger.warn(\n '[Askr][FASTPATH] forced bulk path failed, falling back',\n err\n );\n }\n return false;\n }\n}\n\n/**\n * Reconcile unkeyed children, using bulk fast-path when eligible\n */\nfunction reconcileUnkeyed(parent: Element, children: VNode[]): void {\n if (isBulkTextFastPathEligible(parent, children)) {\n const stats = performBulkTextReplace(parent, children);\n trackBulkTextStats(stats);\n } else {\n trackBulkTextMiss();\n updateUnkeyedChildren(parent, children);\n }\n keyedElements.delete(parent);\n}\n\n/**\n * Update element children from a For boundary vnode\n * Evaluates the For and reconciles the keyed children with existing DOM\n */\nfunction updateForBoundaryChildren(\n element: Element,\n forVnode: DOMElement\n): void {\n const controlState =\n forVnode._controlState ??\n (forVnode._forState as ControlBoundaryState | undefined);\n if (!controlState) return;\n\n const childrenVNodes =\n controlState.kind === 'for'\n ? evaluateForState(controlState)\n : controlState.kind === 'show'\n ? evaluateShowState(controlState)\n : evaluateCaseState(controlState);\n commitForBoundaryChildren(element, controlState, childrenVNodes);\n}\n\n/**\n * Update element children (handles keyed, unkeyed, For boundaries, and non-array cases)\n */\nfunction updateElementChildren(element: Element, vnodeChildren: unknown): void {\n // CRITICAL: Check for null/undefined explicitly, not falsy values\n // because 0, false, and '' are valid children\n if (vnodeChildren === null || vnodeChildren === undefined) {\n // Clean up all children before clearing\n for (let n = element.firstChild; n; ) {\n const next = n.nextSibling;\n cleanupRangeNode(n);\n n = next;\n }\n element.textContent = '';\n keyedElements.delete(element);\n return;\n }\n\n // Handle For boundary as a special single-child case (non-array)\n if (\n !Array.isArray(vnodeChildren) &&\n _isDOMElement(vnodeChildren) &&\n (vnodeChildren as DOMElement).type === __FOR_BOUNDARY__\n ) {\n updateForBoundaryChildren(element, vnodeChildren as DOMElement);\n return;\n }\n\n if (!Array.isArray(vnodeChildren) && isFragment(vnodeChildren)) {\n updateElementChildren(element, getFragmentChildren(vnodeChildren));\n return;\n }\n\n if (!Array.isArray(vnodeChildren)) {\n // Clean up all children before clearing\n for (let n = element.firstChild; n; ) {\n const next = n.nextSibling;\n cleanupRangeNode(n);\n n = next;\n }\n element.textContent = '';\n const dom = createDOMNode(vnodeChildren);\n if (dom) element.appendChild(dom);\n keyedElements.delete(element);\n return;\n }\n\n // Handle For boundary wrapped in a single-element array [forVnode]\n // This is common when JSX transpiles children as arrays\n if (\n vnodeChildren.length === 1 &&\n _isDOMElement(vnodeChildren[0]) &&\n (vnodeChildren[0] as DOMElement).type === __FOR_BOUNDARY__\n ) {\n updateForBoundaryChildren(element, vnodeChildren[0] as DOMElement);\n return;\n }\n\n if (hasKeyedChildren(vnodeChildren)) {\n const oldKeyMap = getOrBuildKeyMap(element);\n try {\n reconcileKeyed(element, vnodeChildren, oldKeyMap);\n } catch {\n // Fall back on error\n const newKeyMap = reconcileKeyedChildren(\n element,\n vnodeChildren,\n oldKeyMap\n );\n keyedElements.set(element, newKeyMap);\n }\n } else {\n reconcileUnkeyed(element, vnodeChildren);\n }\n}\n\n/**\n * Perform a smart update on an existing element\n * Tries text-in-place update first, then full child reconciliation\n */\nfunction smartUpdateElement(element: Element, vnode: DOMElement): void {\n if (vnode.key == null && element.hasAttribute('data-key')) {\n const existingKey = element.getAttribute('data-key');\n if (existingKey !== null) {\n const numericKey = Number(existingKey);\n vnode.key = Number.isNaN(numericKey) ? existingKey : numericKey;\n }\n }\n\n let vnodeChildren = vnode.props?.children ?? vnode.children;\n\n // CRITICAL: For boundary vnodes must NOT be wrapped into an array\n // They need special handling in updateElementChildren to call updateForBoundaryChildren\n if (\n vnodeChildren &&\n _isDOMElement(vnodeChildren) &&\n (vnodeChildren as DOMElement).type === __FOR_BOUNDARY__\n ) {\n // Pass For boundary directly without wrapping\n updateElementChildren(element, vnodeChildren);\n updateElementFromVnode(element, vnode, false);\n return;\n }\n\n // Normalize: if children is a single vnode (not an array), wrap it\n if (vnodeChildren && !Array.isArray(vnodeChildren)) {\n vnodeChildren = [vnodeChildren];\n }\n\n const textCheck = checkSimpleText(vnodeChildren);\n\n if (textCheck.isSimple && tryUpdateTextInPlace(element, textCheck.text)) {\n // Text updated in place, nothing more to do for children\n } else {\n updateElementChildren(element, vnodeChildren);\n }\n\n updateElementFromVnode(element, vnode, false);\n}\n\n/**\n * Process Fragment children with smart updates for each child\n */\nfunction processFragmentChildren(target: Element, childArray: unknown[]): void {\n updateElementChildren(target, childArray);\n}\n\nfunction cleanupRangeNode(node: Node): void {\n teardownNodeSubtree(node);\n}\n\nfunction updateDOMRange(\n target: Element,\n range: DOMRange,\n children: unknown[]\n): void {\n let current: Node | null = range.start.nextSibling;\n\n for (let i = 0; i < children.length; i++) {\n const nextChild = children[i];\n const currentNode = current === range.end ? null : current;\n const nextCurrent = currentNode?.nextSibling ?? null;\n\n if (nextChild === null || nextChild === undefined || nextChild === false) {\n if (currentNode) {\n cleanupRangeNode(currentNode);\n target.removeChild(currentNode);\n }\n current = nextCurrent;\n continue;\n }\n\n if (typeof nextChild === 'string' || typeof nextChild === 'number') {\n if (currentNode?.nodeType === 3) {\n (currentNode as Text).data = String(nextChild);\n } else {\n const textNode = document.createTextNode(String(nextChild));\n if (currentNode) {\n cleanupRangeNode(currentNode);\n target.replaceChild(textNode, currentNode);\n } else {\n target.insertBefore(textNode, range.end);\n }\n }\n current = nextCurrent;\n continue;\n }\n\n if (\n currentNode instanceof Element &&\n _isDOMElement(nextChild) &&\n typeof nextChild.type === 'string' &&\n tagNamesEqualIgnoreCase(currentNode.tagName, nextChild.type)\n ) {\n smartUpdateElement(currentNode, nextChild);\n current = nextCurrent;\n continue;\n }\n\n const newDom = createDOMNode(nextChild);\n if (!newDom) {\n if (currentNode) {\n cleanupRangeNode(currentNode);\n target.removeChild(currentNode);\n }\n current = nextCurrent;\n continue;\n }\n\n if (currentNode) {\n cleanupRangeNode(currentNode);\n target.replaceChild(newDom, currentNode);\n } else {\n target.insertBefore(newDom, range.end);\n }\n\n current = nextCurrent;\n }\n\n while (current && current !== range.end) {\n const next = current.nextSibling;\n cleanupRangeNode(current);\n target.removeChild(current);\n current = next;\n }\n}\n\n/**\n * Apply props/attributes to an element (used for first render with keyed children)\n */\nfunction applyPropsToElement(el: Element, props: Props): void {\n for (const [key, value] of Object.entries(props)) {\n if (key === 'children' || key === 'key') continue;\n if (value === undefined || value === null || value === false) continue;\n\n if (key === 'ref') {\n applyRef(el, value);\n continue;\n }\n\n const eventProp = parseEventProp(key);\n if (eventProp) {\n const { eventName, capture } = eventProp;\n const wrappedHandler = createWrappedHandler(value as EventListener, true);\n const options = getEventListenerOptions(eventName, capture);\n const listenerKey = getEventListenerKey(eventName, capture);\n\n if (options !== undefined)\n el.addEventListener(eventName, wrappedHandler, options);\n else el.addEventListener(eventName, wrappedHandler);\n\n if (!elementListeners.has(el)) elementListeners.set(el, new Map());\n elementListeners.get(el)!.set(listenerKey, {\n handler: wrappedHandler,\n original: value as EventListener,\n eventName,\n options,\n });\n continue;\n }\n\n if (key === 'class' || key === 'className') {\n writeElementClassName(el, String(value));\n } else if (key === 'value' || key === 'checked') {\n (el as HTMLElement & Props)[key] = value;\n } else {\n setRenderedAttribute(el, key, String(value));\n }\n }\n}\n\ntype Ref<T> =\n | ((value: T | null) => void)\n | { current: T | null }\n | null\n | undefined;\n\nfunction applyRef<T>(el: T, ref: unknown): void {\n const r = ref as Ref<T>;\n if (!r) return;\n if (typeof r === 'function') {\n r(el);\n return;\n }\n try {\n (r as { current: T | null }).current = el;\n } catch {\n // Ignore write failures\n }\n}\n\n/**\n * Try to handle first render of element with keyed children\n * Returns true if handled, false to fall back to default rendering\n */\nfunction tryFirstRenderKeyedChildren(\n target: Element,\n vnode: DOMElement\n): boolean {\n const children = vnode.children;\n if (!Array.isArray(children) || !hasKeyedChildren(children)) {\n return false;\n }\n\n const el = createElementForNamespace(\n vnode.type as string,\n target.namespaceURI === SVG_NAMESPACE ? SVG_NAMESPACE : undefined\n );\n target.appendChild(el);\n\n applyPropsToElement(el, vnode.props || {});\n\n const newKeyMap = reconcileKeyedChildren(el, children, undefined);\n keyedElements.set(el, newKeyMap);\n return true;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Fragment Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Check if a vnode is a Fragment\n */\nfunction isFragment(vnode: unknown): vnode is DOMElement {\n return (\n _isDOMElement(vnode) &&\n typeof (vnode as DOMElement).type === 'symbol' &&\n ((vnode as DOMElement).type === Fragment ||\n String((vnode as DOMElement).type) === 'Symbol(askr.fragment)')\n );\n}\n\n/**\n * Unwrap Fragment to get children array\n */\nfunction getFragmentChildren(vnode: DOMElement): unknown[] {\n const fragmentChildren = vnode.props?.children ?? vnode.children ?? [];\n return Array.isArray(fragmentChildren)\n ? fragmentChildren\n : [fragmentChildren];\n}\n\nexport function evaluate(\n node: unknown,\n target: Element | null,\n context?: object,\n retainedOwner?: ComponentInstance\n): void {\n if (!target) return;\n // SSR guard: avoid DOM ops when not in a browser-like environment\n if (typeof document === 'undefined') {\n if (getRuntimeEnv().NODE_ENV !== 'production') {\n try {\n // Keep this lightweight and non-throwing so test harnesses and SSR\n // imports don't crash at runtime; callers should avoid calling\n // `evaluate` in SSR, but we make it safe as a no-op.\n console.warn('[Askr] evaluate() called in non-DOM environment; no-op.');\n } catch (e) {\n void e;\n }\n }\n return;\n }\n // Debug tracing to help understand why initial mounts sometimes don't\n // result in DOM mutations during tests.\n\n // If context provided, use component-owned DOM range (only replace that range)\n if (context && domRanges.has(context)) {\n const range = domRanges.get(context)!;\n const normalizedChildren =\n node === null || node === undefined || node === false\n ? []\n : isFragment(node)\n ? getFragmentChildren(node as DOMElement)\n : Array.isArray(node)\n ? node\n : [node];\n\n updateDOMRange(target, range, normalizedChildren);\n } else if (context) {\n // First render with context: create range markers\n const start = document.createComment('component-start');\n const end = document.createComment('component-end');\n target.appendChild(start);\n target.appendChild(end);\n domRanges.set(context, { start, end });\n // Render into the range\n const dom = createDOMNode(node);\n if (dom) {\n target.insertBefore(dom, end);\n }\n } else {\n // Root render (no context): smart update strategy\n // If target has exactly one child of the same element type as the vnode,\n // reuse the element and just update its content.\n // This preserves the element reference and event handlers across renders.\n\n let vnode = node;\n\n // If vnode is a Fragment, unwrap it to get the actual content for the smart update path.\n // Fragments become invisible in the DOM - their children are placed directly in the parent.\n // So for smart updates, we need to compare against the Fragment's children, not the Fragment itself.\n if (isFragment(vnode)) {\n const childArray = getFragmentChildren(vnode as DOMElement);\n // If Fragment has exactly one child that's an element, unwrap to that child\n // This allows the smart update path to match against it\n if (\n childArray.length === 1 &&\n _isDOMElement(childArray[0]) &&\n typeof (childArray[0] as DOMElement).type === 'string'\n ) {\n vnode = childArray[0];\n } else {\n // Fragment with multiple children - process each child with full smart update logic\n processFragmentChildren(target, childArray);\n return;\n }\n }\n\n if (\n _isDOMElement(vnode) &&\n (vnode as DOMElement).type === __FOR_BOUNDARY__\n ) {\n updateForBoundaryChildren(target, vnode as DOMElement);\n return;\n }\n\n if (Array.isArray(vnode)) {\n updateElementChildren(target, vnode);\n return;\n }\n\n // CRITICAL FIX: Check if target itself matches the vnode type AND target is the component's own element\n // This handles inline-rendered components where target IS the component's element.\n // For root components, target is a container (not the component's rendered element), so we skip this path.\n // We detect this by checking if the instance's target property points to this same element.\n const targetWithInstance = target as Element & {\n __ASKR_INSTANCE?: ComponentInstance;\n };\n const targetInstance =\n retainedOwner?.target === target\n ? retainedOwner\n : targetWithInstance.__ASKR_INSTANCE;\n if (targetInstance && targetInstance.target === target) {\n const retainedHostInstances = getRetainedHostOwnerChain(\n targetWithInstance,\n targetInstance\n );\n\n // This is a nested component's own element.\n if (_isDOMElement(vnode) && typeof vnode.type === 'function') {\n const syncedDom = syncComponentElement(\n target,\n vnode as DOMElement,\n vnode.type as ComponentFunction,\n (((vnode as DOMElement).props ?? {}) as Record<string, unknown>) ||\n {},\n undefined,\n false,\n retainedHostInstances\n );\n\n if (syncedDom instanceof Element) {\n retainHostOwnerChain(\n syncedDom,\n targetInstance,\n retainedHostInstances\n );\n targetInstance.target = syncedDom;\n return;\n }\n }\n\n if (\n _isDOMElement(vnode) &&\n typeof vnode.type === 'string' &&\n tagNamesEqualIgnoreCase(target.tagName, vnode.type)\n ) {\n // Tag names match - update in place\n smartUpdateElement(target, vnode as DOMElement);\n return;\n }\n\n // Tag names don't match - need to replace the element\n // Create new element and replace old one in parent\n const newDom = createDOMNode(vnode);\n if (newDom && target.parentNode) {\n // Transfer the component instance to the new element\n if (newDom instanceof Element) {\n (\n newDom as Element & { __ASKR_INSTANCE?: ComponentInstance }\n ).__ASKR_INSTANCE = targetInstance;\n targetInstance.target = newDom as Element;\n }\n // Clean up old element\n removeAllListeners(target);\n target.parentNode.replaceChild(newDom, target);\n return;\n }\n }\n\n const firstChild = target.children[0] as Element | undefined;\n\n if (\n firstChild &&\n _isDOMElement(vnode) &&\n typeof vnode.type === 'string' &&\n tagNamesEqualIgnoreCase(firstChild.tagName, vnode.type)\n ) {\n // Reuse the existing element - it's the same type\n smartUpdateElement(firstChild, vnode as DOMElement);\n } else {\n // Clear and rebuild (first render or structure changed)\n for (let node = target.firstChild; node; ) {\n const next = node.nextSibling;\n cleanupRangeNode(node);\n node = next;\n }\n target.textContent = '';\n\n // Check if this is an element with keyed children even on first render\n if (\n _isDOMElement(vnode) &&\n typeof vnode.type === 'string' &&\n tryFirstRenderKeyedChildren(target, vnode as DOMElement)\n ) {\n return;\n }\n\n // Default: create whole tree\n const dom = createDOMNode(vnode);\n if (dom) {\n target.appendChild(dom);\n }\n }\n }\n}\n\nexport function clearDOMRange(context: object): void {\n domRanges.delete(context);\n}\n"],"mappings":"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"}
@@ -4,6 +4,8 @@ import { teardownNodeSubtree } from "./cleanup.js";
4
4
  import { recordBenchCounter, recordBenchEvent, recordBenchTiming, withBenchMetricScope } from "../runtime/for-bench.js";
5
5
  import { clearForDomUpdateState } from "../runtime/for.js";
6
6
  //#region src/renderer/for-commit.ts
7
+ const DENSE_MOVE_MINIMUM = 64;
8
+ const DENSE_MOVE_RATIO = .75;
7
9
  function getOrBuildDomKeyMap(parent) {
8
10
  let keyMap = keyedElements.get(parent);
9
11
  if (!keyMap) {
@@ -164,6 +166,13 @@ function commitMoveOnlyReorder(parent, nodes) {
164
166
  }
165
167
  const lisIndices = getLISIndices(positions);
166
168
  if (lisIndices.length === nodes.length) return true;
169
+ const moveCount = nodes.length - lisIndices.length;
170
+ if (moveCount >= Math.max(DENSE_MOVE_MINIMUM, Math.floor(nodes.length * DENSE_MOVE_RATIO))) {
171
+ recordBenchEvent("domMove", moveCount);
172
+ recordBenchCounter("replaceChildrenCommits");
173
+ replaceChildrenInOrder(parent, nodes, false);
174
+ return true;
175
+ }
167
176
  let lisCursor = lisIndices.length - 1;
168
177
  let anchor = null;
169
178
  for (let i = nodes.length - 1; i >= 0; i -= 1) {
@@ -1 +1 @@
1
- {"version":3,"file":"for-commit.js","names":[],"sources":["../../src/renderer/for-commit.ts"],"sourcesContent":["import type { ChildScope } from '../runtime/child-scope';\nimport {\n clearForDomUpdateState,\n recordBenchCounter,\n recordBenchEvent,\n recordBenchTiming,\n type ForCommitStrategy,\n type ForState,\n withBenchMetricScope,\n} from '../runtime/for';\nimport { teardownNodeSubtree } from './cleanup';\nimport { keyedElements } from './keyed';\nimport type { VNode } from './types';\nimport { canUseDirectReplaceChildrenSpread } from './utils';\n\nexport interface ForCommitRuntime {\n isProduction(): boolean;\n syncForItemDom(parent: Element, scope: ChildScope, vnode: VNode): Node | null;\n tryPatchStableForDirtyItem(scope: ChildScope): boolean;\n}\n\nfunction getOrBuildDomKeyMap(\n parent: Element\n): Map<string | number, Element> | undefined {\n let keyMap = keyedElements.get(parent);\n if (!keyMap) {\n keyMap = new Map<string | number, Element>();\n for (\n let child = parent.firstElementChild;\n child;\n child = child.nextElementSibling\n ) {\n const key = child.getAttribute('data-key');\n if (key !== null) {\n keyMap.set(key, child);\n const numericKey = Number(key);\n if (!Number.isNaN(numericKey)) {\n keyMap.set(numericKey, child);\n }\n }\n }\n if (keyMap.size > 0) {\n keyedElements.set(parent, keyMap);\n }\n }\n return keyMap.size > 0 ? keyMap : undefined;\n}\n\nfunction hydrateExistingForDomInOrder(\n parent: Element,\n forState: ForState<unknown>\n): boolean {\n if (parent.children.length !== forState.orderedKeys.length) {\n return false;\n }\n\n for (let i = 0; i < forState.orderedKeys.length; i += 1) {\n const itemKey = forState.orderedKeys[i];\n const itemInstance = forState.items.get(itemKey);\n const currentDom = parent.children[i];\n\n if (\n !itemInstance ||\n currentDom.getAttribute('data-key') !== String(itemKey)\n ) {\n return false;\n }\n\n itemInstance.scope.dom = currentDom;\n itemInstance.scope.needsDomUpdate = true;\n }\n\n return true;\n}\n\nfunction removeForBoundaryNodes(parent: Element, removedNodes: Node[]): void {\n if (\n removedNodes.length > 0 &&\n removedNodes.length === parent.childNodes.length\n ) {\n let canBulkClear = true;\n for (let i = 0; i < removedNodes.length; i++) {\n if (removedNodes[i].parentNode !== parent) {\n canBulkClear = false;\n break;\n }\n }\n\n if (canBulkClear) {\n for (let i = 0; i < removedNodes.length; i++) {\n recordBenchEvent('domRemove');\n teardownNodeSubtree(removedNodes[i]);\n }\n withBenchMetricScope('fullClear', () => {\n recordBenchCounter('bulkClearCommits');\n parent.textContent = '';\n });\n return;\n }\n }\n\n for (let i = 0; i < removedNodes.length; i++) {\n const node = removedNodes[i];\n if (node.parentNode === parent) {\n recordBenchEvent('domRemove');\n teardownNodeSubtree(node);\n parent.removeChild(node);\n }\n }\n}\n\nfunction syncKeyedMapFromForState(\n parent: Element,\n forState: ForState<unknown>,\n strategy: ForCommitStrategy,\n removedNodes: Node[]\n): void {\n const existing = keyedElements.get(parent);\n const ensureMapEntry = (\n map: Map<string | number, Element>,\n key: string | number,\n element: Element\n ): void => {\n map.set(key, element);\n const keyString = String(key);\n map.set(keyString, element);\n const keyNumber = Number(keyString);\n if (!Number.isNaN(keyNumber)) {\n map.set(keyNumber, element);\n }\n };\n\n if (strategy === 'SWAP') {\n if (existing) {\n return;\n }\n }\n\n if (strategy === 'FULL_KEYED' && existing && removedNodes.length === 0) {\n return;\n }\n\n if (strategy === 'NO_REORDER') {\n if (existing && removedNodes.length === 0) {\n return;\n }\n\n if (existing) {\n for (const [mapKey, element] of existing) {\n if (element.parentNode !== parent) {\n existing.delete(mapKey);\n }\n }\n\n if (existing.size > 0) {\n keyedElements.set(parent, existing);\n } else {\n keyedElements.delete(parent);\n }\n return;\n }\n }\n\n if (strategy === 'TRUNCATE' && forState.orderedKeys.length === 0) {\n if (existing) {\n existing.clear();\n }\n keyedElements.delete(parent);\n return;\n }\n\n if (strategy === 'APPEND' && existing) {\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const key = forState.orderedKeys[i];\n if (key === null || existing.has(key)) continue;\n const itemInstance = forState.items.get(key);\n if (itemInstance?.scope.dom instanceof Element) {\n ensureMapEntry(existing, key, itemInstance.scope.dom);\n }\n }\n\n if (existing.size > 0) {\n keyedElements.set(parent, existing);\n } else {\n keyedElements.delete(parent);\n }\n return;\n }\n\n const nextMap = existing ?? new Map<string | number, Element>();\n nextMap.clear();\n\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const key = forState.orderedKeys[i];\n if (key === null) continue;\n const itemInstance = forState.items.get(key);\n if (itemInstance?.scope.dom instanceof Element) {\n ensureMapEntry(nextMap, key, itemInstance.scope.dom);\n }\n }\n\n if (nextMap.size > 0) {\n keyedElements.set(parent, nextMap);\n } else {\n keyedElements.delete(parent);\n }\n}\n\nfunction replaceChildrenInOrder(\n parent: Element,\n nodes: Node[],\n allowDirectSpread: boolean\n): void {\n if (allowDirectSpread && canUseDirectReplaceChildrenSpread(nodes.length)) {\n parent.replaceChildren(...nodes);\n return;\n }\n\n const fragment = parent.ownerDocument.createDocumentFragment();\n for (let i = 0; i < nodes.length; i++) {\n fragment.appendChild(nodes[i]);\n }\n parent.replaceChildren(fragment);\n}\n\nfunction getLISIndices(sequence: number[]): number[] {\n if (sequence.length === 0) {\n return [];\n }\n\n const predecessors = sequence.slice();\n const lisIndices: number[] = [0];\n\n for (let i = 1; i < sequence.length; i += 1) {\n const current = sequence[i];\n const lastLisIndex = lisIndices[lisIndices.length - 1];\n\n if (sequence[lastLisIndex] < current) {\n predecessors[i] = lastLisIndex;\n lisIndices.push(i);\n continue;\n }\n\n let lo = 0;\n let hi = lisIndices.length - 1;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (sequence[lisIndices[mid]] < current) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n\n if (current < sequence[lisIndices[lo]]) {\n if (lo > 0) {\n predecessors[i] = lisIndices[lo - 1];\n }\n lisIndices[lo] = i;\n }\n }\n\n let cursor = lisIndices.length - 1;\n let index = lisIndices[cursor];\n while (cursor >= 0) {\n lisIndices[cursor] = index;\n index = predecessors[index];\n cursor -= 1;\n }\n\n return lisIndices;\n}\n\nfunction commitMoveOnlyReorder(parent: Element, nodes: Node[]): boolean {\n const currentNodes = Array.from(parent.childNodes);\n if (currentNodes.length !== nodes.length) {\n return false;\n }\n\n const currentIndexByNode = new Map<Node, number>();\n for (let i = 0; i < currentNodes.length; i += 1) {\n currentIndexByNode.set(currentNodes[i], i);\n }\n\n const positions = Array.from({ length: nodes.length }, () => 0);\n for (let i = 0; i < nodes.length; i += 1) {\n const position = currentIndexByNode.get(nodes[i]);\n if (position === undefined) {\n return false;\n }\n positions[i] = position;\n }\n\n const lisIndices = getLISIndices(positions);\n if (lisIndices.length === nodes.length) {\n return true;\n }\n\n let lisCursor = lisIndices.length - 1;\n let anchor: Node | null = null;\n\n for (let i = nodes.length - 1; i >= 0; i -= 1) {\n const node = nodes[i];\n if (lisCursor >= 0 && i === lisIndices[lisCursor]) {\n anchor = node;\n lisCursor -= 1;\n continue;\n }\n\n if (node.nextSibling !== anchor) {\n recordBenchEvent('domMove');\n parent.insertBefore(node, anchor);\n }\n\n anchor = node;\n }\n\n return true;\n}\n\nexport function commitForStateBoundaryChildren(\n parent: Element,\n forState: ForState<unknown>,\n childrenVNodes: VNode[],\n runtime: ForCommitRuntime\n): void {\n const domCommitStart = performance.now();\n\n const hydrateExistingForDom = (): void => {\n if (hydrateExistingForDomInOrder(parent, forState)) {\n return;\n }\n\n const domKeyMap = getOrBuildDomKeyMap(parent);\n if (!domKeyMap) {\n return;\n }\n\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const itemKey = forState.orderedKeys[i];\n const itemInstance = forState.items.get(itemKey);\n if (!itemInstance || itemInstance.scope.dom) {\n continue;\n }\n\n const existingDom = domKeyMap.get(itemKey);\n if (!existingDom) {\n continue;\n }\n\n itemInstance.scope.dom = existingDom;\n itemInstance.scope.needsDomUpdate = true;\n }\n };\n\n if (forState.orderedKeys.length === 0) {\n removeForBoundaryNodes(parent, forState.lastRemovedNodes);\n\n const fallbackScope = forState.fallbackScope;\n const fallbackVNode = childrenVNodes[0];\n const nextDom =\n fallbackScope && fallbackVNode !== undefined\n ? runtime.syncForItemDom(parent, fallbackScope, fallbackVNode)\n : null;\n\n if (nextDom) {\n if (\n parent.childNodes.length !== 1 ||\n parent.firstChild !== nextDom ||\n forState.lastRemovedNodes.length > 0\n ) {\n parent.replaceChildren(nextDom);\n }\n } else if (parent.firstChild) {\n parent.textContent = '';\n }\n\n keyedElements.delete(parent);\n forState._hasResolvedItemDom = false;\n recordBenchTiming('domCommit', performance.now() - domCommitStart);\n clearForDomUpdateState(forState);\n return;\n }\n\n if (!forState._hasResolvedItemDom && parent.childNodes.length > 0) {\n hydrateExistingForDom();\n }\n\n const getDirtyForIndices = (): number[] => {\n const pendingDirtyIndices = forState.pendingDirtyIndices;\n if (pendingDirtyIndices && pendingDirtyIndices.length > 0) {\n return pendingDirtyIndices;\n }\n\n const dirtyIndices: number[] = [];\n for (let index = 0; index < forState.orderedKeys.length; index += 1) {\n const itemInstance = forState.orderedItems[index];\n if (itemInstance?.scope.needsDomUpdate) {\n dirtyIndices.push(index);\n }\n }\n\n return dirtyIndices;\n };\n\n let dirtyIndicesCache: number[] | null = null;\n const ensureDirtyIndices = (): number[] => {\n if (dirtyIndicesCache) {\n return dirtyIndicesCache;\n }\n\n dirtyIndicesCache = getDirtyForIndices();\n return dirtyIndicesCache;\n };\n\n const dirtyIndices =\n forState.lastCommitStrategy === 'NO_REORDER' ? ensureDirtyIndices() : [];\n let boundaryChildrenExact = false;\n\n const commitDirtyNoReorder = (dirtyIndices: number[]): void => {\n if (dirtyIndices.length === 0) {\n boundaryChildrenExact = true;\n return;\n }\n\n const orderedItems = forState.orderedItems;\n const childNodes = parent.childNodes;\n\n for (let dirtyIndex = 0; dirtyIndex < dirtyIndices.length; dirtyIndex++) {\n const i = dirtyIndices[dirtyIndex];\n const itemInstance = orderedItems[i];\n if (!itemInstance) {\n continue;\n }\n\n if (runtime.tryPatchStableForDirtyItem(itemInstance.scope)) {\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom) {\n continue;\n }\n\n const anchor = childNodes[i] ?? null;\n if (dom.parentNode !== parent || dom !== anchor) {\n recordBenchEvent('domInsert');\n parent.insertBefore(dom, anchor);\n }\n }\n\n boundaryChildrenExact = true;\n };\n\n const commitPositional = (): void => {\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const itemInstance = forState.orderedItems[i];\n if (!itemInstance) {\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom) {\n continue;\n }\n\n if (dom.parentNode !== parent) {\n const anchor = parent.childNodes[i] ?? null;\n recordBenchEvent('domInsert');\n parent.insertBefore(dom, anchor);\n }\n }\n\n boundaryChildrenExact = true;\n };\n\n const commitAppend = (): void => {\n const canHydrateInPlace =\n !forState._hasResolvedItemDom &&\n forState.lastRemovedNodes.length === 0 &&\n parent.childNodes.length === forState.orderedKeys.length;\n if (canHydrateInPlace) {\n let exactOrder = true;\n let currentNode = parent.firstChild;\n\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const itemInstance = forState.orderedItems[i];\n if (!itemInstance) {\n exactOrder = false;\n currentNode = currentNode?.nextSibling ?? null;\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom || dom.parentNode !== parent || dom !== currentNode) {\n exactOrder = false;\n }\n\n currentNode = currentNode?.nextSibling ?? null;\n }\n\n if (exactOrder) {\n boundaryChildrenExact = true;\n return;\n }\n }\n\n withBenchMetricScope('coldCreate', () => {\n const fragment = parent.ownerDocument.createDocumentFragment();\n let hasPendingAppend = false;\n\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const itemInstance = forState.orderedItems[i];\n if (!itemInstance) {\n continue;\n }\n\n if (\n itemInstance.scope.dom?.parentNode === parent &&\n !itemInstance.scope.needsDomUpdate\n ) {\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom) {\n continue;\n }\n\n if (dom.parentNode !== parent) {\n recordBenchEvent('domInsert');\n fragment.appendChild(dom);\n hasPendingAppend = true;\n }\n }\n\n if (hasPendingAppend) {\n parent.appendChild(fragment);\n }\n });\n\n boundaryChildrenExact = true;\n };\n\n const commitSwap = (): void => {\n const swapIndices = forState.pendingSwapIndices;\n if (!swapIndices) {\n return;\n }\n\n let [firstIndex, secondIndex] = swapIndices;\n if (firstIndex === secondIndex) {\n return;\n }\n\n if (firstIndex > secondIndex) {\n [firstIndex, secondIndex] = [secondIndex, firstIndex];\n }\n\n const firstKey = forState.orderedKeys[firstIndex];\n const secondKey = forState.orderedKeys[secondIndex];\n const firstItem = forState.items.get(firstKey);\n const secondItem = forState.items.get(secondKey);\n\n if (!firstItem || !secondItem) {\n commitReorder();\n return;\n }\n\n const firstDom = runtime.syncForItemDom(\n parent,\n firstItem.scope,\n childrenVNodes[firstIndex]\n );\n const secondDom = runtime.syncForItemDom(\n parent,\n secondItem.scope,\n childrenVNodes[secondIndex]\n );\n\n if (!firstDom || !secondDom) {\n commitReorder();\n return;\n }\n\n if (firstDom.parentNode !== parent || secondDom.parentNode !== parent) {\n commitReorder();\n return;\n }\n\n const firstBeforeSecond =\n (firstDom.compareDocumentPosition(secondDom) &\n Node.DOCUMENT_POSITION_FOLLOWING) !==\n 0;\n if (firstBeforeSecond) {\n boundaryChildrenExact = true;\n return;\n }\n\n const firstNextSibling = firstDom.nextSibling;\n recordBenchEvent('domMove');\n parent.insertBefore(firstDom, secondDom);\n recordBenchEvent('domMove');\n parent.insertBefore(secondDom, firstNextSibling);\n\n boundaryChildrenExact = true;\n };\n\n const commitReorder = (): void => {\n const items = forState.orderedItems;\n const count = items.length;\n\n if (forState.pendingMoveOnly && forState.lastRemovedNodes.length === 0) {\n const nodes = Array<Node>(count);\n let movedCount = 0;\n let insertedCount = 0;\n\n for (let i = 0; i < count; i++) {\n const itemInstance = items[i];\n if (!itemInstance) {\n return;\n }\n\n const scope = itemInstance.scope;\n const dom =\n scope.dom && !scope.needsDomUpdate\n ? scope.dom\n : runtime.syncForItemDom(parent, scope, childrenVNodes[i]);\n\n if (!dom) {\n return;\n }\n\n if (dom.parentNode === parent) {\n movedCount++;\n } else {\n insertedCount++;\n }\n nodes[i] = dom;\n }\n\n if (!canUseDirectReplaceChildrenSpread(count)) {\n if (movedCount > 0) {\n recordBenchEvent('domMove', movedCount);\n }\n if (insertedCount > 0) {\n recordBenchEvent('domInsert', insertedCount);\n }\n replaceChildrenInOrder(parent, nodes, false);\n boundaryChildrenExact = true;\n return;\n }\n\n if (insertedCount > 0) {\n if (movedCount > 0) {\n recordBenchEvent('domMove', movedCount);\n }\n recordBenchEvent('domInsert', insertedCount);\n replaceChildrenInOrder(parent, nodes, true);\n boundaryChildrenExact = true;\n return;\n }\n\n if (count > 1 && commitMoveOnlyReorder(parent, nodes)) {\n boundaryChildrenExact = true;\n return;\n }\n\n if (movedCount > 0) {\n recordBenchEvent('domMove', movedCount);\n }\n if (insertedCount > 0) {\n recordBenchEvent('domInsert', insertedCount);\n }\n\n replaceChildrenInOrder(parent, nodes, true);\n boundaryChildrenExact = true;\n return;\n }\n\n let hasExistingChild = false;\n for (let i = 0; i < count; i++) {\n const itemInstance = items[i];\n if (itemInstance?.scope.dom?.parentNode === parent) {\n hasExistingChild = true;\n break;\n }\n }\n\n if (!hasExistingChild) {\n withBenchMetricScope('coldCreate', () => {\n const nodes: Node[] = [];\n for (let i = 0; i < count; i++) {\n const itemInstance = items[i];\n if (!itemInstance) continue;\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (dom) {\n recordBenchEvent('domInsert');\n nodes.push(dom);\n }\n }\n recordBenchCounter('replaceChildrenCommits');\n replaceChildrenInOrder(parent, nodes, false);\n });\n\n boundaryChildrenExact = true;\n return;\n }\n\n if (forState.lastRemovedNodes.length === 0) {\n const nodes: Node[] = [];\n\n for (let i = 0; i < count; i++) {\n const itemInstance = items[i];\n if (!itemInstance) {\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom) {\n continue;\n }\n\n recordBenchEvent(dom.parentNode === parent ? 'domMove' : 'domInsert');\n nodes.push(dom);\n }\n\n replaceChildrenInOrder(parent, nodes, false);\n boundaryChildrenExact = true;\n return;\n }\n\n for (let i = 0; i < count; i++) {\n const itemInstance = items[i];\n if (!itemInstance) {\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom) {\n continue;\n }\n\n const anchor = parent.childNodes[i] ?? null;\n if (dom !== anchor) {\n recordBenchEvent('domMove');\n parent.insertBefore(dom, anchor);\n }\n }\n\n boundaryChildrenExact = true;\n };\n\n const isLocalOnlyDirtyCommit =\n forState.lastCommitStrategy === 'NO_REORDER' &&\n dirtyIndices.length > 0 &&\n forState.pendingDirtyIndices === null &&\n forState.pendingSwapIndices === null &&\n !forState.pendingMoveOnly &&\n forState.lastRemovedNodes.length === 0 &&\n parent.childNodes.length === forState.orderedKeys.length;\n\n if (isLocalOnlyDirtyCommit) {\n commitDirtyNoReorder(dirtyIndices);\n syncKeyedMapFromForState(parent, forState, 'NO_REORDER', []);\n\n recordBenchTiming('domCommit', performance.now() - domCommitStart);\n clearForDomUpdateState(forState);\n return;\n }\n\n switch (forState.lastCommitStrategy) {\n case 'NO_REORDER':\n commitDirtyNoReorder(dirtyIndices);\n break;\n case 'TRUNCATE':\n commitPositional();\n break;\n case 'APPEND':\n commitAppend();\n break;\n case 'SWAP':\n commitSwap();\n break;\n case 'FULL_KEYED':\n default:\n commitReorder();\n break;\n }\n\n removeForBoundaryNodes(parent, forState.lastRemovedNodes);\n if (forState.lastRemovedNodes.length > 0) {\n boundaryChildrenExact = false;\n }\n syncKeyedMapFromForState(\n parent,\n forState,\n forState.lastCommitStrategy,\n forState.lastRemovedNodes\n );\n\n const syncExactForBoundaryChildren = (): void => {\n const expectedNodes: Node[] = [];\n\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const itemInstance = forState.orderedItems[i];\n if (!itemInstance) {\n continue;\n }\n\n const dom =\n itemInstance.scope.dom && !itemInstance.scope.needsDomUpdate\n ? itemInstance.scope.dom\n : runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (dom) {\n expectedNodes.push(dom);\n }\n }\n\n const currentNodes = Array.from(parent.childNodes);\n if (\n currentNodes.length === expectedNodes.length &&\n currentNodes.every((node, index) => node === expectedNodes[index])\n ) {\n return;\n }\n\n const expectedNodeSet = new Set(expectedNodes);\n for (const currentNode of currentNodes) {\n if (expectedNodeSet.has(currentNode)) {\n continue;\n }\n\n teardownNodeSubtree(currentNode);\n\n if (currentNode.parentNode === parent) {\n recordBenchEvent('domRemove');\n currentNode.remove();\n }\n }\n\n replaceChildrenInOrder(parent, expectedNodes, true);\n };\n\n if (!boundaryChildrenExact) {\n syncExactForBoundaryChildren();\n }\n forState._hasResolvedItemDom = true;\n recordBenchTiming('domCommit', performance.now() - domCommitStart);\n clearForDomUpdateState(forState);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"for-commit.js","names":[],"sources":["../../src/renderer/for-commit.ts"],"sourcesContent":["import type { ChildScope } from '../runtime/child-scope';\nimport {\n clearForDomUpdateState,\n recordBenchCounter,\n recordBenchEvent,\n recordBenchTiming,\n type ForCommitStrategy,\n type ForState,\n withBenchMetricScope,\n} from '../runtime/for';\nimport { teardownNodeSubtree } from './cleanup';\nimport { keyedElements } from './keyed';\nimport type { VNode } from './types';\nimport { canUseDirectReplaceChildrenSpread } from './utils';\n\nconst DENSE_MOVE_MINIMUM = 64;\nconst DENSE_MOVE_RATIO = 0.75;\n\nexport interface ForCommitRuntime {\n isProduction(): boolean;\n syncForItemDom(parent: Element, scope: ChildScope, vnode: VNode): Node | null;\n tryPatchStableForDirtyItem(scope: ChildScope): boolean;\n}\n\nfunction getOrBuildDomKeyMap(\n parent: Element\n): Map<string | number, Element> | undefined {\n let keyMap = keyedElements.get(parent);\n if (!keyMap) {\n keyMap = new Map<string | number, Element>();\n for (\n let child = parent.firstElementChild;\n child;\n child = child.nextElementSibling\n ) {\n const key = child.getAttribute('data-key');\n if (key !== null) {\n keyMap.set(key, child);\n const numericKey = Number(key);\n if (!Number.isNaN(numericKey)) {\n keyMap.set(numericKey, child);\n }\n }\n }\n if (keyMap.size > 0) {\n keyedElements.set(parent, keyMap);\n }\n }\n return keyMap.size > 0 ? keyMap : undefined;\n}\n\nfunction hydrateExistingForDomInOrder(\n parent: Element,\n forState: ForState<unknown>\n): boolean {\n if (parent.children.length !== forState.orderedKeys.length) {\n return false;\n }\n\n for (let i = 0; i < forState.orderedKeys.length; i += 1) {\n const itemKey = forState.orderedKeys[i];\n const itemInstance = forState.items.get(itemKey);\n const currentDom = parent.children[i];\n\n if (\n !itemInstance ||\n currentDom.getAttribute('data-key') !== String(itemKey)\n ) {\n return false;\n }\n\n itemInstance.scope.dom = currentDom;\n itemInstance.scope.needsDomUpdate = true;\n }\n\n return true;\n}\n\nfunction removeForBoundaryNodes(parent: Element, removedNodes: Node[]): void {\n if (\n removedNodes.length > 0 &&\n removedNodes.length === parent.childNodes.length\n ) {\n let canBulkClear = true;\n for (let i = 0; i < removedNodes.length; i++) {\n if (removedNodes[i].parentNode !== parent) {\n canBulkClear = false;\n break;\n }\n }\n\n if (canBulkClear) {\n for (let i = 0; i < removedNodes.length; i++) {\n recordBenchEvent('domRemove');\n teardownNodeSubtree(removedNodes[i]);\n }\n withBenchMetricScope('fullClear', () => {\n recordBenchCounter('bulkClearCommits');\n parent.textContent = '';\n });\n return;\n }\n }\n\n for (let i = 0; i < removedNodes.length; i++) {\n const node = removedNodes[i];\n if (node.parentNode === parent) {\n recordBenchEvent('domRemove');\n teardownNodeSubtree(node);\n parent.removeChild(node);\n }\n }\n}\n\nfunction syncKeyedMapFromForState(\n parent: Element,\n forState: ForState<unknown>,\n strategy: ForCommitStrategy,\n removedNodes: Node[]\n): void {\n const existing = keyedElements.get(parent);\n const ensureMapEntry = (\n map: Map<string | number, Element>,\n key: string | number,\n element: Element\n ): void => {\n map.set(key, element);\n const keyString = String(key);\n map.set(keyString, element);\n const keyNumber = Number(keyString);\n if (!Number.isNaN(keyNumber)) {\n map.set(keyNumber, element);\n }\n };\n\n if (strategy === 'SWAP') {\n if (existing) {\n return;\n }\n }\n\n if (strategy === 'FULL_KEYED' && existing && removedNodes.length === 0) {\n return;\n }\n\n if (strategy === 'NO_REORDER') {\n if (existing && removedNodes.length === 0) {\n return;\n }\n\n if (existing) {\n for (const [mapKey, element] of existing) {\n if (element.parentNode !== parent) {\n existing.delete(mapKey);\n }\n }\n\n if (existing.size > 0) {\n keyedElements.set(parent, existing);\n } else {\n keyedElements.delete(parent);\n }\n return;\n }\n }\n\n if (strategy === 'TRUNCATE' && forState.orderedKeys.length === 0) {\n if (existing) {\n existing.clear();\n }\n keyedElements.delete(parent);\n return;\n }\n\n if (strategy === 'APPEND' && existing) {\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const key = forState.orderedKeys[i];\n if (key === null || existing.has(key)) continue;\n const itemInstance = forState.items.get(key);\n if (itemInstance?.scope.dom instanceof Element) {\n ensureMapEntry(existing, key, itemInstance.scope.dom);\n }\n }\n\n if (existing.size > 0) {\n keyedElements.set(parent, existing);\n } else {\n keyedElements.delete(parent);\n }\n return;\n }\n\n const nextMap = existing ?? new Map<string | number, Element>();\n nextMap.clear();\n\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const key = forState.orderedKeys[i];\n if (key === null) continue;\n const itemInstance = forState.items.get(key);\n if (itemInstance?.scope.dom instanceof Element) {\n ensureMapEntry(nextMap, key, itemInstance.scope.dom);\n }\n }\n\n if (nextMap.size > 0) {\n keyedElements.set(parent, nextMap);\n } else {\n keyedElements.delete(parent);\n }\n}\n\nfunction replaceChildrenInOrder(\n parent: Element,\n nodes: Node[],\n allowDirectSpread: boolean\n): void {\n if (allowDirectSpread && canUseDirectReplaceChildrenSpread(nodes.length)) {\n parent.replaceChildren(...nodes);\n return;\n }\n\n const fragment = parent.ownerDocument.createDocumentFragment();\n for (let i = 0; i < nodes.length; i++) {\n fragment.appendChild(nodes[i]);\n }\n parent.replaceChildren(fragment);\n}\n\nfunction getLISIndices(sequence: number[]): number[] {\n if (sequence.length === 0) {\n return [];\n }\n\n const predecessors = sequence.slice();\n const lisIndices: number[] = [0];\n\n for (let i = 1; i < sequence.length; i += 1) {\n const current = sequence[i];\n const lastLisIndex = lisIndices[lisIndices.length - 1];\n\n if (sequence[lastLisIndex] < current) {\n predecessors[i] = lastLisIndex;\n lisIndices.push(i);\n continue;\n }\n\n let lo = 0;\n let hi = lisIndices.length - 1;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (sequence[lisIndices[mid]] < current) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n\n if (current < sequence[lisIndices[lo]]) {\n if (lo > 0) {\n predecessors[i] = lisIndices[lo - 1];\n }\n lisIndices[lo] = i;\n }\n }\n\n let cursor = lisIndices.length - 1;\n let index = lisIndices[cursor];\n while (cursor >= 0) {\n lisIndices[cursor] = index;\n index = predecessors[index];\n cursor -= 1;\n }\n\n return lisIndices;\n}\n\nfunction commitMoveOnlyReorder(parent: Element, nodes: Node[]): boolean {\n const currentNodes = Array.from(parent.childNodes);\n if (currentNodes.length !== nodes.length) {\n return false;\n }\n\n const currentIndexByNode = new Map<Node, number>();\n for (let i = 0; i < currentNodes.length; i += 1) {\n currentIndexByNode.set(currentNodes[i], i);\n }\n\n const positions = Array.from({ length: nodes.length }, () => 0);\n for (let i = 0; i < nodes.length; i += 1) {\n const position = currentIndexByNode.get(nodes[i]);\n if (position === undefined) {\n return false;\n }\n positions[i] = position;\n }\n\n const lisIndices = getLISIndices(positions);\n if (lisIndices.length === nodes.length) {\n return true;\n }\n\n const moveCount = nodes.length - lisIndices.length;\n const denseMoveThreshold = Math.max(\n DENSE_MOVE_MINIMUM,\n Math.floor(nodes.length * DENSE_MOVE_RATIO)\n );\n\n if (moveCount >= denseMoveThreshold) {\n recordBenchEvent('domMove', moveCount);\n recordBenchCounter('replaceChildrenCommits');\n replaceChildrenInOrder(parent, nodes, false);\n return true;\n }\n\n let lisCursor = lisIndices.length - 1;\n let anchor: Node | null = null;\n\n for (let i = nodes.length - 1; i >= 0; i -= 1) {\n const node = nodes[i];\n if (lisCursor >= 0 && i === lisIndices[lisCursor]) {\n anchor = node;\n lisCursor -= 1;\n continue;\n }\n\n if (node.nextSibling !== anchor) {\n recordBenchEvent('domMove');\n parent.insertBefore(node, anchor);\n }\n\n anchor = node;\n }\n\n return true;\n}\n\nexport function commitForStateBoundaryChildren(\n parent: Element,\n forState: ForState<unknown>,\n childrenVNodes: VNode[],\n runtime: ForCommitRuntime\n): void {\n const domCommitStart = performance.now();\n\n const hydrateExistingForDom = (): void => {\n if (hydrateExistingForDomInOrder(parent, forState)) {\n return;\n }\n\n const domKeyMap = getOrBuildDomKeyMap(parent);\n if (!domKeyMap) {\n return;\n }\n\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const itemKey = forState.orderedKeys[i];\n const itemInstance = forState.items.get(itemKey);\n if (!itemInstance || itemInstance.scope.dom) {\n continue;\n }\n\n const existingDom = domKeyMap.get(itemKey);\n if (!existingDom) {\n continue;\n }\n\n itemInstance.scope.dom = existingDom;\n itemInstance.scope.needsDomUpdate = true;\n }\n };\n\n if (forState.orderedKeys.length === 0) {\n removeForBoundaryNodes(parent, forState.lastRemovedNodes);\n\n const fallbackScope = forState.fallbackScope;\n const fallbackVNode = childrenVNodes[0];\n const nextDom =\n fallbackScope && fallbackVNode !== undefined\n ? runtime.syncForItemDom(parent, fallbackScope, fallbackVNode)\n : null;\n\n if (nextDom) {\n if (\n parent.childNodes.length !== 1 ||\n parent.firstChild !== nextDom ||\n forState.lastRemovedNodes.length > 0\n ) {\n parent.replaceChildren(nextDom);\n }\n } else if (parent.firstChild) {\n parent.textContent = '';\n }\n\n keyedElements.delete(parent);\n forState._hasResolvedItemDom = false;\n recordBenchTiming('domCommit', performance.now() - domCommitStart);\n clearForDomUpdateState(forState);\n return;\n }\n\n if (!forState._hasResolvedItemDom && parent.childNodes.length > 0) {\n hydrateExistingForDom();\n }\n\n const getDirtyForIndices = (): number[] => {\n const pendingDirtyIndices = forState.pendingDirtyIndices;\n if (pendingDirtyIndices && pendingDirtyIndices.length > 0) {\n return pendingDirtyIndices;\n }\n\n const dirtyIndices: number[] = [];\n for (let index = 0; index < forState.orderedKeys.length; index += 1) {\n const itemInstance = forState.orderedItems[index];\n if (itemInstance?.scope.needsDomUpdate) {\n dirtyIndices.push(index);\n }\n }\n\n return dirtyIndices;\n };\n\n let dirtyIndicesCache: number[] | null = null;\n const ensureDirtyIndices = (): number[] => {\n if (dirtyIndicesCache) {\n return dirtyIndicesCache;\n }\n\n dirtyIndicesCache = getDirtyForIndices();\n return dirtyIndicesCache;\n };\n\n const dirtyIndices =\n forState.lastCommitStrategy === 'NO_REORDER' ? ensureDirtyIndices() : [];\n let boundaryChildrenExact = false;\n\n const commitDirtyNoReorder = (dirtyIndices: number[]): void => {\n if (dirtyIndices.length === 0) {\n boundaryChildrenExact = true;\n return;\n }\n\n const orderedItems = forState.orderedItems;\n const childNodes = parent.childNodes;\n\n for (let dirtyIndex = 0; dirtyIndex < dirtyIndices.length; dirtyIndex++) {\n const i = dirtyIndices[dirtyIndex];\n const itemInstance = orderedItems[i];\n if (!itemInstance) {\n continue;\n }\n\n if (runtime.tryPatchStableForDirtyItem(itemInstance.scope)) {\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom) {\n continue;\n }\n\n const anchor = childNodes[i] ?? null;\n if (dom.parentNode !== parent || dom !== anchor) {\n recordBenchEvent('domInsert');\n parent.insertBefore(dom, anchor);\n }\n }\n\n boundaryChildrenExact = true;\n };\n\n const commitPositional = (): void => {\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const itemInstance = forState.orderedItems[i];\n if (!itemInstance) {\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom) {\n continue;\n }\n\n if (dom.parentNode !== parent) {\n const anchor = parent.childNodes[i] ?? null;\n recordBenchEvent('domInsert');\n parent.insertBefore(dom, anchor);\n }\n }\n\n boundaryChildrenExact = true;\n };\n\n const commitAppend = (): void => {\n const canHydrateInPlace =\n !forState._hasResolvedItemDom &&\n forState.lastRemovedNodes.length === 0 &&\n parent.childNodes.length === forState.orderedKeys.length;\n if (canHydrateInPlace) {\n let exactOrder = true;\n let currentNode = parent.firstChild;\n\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const itemInstance = forState.orderedItems[i];\n if (!itemInstance) {\n exactOrder = false;\n currentNode = currentNode?.nextSibling ?? null;\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom || dom.parentNode !== parent || dom !== currentNode) {\n exactOrder = false;\n }\n\n currentNode = currentNode?.nextSibling ?? null;\n }\n\n if (exactOrder) {\n boundaryChildrenExact = true;\n return;\n }\n }\n\n withBenchMetricScope('coldCreate', () => {\n const fragment = parent.ownerDocument.createDocumentFragment();\n let hasPendingAppend = false;\n\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const itemInstance = forState.orderedItems[i];\n if (!itemInstance) {\n continue;\n }\n\n if (\n itemInstance.scope.dom?.parentNode === parent &&\n !itemInstance.scope.needsDomUpdate\n ) {\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom) {\n continue;\n }\n\n if (dom.parentNode !== parent) {\n recordBenchEvent('domInsert');\n fragment.appendChild(dom);\n hasPendingAppend = true;\n }\n }\n\n if (hasPendingAppend) {\n parent.appendChild(fragment);\n }\n });\n\n boundaryChildrenExact = true;\n };\n\n const commitSwap = (): void => {\n const swapIndices = forState.pendingSwapIndices;\n if (!swapIndices) {\n return;\n }\n\n let [firstIndex, secondIndex] = swapIndices;\n if (firstIndex === secondIndex) {\n return;\n }\n\n if (firstIndex > secondIndex) {\n [firstIndex, secondIndex] = [secondIndex, firstIndex];\n }\n\n const firstKey = forState.orderedKeys[firstIndex];\n const secondKey = forState.orderedKeys[secondIndex];\n const firstItem = forState.items.get(firstKey);\n const secondItem = forState.items.get(secondKey);\n\n if (!firstItem || !secondItem) {\n commitReorder();\n return;\n }\n\n const firstDom = runtime.syncForItemDom(\n parent,\n firstItem.scope,\n childrenVNodes[firstIndex]\n );\n const secondDom = runtime.syncForItemDom(\n parent,\n secondItem.scope,\n childrenVNodes[secondIndex]\n );\n\n if (!firstDom || !secondDom) {\n commitReorder();\n return;\n }\n\n if (firstDom.parentNode !== parent || secondDom.parentNode !== parent) {\n commitReorder();\n return;\n }\n\n const firstBeforeSecond =\n (firstDom.compareDocumentPosition(secondDom) &\n Node.DOCUMENT_POSITION_FOLLOWING) !==\n 0;\n if (firstBeforeSecond) {\n boundaryChildrenExact = true;\n return;\n }\n\n const firstNextSibling = firstDom.nextSibling;\n recordBenchEvent('domMove');\n parent.insertBefore(firstDom, secondDom);\n recordBenchEvent('domMove');\n parent.insertBefore(secondDom, firstNextSibling);\n\n boundaryChildrenExact = true;\n };\n\n const commitReorder = (): void => {\n const items = forState.orderedItems;\n const count = items.length;\n\n if (forState.pendingMoveOnly && forState.lastRemovedNodes.length === 0) {\n const nodes = Array<Node>(count);\n let movedCount = 0;\n let insertedCount = 0;\n\n for (let i = 0; i < count; i++) {\n const itemInstance = items[i];\n if (!itemInstance) {\n return;\n }\n\n const scope = itemInstance.scope;\n const dom =\n scope.dom && !scope.needsDomUpdate\n ? scope.dom\n : runtime.syncForItemDom(parent, scope, childrenVNodes[i]);\n\n if (!dom) {\n return;\n }\n\n if (dom.parentNode === parent) {\n movedCount++;\n } else {\n insertedCount++;\n }\n nodes[i] = dom;\n }\n\n if (!canUseDirectReplaceChildrenSpread(count)) {\n if (movedCount > 0) {\n recordBenchEvent('domMove', movedCount);\n }\n if (insertedCount > 0) {\n recordBenchEvent('domInsert', insertedCount);\n }\n replaceChildrenInOrder(parent, nodes, false);\n boundaryChildrenExact = true;\n return;\n }\n\n if (insertedCount > 0) {\n if (movedCount > 0) {\n recordBenchEvent('domMove', movedCount);\n }\n recordBenchEvent('domInsert', insertedCount);\n replaceChildrenInOrder(parent, nodes, true);\n boundaryChildrenExact = true;\n return;\n }\n\n if (count > 1 && commitMoveOnlyReorder(parent, nodes)) {\n boundaryChildrenExact = true;\n return;\n }\n\n if (movedCount > 0) {\n recordBenchEvent('domMove', movedCount);\n }\n if (insertedCount > 0) {\n recordBenchEvent('domInsert', insertedCount);\n }\n\n replaceChildrenInOrder(parent, nodes, true);\n boundaryChildrenExact = true;\n return;\n }\n\n let hasExistingChild = false;\n for (let i = 0; i < count; i++) {\n const itemInstance = items[i];\n if (itemInstance?.scope.dom?.parentNode === parent) {\n hasExistingChild = true;\n break;\n }\n }\n\n if (!hasExistingChild) {\n withBenchMetricScope('coldCreate', () => {\n const nodes: Node[] = [];\n for (let i = 0; i < count; i++) {\n const itemInstance = items[i];\n if (!itemInstance) continue;\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (dom) {\n recordBenchEvent('domInsert');\n nodes.push(dom);\n }\n }\n recordBenchCounter('replaceChildrenCommits');\n replaceChildrenInOrder(parent, nodes, false);\n });\n\n boundaryChildrenExact = true;\n return;\n }\n\n if (forState.lastRemovedNodes.length === 0) {\n const nodes: Node[] = [];\n\n for (let i = 0; i < count; i++) {\n const itemInstance = items[i];\n if (!itemInstance) {\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom) {\n continue;\n }\n\n recordBenchEvent(dom.parentNode === parent ? 'domMove' : 'domInsert');\n nodes.push(dom);\n }\n\n replaceChildrenInOrder(parent, nodes, false);\n boundaryChildrenExact = true;\n return;\n }\n\n for (let i = 0; i < count; i++) {\n const itemInstance = items[i];\n if (!itemInstance) {\n continue;\n }\n\n const dom = runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (!dom) {\n continue;\n }\n\n const anchor = parent.childNodes[i] ?? null;\n if (dom !== anchor) {\n recordBenchEvent('domMove');\n parent.insertBefore(dom, anchor);\n }\n }\n\n boundaryChildrenExact = true;\n };\n\n const isLocalOnlyDirtyCommit =\n forState.lastCommitStrategy === 'NO_REORDER' &&\n dirtyIndices.length > 0 &&\n forState.pendingDirtyIndices === null &&\n forState.pendingSwapIndices === null &&\n !forState.pendingMoveOnly &&\n forState.lastRemovedNodes.length === 0 &&\n parent.childNodes.length === forState.orderedKeys.length;\n\n if (isLocalOnlyDirtyCommit) {\n commitDirtyNoReorder(dirtyIndices);\n syncKeyedMapFromForState(parent, forState, 'NO_REORDER', []);\n\n recordBenchTiming('domCommit', performance.now() - domCommitStart);\n clearForDomUpdateState(forState);\n return;\n }\n\n switch (forState.lastCommitStrategy) {\n case 'NO_REORDER':\n commitDirtyNoReorder(dirtyIndices);\n break;\n case 'TRUNCATE':\n commitPositional();\n break;\n case 'APPEND':\n commitAppend();\n break;\n case 'SWAP':\n commitSwap();\n break;\n case 'FULL_KEYED':\n default:\n commitReorder();\n break;\n }\n\n removeForBoundaryNodes(parent, forState.lastRemovedNodes);\n if (forState.lastRemovedNodes.length > 0) {\n boundaryChildrenExact = false;\n }\n syncKeyedMapFromForState(\n parent,\n forState,\n forState.lastCommitStrategy,\n forState.lastRemovedNodes\n );\n\n const syncExactForBoundaryChildren = (): void => {\n const expectedNodes: Node[] = [];\n\n for (let i = 0; i < forState.orderedKeys.length; i++) {\n const itemInstance = forState.orderedItems[i];\n if (!itemInstance) {\n continue;\n }\n\n const dom =\n itemInstance.scope.dom && !itemInstance.scope.needsDomUpdate\n ? itemInstance.scope.dom\n : runtime.syncForItemDom(\n parent,\n itemInstance.scope,\n childrenVNodes[i]\n );\n if (dom) {\n expectedNodes.push(dom);\n }\n }\n\n const currentNodes = Array.from(parent.childNodes);\n if (\n currentNodes.length === expectedNodes.length &&\n currentNodes.every((node, index) => node === expectedNodes[index])\n ) {\n return;\n }\n\n const expectedNodeSet = new Set(expectedNodes);\n for (const currentNode of currentNodes) {\n if (expectedNodeSet.has(currentNode)) {\n continue;\n }\n\n teardownNodeSubtree(currentNode);\n\n if (currentNode.parentNode === parent) {\n recordBenchEvent('domRemove');\n currentNode.remove();\n }\n }\n\n replaceChildrenInOrder(parent, expectedNodes, true);\n };\n\n if (!boundaryChildrenExact) {\n syncExactForBoundaryChildren();\n }\n forState._hasResolvedItemDom = true;\n recordBenchTiming('domCommit', performance.now() - domCommitStart);\n clearForDomUpdateState(forState);\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import { getSignal } from "../runtime/component.js";
2
- import { ResourceResult, capture, on, resource, stream, task, timer } from "../runtime/operations.js";
3
- export { type ResourceResult, capture, getSignal, on, resource, stream, task, timer };
2
+ import { ActivityPredicate, ResourceResult, TimerOptions, capture, documentVisible, on, resource, routeActive, stream, task, timer, windowFocused } from "../runtime/operations.js";
3
+ export { type ActivityPredicate, type ResourceResult, type TimerOptions, capture, documentVisible, getSignal, on, resource, routeActive, stream, task, timer, windowFocused };
@@ -1,3 +1,3 @@
1
1
  import { getSignal } from "../runtime/component.js";
2
- import { capture, on, resource, stream, task, timer } from "../runtime/operations.js";
3
- export { capture, getSignal, on, resource, stream, task, timer };
2
+ import { capture, documentVisible, on, resource, routeActive, stream, task, timer, windowFocused } from "../runtime/operations.js";
3
+ export { capture, documentVisible, getSignal, on, resource, routeActive, stream, task, timer, windowFocused };
@@ -5,6 +5,9 @@ function splitPathSegments(path) {
5
5
  const withoutLeadingSlash = normalized.startsWith("/") ? normalized.slice(1) : normalized;
6
6
  return withoutLeadingSlash.length === 0 ? [] : withoutLeadingSlash.split("/");
7
7
  }
8
+ function normalizeRouteSegmentName(value) {
9
+ return value.trim();
10
+ }
8
11
  /**
9
12
  * Parse a route template path into typed segments.
10
13
  *
@@ -12,6 +15,7 @@ function splitPathSegments(path) {
12
15
  * parseSegments('/users/{id}') // [{kind:'static',value:'users'},{kind:'param',value:'id'}]
13
16
  * parseSegments('/*') // [{kind:'catchall',value:'*'}]
14
17
  * parseSegments('/posts/*') // [{kind:'static',value:'posts'},{kind:'wildcard',value:'*'}]
18
+ * parseSegments('/files/{*path}') // [{kind:'static',value:'files'},{kind:'splat',value:'path'}]
15
19
  */
16
20
  function parseSegments(path) {
17
21
  const parts = (path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path).split("/").filter(Boolean);
@@ -20,10 +24,17 @@ function parseSegments(path) {
20
24
  value: "*"
21
25
  }];
22
26
  return parts.map((segment) => {
23
- if (segment.startsWith("{") && segment.endsWith("}")) return {
24
- kind: "param",
25
- value: segment.slice(1, -1)
26
- };
27
+ if (segment.startsWith("{") && segment.endsWith("}")) {
28
+ const value = normalizeRouteSegmentName(segment.slice(1, -1));
29
+ if (value.startsWith("*")) return {
30
+ kind: "splat",
31
+ value: normalizeRouteSegmentName(value.slice(1))
32
+ };
33
+ return {
34
+ kind: "param",
35
+ value
36
+ };
37
+ }
27
38
  if (segment === "*") return {
28
39
  kind: "wildcard",
29
40
  value: "*"
@@ -46,6 +57,7 @@ function computeRank(segments) {
46
57
  for (const seg of segments) if (seg.kind === "static") score += 3;
47
58
  else if (seg.kind === "param") score += 2;
48
59
  else if (seg.kind === "wildcard") score += 1;
60
+ else if (seg.kind === "splat") score -= .5;
49
61
  return score;
50
62
  }
51
63
  /** Reused frozen empty params object — returned for purely-static (no-capture) routes. */
@@ -76,13 +88,26 @@ function decodeRouteParam(part) {
76
88
  */
77
89
  function matchSegments(urlParts, segments) {
78
90
  if (segments.length === 1 && segments[0].kind === "catchall") return { "*": urlParts.length === 0 ? "/" : urlParts.length === 1 ? urlParts[0] : "/" + urlParts.join("/") };
79
- if (urlParts.length !== segments.length) return null;
91
+ const splatIndex = segments.findIndex((segment) => segment.kind === "splat");
92
+ if (splatIndex !== -1) {
93
+ if (splatIndex !== segments.length - 1) return null;
94
+ if (urlParts.length < splatIndex) return null;
95
+ } else if (urlParts.length !== segments.length) return null;
80
96
  let params = null;
97
+ const normalizeCapturedSplatParts = (parts) => {
98
+ let start = 0;
99
+ while (start < parts.length && parts[start] === "") start += 1;
100
+ return start === 0 ? parts : parts.slice(start);
101
+ };
81
102
  for (let i = 0; i < segments.length; i++) {
82
103
  const seg = segments[i];
83
104
  const part = urlParts[i];
84
105
  if (seg.kind === "static") {
85
106
  if (seg.value !== part) return null;
107
+ } else if (seg.kind === "splat") {
108
+ if (params === null) params = {};
109
+ params[seg.value] = normalizeCapturedSplatParts(urlParts.slice(i).map(decodeRouteParam)).join("/");
110
+ return params;
86
111
  } else {
87
112
  if (params === null) params = {};
88
113
  if (seg.kind === "param") params[seg.value] = decodeRouteParam(part);
@@ -92,6 +117,6 @@ function matchSegments(urlParts, segments) {
92
117
  return params ?? emptyParams;
93
118
  }
94
119
  //#endregion
95
- export { computeRank, matchSegments, parseSegments, splitPathSegments };
120
+ export { computeRank, matchSegments, normalizeRouteSegmentName, parseSegments, splitPathSegments };
96
121
 
97
122
  //# sourceMappingURL=match.js.map