@askrjs/askr 0.0.33 → 0.0.35

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.
@@ -227,7 +227,7 @@ function updateElementChildren(element, vnodeChildren) {
227
227
  * Tries text-in-place update first, then full child reconciliation
228
228
  */
229
229
  function smartUpdateElement(element, vnode) {
230
- let vnodeChildren = vnode.children || vnode.props?.children;
230
+ let vnodeChildren = vnode.props?.children ?? vnode.children;
231
231
  if (vnodeChildren && _isDOMElement(vnodeChildren) && vnodeChildren.type === __FOR_BOUNDARY__) {
232
232
  updateElementChildren(element, vnodeChildren);
233
233
  updateElementFromVnode(element, vnode, false);
@@ -368,7 +368,7 @@ function isFragment(vnode) {
368
368
  * Unwrap Fragment to get children array
369
369
  */
370
370
  function getFragmentChildren(vnode) {
371
- const fragmentChildren = vnode.props?.children || vnode.children || [];
371
+ const fragmentChildren = vnode.props?.children ?? vnode.children ?? [];
372
372
  return Array.isArray(fragmentChildren) ? fragmentChildren : [fragmentChildren];
373
373
  }
374
374
  function evaluate(node, target, context) {
@@ -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 {\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 getPassiveOptions,\n parseEventName,\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 let vnodeChildren = vnode.children || vnode.props?.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 eventName = parseEventName(key);\n if (eventName) {\n const wrappedHandler = createWrappedHandler(value as EventListener, true);\n const options = getPassiveOptions(eventName);\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(eventName, {\n handler: wrappedHandler,\n original: value as EventListener,\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 el.setAttribute(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 (props: Props) => unknown,\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":"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"}
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 {\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 getPassiveOptions,\n parseEventName,\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 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 eventName = parseEventName(key);\n if (eventName) {\n const wrappedHandler = createWrappedHandler(value as EventListener, true);\n const options = getPassiveOptions(eventName);\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(eventName, {\n handler: wrappedHandler,\n original: value as EventListener,\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 el.setAttribute(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 (props: Props) => unknown,\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":"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"}
@@ -1,6 +1,6 @@
1
- import { AccessDecision, AccessDenyDecision, AccessRedirectDecision, GroupHelperOptions, LayoutScopeRecord, ParsedSegment, RegisterRoutesOptions, Route, RouteAuthMode, RouteAuthOptions, RouteAuthResolver, RouteAuthState, RouteComponent, RouteContext, RouteDefinition, RouteHandler, RouteManifest, RouteMatch, RouteMode, RouteOptions, RoutePolicy, RouteQuery, RouteRecord, RouteRenderResult, RouteRequestOptions, RouteRequestResult, RouteSnapshot } from "../common/router.js";
1
+ import { AccessDecision, AccessDenyDecision, AccessRedirectDecision, GroupHelperOptions, LayoutScopeRecord, PageHelperOptions, PageScopeRecord, ParsedSegment, RegisterRoutesOptions, Route, RouteAuthMode, RouteAuthOptions, RouteAuthResolver, RouteAuthState, RouteComponent, RouteContext, RouteDefinition, RouteHandler, RouteManifest, RouteMatch, RouteMode, RouteOptions, RoutePolicy, RouteQuery, RouteRecord, RouteRenderResult, RouteRequestOptions, RouteRequestResult, RouteSnapshot } from "../common/router.js";
2
2
  import { HistoryScrollBehavior, NavigateOptions, NavigationScrollBehavior, ScrollRestorationOptions, navigate } from "./navigate.js";
3
- import { _applyManifest, _drainLazy, clearRoutes, currentRoute, fallback, getLoadedNamespaces, getManifest, getNamespaceRoutes, getRoutes, group, lazy, registerRoutes, resolveRouteRequest, route, setServerLocation, unloadNamespace } from "./route.js";
3
+ import { Outlet, _applyManifest, _drainLazy, clearRoutes, currentRoute, fallback, getLoadedNamespaces, getManifest, getNamespaceRoutes, getRoutes, group, index, lazy, page, registerRoutes, resolveRouteRequest, route, setServerLocation, unloadNamespace } from "./route.js";
4
4
  import { allow, deny, forbidden, notFound, redirect, requireAuth, requirePermission, requireRole, unauthorized } from "./policy.js";
5
5
  import { Link, LinkProps } from "../components/link.js";
6
- export { type AccessDecision, type AccessDenyDecision, type AccessRedirectDecision, type GroupHelperOptions, type HistoryScrollBehavior, type LayoutScopeRecord, Link, type LinkProps, type NavigateOptions, type NavigationScrollBehavior, type ParsedSegment, type RegisterRoutesOptions, type Route, type RouteAuthMode, type RouteAuthOptions, type RouteAuthResolver, type RouteAuthState, type RouteComponent, type RouteContext, type RouteDefinition, type RouteHandler, type RouteManifest, type RouteMatch, type RouteMode, type RouteOptions, type RoutePolicy, type RouteQuery, type RouteRecord, type RouteRenderResult, type RouteRequestOptions, type RouteRequestResult, type RouteSnapshot, type ScrollRestorationOptions, _applyManifest, _drainLazy, allow, clearRoutes, currentRoute, deny, fallback, forbidden, getLoadedNamespaces, getManifest, getNamespaceRoutes, getRoutes, group, lazy, navigate, notFound, redirect, registerRoutes, requireAuth, requirePermission, requireRole, resolveRouteRequest, route, setServerLocation, unauthorized, unloadNamespace };
6
+ export { type AccessDecision, type AccessDenyDecision, type AccessRedirectDecision, type GroupHelperOptions, type HistoryScrollBehavior, type LayoutScopeRecord, Link, type LinkProps, type NavigateOptions, type NavigationScrollBehavior, Outlet, type PageHelperOptions, type PageScopeRecord, type ParsedSegment, type RegisterRoutesOptions, type Route, type RouteAuthMode, type RouteAuthOptions, type RouteAuthResolver, type RouteAuthState, type RouteComponent, type RouteContext, type RouteDefinition, type RouteHandler, type RouteManifest, type RouteMatch, type RouteMode, type RouteOptions, type RoutePolicy, type RouteQuery, type RouteRecord, type RouteRenderResult, type RouteRequestOptions, type RouteRequestResult, type RouteSnapshot, type ScrollRestorationOptions, _applyManifest, _drainLazy, allow, clearRoutes, currentRoute, deny, fallback, forbidden, getLoadedNamespaces, getManifest, getNamespaceRoutes, getRoutes, group, index, lazy, navigate, notFound, page, redirect, registerRoutes, requireAuth, requirePermission, requireRole, resolveRouteRequest, route, setServerLocation, unauthorized, unloadNamespace };
@@ -1,5 +1,5 @@
1
1
  import { allow, deny, forbidden, notFound, redirect, requireAuth, requirePermission, requireRole, unauthorized } from "./policy.js";
2
- import { _applyManifest, _drainLazy, clearRoutes, currentRoute, fallback, getLoadedNamespaces, getManifest, getNamespaceRoutes, getRoutes, group, lazy, registerRoutes, resolveRouteRequest, route, setServerLocation, unloadNamespace } from "./route.js";
2
+ import { Outlet, _applyManifest, _drainLazy, clearRoutes, currentRoute, fallback, getLoadedNamespaces, getManifest, getNamespaceRoutes, getRoutes, group, index, lazy, page, registerRoutes, resolveRouteRequest, route, setServerLocation, unloadNamespace } from "./route.js";
3
3
  import { navigate } from "./navigate.js";
4
4
  import { Link } from "../components/link.js";
5
- export { Link, _applyManifest, _drainLazy, allow, clearRoutes, currentRoute, deny, fallback, forbidden, getLoadedNamespaces, getManifest, getNamespaceRoutes, getRoutes, group, lazy, navigate, notFound, redirect, registerRoutes, requireAuth, requirePermission, requireRole, resolveRouteRequest, route, setServerLocation, unauthorized, unloadNamespace };
5
+ export { Link, Outlet, _applyManifest, _drainLazy, allow, clearRoutes, currentRoute, deny, fallback, forbidden, getLoadedNamespaces, getManifest, getNamespaceRoutes, getRoutes, group, index, lazy, navigate, notFound, page, redirect, registerRoutes, requireAuth, requirePermission, requireRole, resolveRouteRequest, route, setServerLocation, unauthorized, unloadNamespace };
@@ -1 +1 @@
1
- {"version":3,"file":"navigate.d.ts","names":[],"sources":["../../src/router/navigate.ts"],"mappings":";;;KAyBY,wBAAA;AAAA,KACA,qBAAA;AAAA,KAEA,wBAAA;EACV,UAAA,GAAa,wBAAA;EACb,OAAA,GAAU,qBAAA;AAAA;AAAA,KAqBA,eAAA;EACV,OAAA;EACA,MAAA,GAAS,wBAAA;AAAA;AAAA,iBAsIK,0BAAA,CACd,OAAA,aAAoB,wBAAA;;iBA6ON,mBAAA,CACd,QAAA,EAAU,iBAAA,EACV,IAAA;;;;;iBAiBc,QAAA,CAAS,IAAA,UAAc,OAAA,GAAS,eAAA;;AAzYhD;;iBAqdgB,oBAAA,CAAA;;;;iBASA,iBAAA,CAAA"}
1
+ {"version":3,"file":"navigate.d.ts","names":[],"sources":["../../src/router/navigate.ts"],"mappings":";;;KA0BY,wBAAA;AAAA,KACA,qBAAA;AAAA,KAEA,wBAAA;EACV,UAAA,GAAa,wBAAA;EACb,OAAA,GAAU,qBAAA;AAAA;AAAA,KAqBA,eAAA;EACV,OAAA;EACA,MAAA,GAAS,wBAAA;AAAA;AAAA,iBAsIK,0BAAA,CACd,OAAA,aAAoB,wBAAA;;iBAkPN,mBAAA,CACd,QAAA,EAAU,iBAAA,EACV,IAAA;;;;;iBAsBc,QAAA,CAAS,IAAA,UAAc,OAAA,GAAS,eAAA;;AAnZhD;;iBAqegB,oBAAA,CAAA;;;;iBASA,iBAAA,CAAA"}
@@ -2,7 +2,7 @@ import { isDevelopmentEnvironment, isProductionEnvironment } from "../common/env
2
2
  import { logger } from "../dev/logger.js";
3
3
  import { ELEMENT_TYPE, Fragment } from "../common/jsx.js";
4
4
  import { mountComponent } from "../runtime/component.js";
5
- import { lockRouteRegistration, resolveRoute, resolveRouteRequest } from "./route.js";
5
+ import { lockRouteRegistration, resolveRoute, resolveRouteRequest, syncCurrentRouteSnapshot } from "./route.js";
6
6
  import "../jsx/index.js";
7
7
  import { DefaultPortal } from "../foundations/structures/portal.js";
8
8
  //#region src/router/navigate.ts
@@ -211,6 +211,7 @@ function applyNavigationTarget(path, options, target) {
211
211
  };
212
212
  const historyMethod = options.history === "replace" ? "replaceState" : "pushState";
213
213
  window.history[historyMethod]({ path: href }, "", href);
214
+ syncCurrentRouteSnapshot(window.location.pathname, window.location.search, window.location.hash);
214
215
  if (currentInstance) {
215
216
  if (pathname === currentPathname && isRenderResult(resolved)) {
216
217
  rerenderResolvedRoute(pathname, href);
@@ -225,6 +226,7 @@ function registerAppInstance(instance, path) {
225
226
  currentInstance = instance;
226
227
  currentPathname = path;
227
228
  currentHref = getWindowHref();
229
+ syncCurrentRouteSnapshot(window.location.pathname, window.location.search, window.location.hash);
228
230
  saveScrollPosition(currentHref);
229
231
  if (isProductionEnvironment()) lockRouteRegistration();
230
232
  }
@@ -260,6 +262,7 @@ function handlePopState(_event) {
260
262
  navigate(resolved.to, { history: "replace" });
261
263
  return;
262
264
  }
265
+ syncCurrentRouteSnapshot(window.location.pathname, window.location.search, window.location.hash);
263
266
  if (pathname === currentPathname && isRenderResult(resolved)) {
264
267
  rerenderResolvedRoute(pathname, href);
265
268
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"navigate.js","names":[],"sources":["../../src/router/navigate.ts"],"sourcesContent":["/**\n * Client-side navigation with History API\n */\n\nimport {\n resolveRoute,\n resolveRouteRequest,\n lockRouteRegistration,\n type ResolvedRoute,\n} from './route';\nimport { mountComponent, type ComponentInstance } from '../runtime/component';\nimport {\n isDevelopmentEnvironment,\n isProductionEnvironment,\n} from '../common/env';\nimport { logger } from '../dev/logger';\nimport type { RouteRenderResult, RouteRequestResult } from '../common/router';\nimport { Fragment, ELEMENT_TYPE } from '../jsx';\nimport { DefaultPortal } from '../foundations/structures/portal';\n\n// Global app state for navigation\nlet currentInstance: ComponentInstance | null = null;\nlet currentPathname = '/';\nlet currentHref = '/';\n\nexport type NavigationScrollBehavior = 'top' | 'preserve';\nexport type HistoryScrollBehavior = 'restore' | 'top' | 'preserve';\n\nexport type ScrollRestorationOptions = {\n navigation?: NavigationScrollBehavior;\n history?: HistoryScrollBehavior;\n};\n\ntype NormalizedScrollRestorationOptions = {\n enabled: boolean;\n navigation: NavigationScrollBehavior;\n history: HistoryScrollBehavior;\n};\n\nconst DEFAULT_SCROLL_RESTORATION: NormalizedScrollRestorationOptions = {\n enabled: true,\n navigation: 'top',\n history: 'restore',\n};\n\nlet scrollRestorationOptions: NormalizedScrollRestorationOptions = {\n ...DEFAULT_SCROLL_RESTORATION,\n};\n\nconst scrollPositions = new Map<string, { x: number; y: number }>();\n\nexport type NavigateOptions = {\n history?: 'push' | 'replace';\n scroll?: NavigationScrollBehavior;\n};\n\nfunction getWindowHref(): string {\n if (typeof window === 'undefined') {\n return currentHref;\n }\n\n return `${window.location.pathname}${window.location.search}${window.location.hash}`;\n}\n\nfunction normalizeScrollRestorationOptions(\n options?: boolean | ScrollRestorationOptions\n): NormalizedScrollRestorationOptions {\n if (options === false) {\n return {\n enabled: false,\n navigation: DEFAULT_SCROLL_RESTORATION.navigation,\n history: DEFAULT_SCROLL_RESTORATION.history,\n };\n }\n\n if (options === true || options === undefined) {\n return { ...DEFAULT_SCROLL_RESTORATION };\n }\n\n return {\n enabled: true,\n navigation: options.navigation ?? DEFAULT_SCROLL_RESTORATION.navigation,\n history: options.history ?? DEFAULT_SCROLL_RESTORATION.history,\n };\n}\n\nfunction readScrollPosition(): { x: number; y: number } {\n if (typeof window === 'undefined') {\n return { x: 0, y: 0 };\n }\n\n const x =\n typeof window.scrollX === 'number'\n ? window.scrollX\n : typeof window.pageXOffset === 'number'\n ? window.pageXOffset\n : 0;\n const y =\n typeof window.scrollY === 'number'\n ? window.scrollY\n : typeof window.pageYOffset === 'number'\n ? window.pageYOffset\n : 0;\n\n return { x, y };\n}\n\nfunction writeHistoryScrollPosition(\n href: string,\n position: { x: number; y: number }\n): void {\n if (typeof window === 'undefined' || getWindowHref() !== href) {\n return;\n }\n if (typeof window.history?.replaceState !== 'function') {\n return;\n }\n const state =\n window.history.state && typeof window.history.state === 'object'\n ? window.history.state\n : {};\n\n window.history.replaceState(\n {\n ...state,\n path: href,\n scroll: position,\n },\n '',\n href\n );\n}\n\nfunction saveScrollPosition(href: string): void {\n if (!scrollRestorationOptions.enabled || typeof window === 'undefined') {\n return;\n }\n\n const position = readScrollPosition();\n scrollPositions.set(href, position);\n writeHistoryScrollPosition(href, position);\n}\n\nfunction scrollToPosition(position: { x: number; y: number }): void {\n if (typeof window === 'undefined' || typeof window.scrollTo !== 'function') {\n return;\n }\n\n window.scrollTo(position.x, position.y);\n}\n\nfunction applyNavigationScroll(behavior: NavigationScrollBehavior): void {\n if (!scrollRestorationOptions.enabled || behavior === 'preserve') {\n return;\n }\n\n scrollToPosition({ x: 0, y: 0 });\n}\n\nfunction applyHistoryScroll(href: string, state: PopStateEvent['state']): void {\n if (!scrollRestorationOptions.enabled) {\n return;\n }\n\n if (scrollRestorationOptions.history === 'preserve') {\n return;\n }\n\n if (scrollRestorationOptions.history === 'top') {\n scrollToPosition({ x: 0, y: 0 });\n return;\n }\n\n const fromState =\n state && typeof state === 'object' && 'scroll' in state\n ? (state.scroll as { x?: unknown; y?: unknown })\n : undefined;\n const saved =\n fromState &&\n typeof fromState.x === 'number' &&\n typeof fromState.y === 'number'\n ? { x: fromState.x, y: fromState.y }\n : scrollPositions.get(href);\n\n scrollToPosition(saved ?? { x: 0, y: 0 });\n}\n\nexport function configureScrollRestoration(\n options?: boolean | ScrollRestorationOptions\n): void {\n scrollRestorationOptions = normalizeScrollRestorationOptions(options);\n\n if (typeof window === 'undefined') {\n return;\n }\n\n if ('scrollRestoration' in window.history) {\n try {\n window.history.scrollRestoration = scrollRestorationOptions.enabled\n ? 'manual'\n : 'auto';\n } catch {\n // Ignore environments that expose but do not allow setting scrollRestoration.\n }\n }\n}\n\nfunction parseTargetUrl(path: string): URL {\n const pathname = window.location.pathname || '/';\n const search = window.location.search || '';\n const hash = window.location.hash || '';\n const href =\n typeof window.location.href === 'string' ? window.location.href : '';\n const base =\n href &&\n href !== 'about:blank' &&\n !href.startsWith('about:') &&\n !href.startsWith('data:')\n ? href\n : `http://localhost${pathname}${search}${hash}`;\n\n return new URL(path, base);\n}\n\nfunction isRenderResult(\n result: RouteRequestResult\n): result is RouteRenderResult {\n return result !== null && result.kind === 'render';\n}\n\nfunction isPromise<T>(value: T | Promise<T>): value is Promise<T> {\n return value instanceof Promise;\n}\n\nfunction createDeniedResolvedRoute(status: number): ResolvedRoute {\n return {\n handler: () => ({\n type: 'div',\n props: {\n 'data-route-denied': String(status),\n },\n children: [String(status)],\n }),\n params: {},\n };\n}\n\nfunction bindResolvedRouteHandler(\n resolved: ResolvedRoute\n): ComponentInstance['fn'] {\n return () =>\n resolved.handler(resolved.params) as ReturnType<ComponentInstance['fn']>;\n}\n\nfunction wrapRootRouteHandler(\n componentFn: ComponentInstance['fn']\n): ComponentInstance['fn'] {\n const wrappedFn: ComponentInstance['fn'] = (props, ctx) => {\n const out = componentFn(props, ctx);\n const portalVNode = {\n $$typeof: ELEMENT_TYPE,\n type: DefaultPortal,\n props: {},\n key: '__default_portal',\n } as unknown;\n\n return {\n $$typeof: ELEMENT_TYPE,\n type: Fragment,\n props: {\n children:\n out === undefined || out === null\n ? [portalVNode]\n : [out, portalVNode],\n },\n } as ReturnType<ComponentInstance['fn']>;\n };\n\n Object.defineProperty(wrappedFn, 'name', {\n value: componentFn.name || 'Component',\n });\n\n return wrappedFn;\n}\n\nfunction remountResolvedRoute(\n resolved: ResolvedRoute,\n pathname: string,\n href: string\n): boolean {\n if (!currentInstance) {\n return false;\n }\n\n // The route handler IS the component function\n // It takes params as props and renders the route\n currentInstance.fn = wrapRootRouteHandler(bindResolvedRouteHandler(resolved));\n currentInstance.props = {};\n\n // Reset state to prevent leakage from previous route\n // Each route navigation starts completely fresh\n currentInstance.stateValues = [];\n currentInstance.expectedStateIndices = [];\n currentInstance.firstRenderComplete = false;\n currentInstance.stateIndexCheck = -1;\n // Increment generation to invalidate pending async evaluations from previous route\n currentInstance.evaluationGeneration++;\n currentInstance.notifyUpdate = null;\n currentInstance.mountOperations = [];\n currentInstance._placeholder = undefined;\n currentInstance.hasPendingUpdate = false;\n\n // Route-local async work should create a fresh abort controller lazily.\n currentInstance.abortController = null;\n\n // Re-execute component against the existing host so reconciliation can\n // preserve any shared layout DOM between sibling routes.\n mountComponent(currentInstance);\n currentPathname = pathname;\n currentHref = href;\n return true;\n}\n\nfunction rerenderResolvedRoute(pathname: string, href: string): boolean {\n if (!currentInstance) {\n return false;\n }\n\n const resolved = resolveRoute(pathname);\n if (!resolved) {\n return false;\n }\n\n currentPathname = pathname;\n currentHref = href;\n currentInstance._enqueueRun?.();\n return true;\n}\n\nfunction resolveNavigationTarget(path: string) {\n const target = parseTargetUrl(path);\n const pathname = target.pathname;\n const href = `${target.pathname}${target.search}${target.hash}`;\n const resolved = resolveRouteRequest(href);\n\n if (isPromise(resolved)) {\n return resolved.then((next) => ({\n href,\n pathname,\n resolved: next,\n }));\n }\n\n return {\n href,\n pathname,\n resolved,\n };\n}\n\nfunction applyNavigationTarget(\n path: string,\n options: NavigateOptions,\n target: {\n href: string;\n pathname: string;\n resolved: RouteRequestResult;\n }\n): void {\n const { href, pathname, resolved } = target;\n const previousPathname = currentPathname;\n\n saveScrollPosition(currentHref);\n\n if (!resolved) {\n if (isDevelopmentEnvironment()) {\n logger.warn(`No route found for path: ${path}`);\n }\n return;\n }\n\n if (resolved.kind === 'redirect') {\n const redirectTarget = parseTargetUrl(resolved.to);\n const redirectHref = `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}`;\n if (redirectHref === href) {\n if (isDevelopmentEnvironment()) {\n logger.warn(\n `Navigation guard redirected to the same path: ${redirectHref}`\n );\n }\n return;\n }\n\n navigate(redirectHref, { history: 'replace' });\n return;\n }\n\n const nextResolved =\n resolved.kind === 'deny'\n ? createDeniedResolvedRoute(resolved.status)\n : {\n handler: resolved.handler,\n params: resolved.params,\n };\n\n const historyMethod =\n options.history === 'replace' ? 'replaceState' : 'pushState';\n window.history[historyMethod]({ path: href }, '', href);\n\n if (currentInstance) {\n if (pathname === currentPathname && isRenderResult(resolved)) {\n rerenderResolvedRoute(pathname, href);\n return;\n }\n\n remountResolvedRoute(nextResolved, pathname, href);\n if (pathname !== previousPathname) {\n applyNavigationScroll(\n options.scroll ?? scrollRestorationOptions.navigation\n );\n }\n }\n}\n\n/** Register the current app instance (called by createSPA/hydrateSPA). */\nexport function registerAppInstance(\n instance: ComponentInstance,\n path: string\n): void {\n currentInstance = instance;\n currentPathname = path;\n currentHref = getWindowHref();\n saveScrollPosition(currentHref);\n // Lock further route registrations after the app has started — but allow tests to register routes.\n // Enforce only in production to avoid breaking test infra which registers routes dynamically.\n if (isProductionEnvironment()) {\n lockRouteRegistration();\n }\n}\n\n/**\n * Navigate to a new path\n * Updates URL, resolves route, and re-mounts app with new handler\n */\nexport function navigate(path: string, options: NavigateOptions = {}): void {\n if (typeof window === 'undefined') {\n // SSR context\n return;\n }\n\n const target = resolveNavigationTarget(path);\n if (isPromise(target)) {\n void target.then((resolvedTarget) =>\n applyNavigationTarget(path, options, resolvedTarget)\n );\n return;\n }\n\n applyNavigationTarget(path, options, target);\n}\n\n/**\n * Handle browser back/forward buttons\n */\nfunction handlePopState(_event: PopStateEvent): void {\n const previousHref = currentHref;\n const pathname = window.location.pathname;\n const href = `${window.location.pathname}${window.location.search}${window.location.hash}`;\n\n saveScrollPosition(previousHref);\n\n if (!currentInstance) {\n return;\n }\n\n const resolved = resolveRouteRequest(href);\n\n const applyResolved = (resolved: RouteRequestResult) => {\n if (!resolved) {\n if (isDevelopmentEnvironment()) {\n logger.warn(`No route found for path: ${pathname}`);\n }\n return;\n }\n\n if (resolved.kind === 'redirect') {\n navigate(resolved.to, { history: 'replace' });\n return;\n }\n\n if (pathname === currentPathname && isRenderResult(resolved)) {\n rerenderResolvedRoute(pathname, href);\n return;\n }\n\n remountResolvedRoute(\n resolved.kind === 'deny'\n ? createDeniedResolvedRoute(resolved.status)\n : {\n handler: resolved.handler,\n params: resolved.params,\n },\n pathname,\n href\n );\n\n applyHistoryScroll(href, _event.state);\n };\n\n if (isPromise(resolved)) {\n void resolved.then((next) => applyResolved(next));\n return;\n }\n\n applyResolved(resolved);\n}\n\n/**\n * Setup popstate listener for browser navigation\n */\nexport function initializeNavigation(): void {\n if (typeof window !== 'undefined') {\n window.addEventListener('popstate', handlePopState);\n }\n}\n\n/**\n * Cleanup navigation listeners\n */\nexport function cleanupNavigation(): void {\n if (typeof window !== 'undefined') {\n window.removeEventListener('popstate', handlePopState);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"navigate.js","names":[],"sources":["../../src/router/navigate.ts"],"sourcesContent":["/**\n * Client-side navigation with History API\n */\n\nimport {\n resolveRoute,\n resolveRouteRequest,\n lockRouteRegistration,\n syncCurrentRouteSnapshot,\n type ResolvedRoute,\n} from './route';\nimport { mountComponent, type ComponentInstance } from '../runtime/component';\nimport {\n isDevelopmentEnvironment,\n isProductionEnvironment,\n} from '../common/env';\nimport { logger } from '../dev/logger';\nimport type { RouteRenderResult, RouteRequestResult } from '../common/router';\nimport { Fragment, ELEMENT_TYPE } from '../jsx';\nimport { DefaultPortal } from '../foundations/structures/portal';\n\n// Global app state for navigation\nlet currentInstance: ComponentInstance | null = null;\nlet currentPathname = '/';\nlet currentHref = '/';\n\nexport type NavigationScrollBehavior = 'top' | 'preserve';\nexport type HistoryScrollBehavior = 'restore' | 'top' | 'preserve';\n\nexport type ScrollRestorationOptions = {\n navigation?: NavigationScrollBehavior;\n history?: HistoryScrollBehavior;\n};\n\ntype NormalizedScrollRestorationOptions = {\n enabled: boolean;\n navigation: NavigationScrollBehavior;\n history: HistoryScrollBehavior;\n};\n\nconst DEFAULT_SCROLL_RESTORATION: NormalizedScrollRestorationOptions = {\n enabled: true,\n navigation: 'top',\n history: 'restore',\n};\n\nlet scrollRestorationOptions: NormalizedScrollRestorationOptions = {\n ...DEFAULT_SCROLL_RESTORATION,\n};\n\nconst scrollPositions = new Map<string, { x: number; y: number }>();\n\nexport type NavigateOptions = {\n history?: 'push' | 'replace';\n scroll?: NavigationScrollBehavior;\n};\n\nfunction getWindowHref(): string {\n if (typeof window === 'undefined') {\n return currentHref;\n }\n\n return `${window.location.pathname}${window.location.search}${window.location.hash}`;\n}\n\nfunction normalizeScrollRestorationOptions(\n options?: boolean | ScrollRestorationOptions\n): NormalizedScrollRestorationOptions {\n if (options === false) {\n return {\n enabled: false,\n navigation: DEFAULT_SCROLL_RESTORATION.navigation,\n history: DEFAULT_SCROLL_RESTORATION.history,\n };\n }\n\n if (options === true || options === undefined) {\n return { ...DEFAULT_SCROLL_RESTORATION };\n }\n\n return {\n enabled: true,\n navigation: options.navigation ?? DEFAULT_SCROLL_RESTORATION.navigation,\n history: options.history ?? DEFAULT_SCROLL_RESTORATION.history,\n };\n}\n\nfunction readScrollPosition(): { x: number; y: number } {\n if (typeof window === 'undefined') {\n return { x: 0, y: 0 };\n }\n\n const x =\n typeof window.scrollX === 'number'\n ? window.scrollX\n : typeof window.pageXOffset === 'number'\n ? window.pageXOffset\n : 0;\n const y =\n typeof window.scrollY === 'number'\n ? window.scrollY\n : typeof window.pageYOffset === 'number'\n ? window.pageYOffset\n : 0;\n\n return { x, y };\n}\n\nfunction writeHistoryScrollPosition(\n href: string,\n position: { x: number; y: number }\n): void {\n if (typeof window === 'undefined' || getWindowHref() !== href) {\n return;\n }\n if (typeof window.history?.replaceState !== 'function') {\n return;\n }\n const state =\n window.history.state && typeof window.history.state === 'object'\n ? window.history.state\n : {};\n\n window.history.replaceState(\n {\n ...state,\n path: href,\n scroll: position,\n },\n '',\n href\n );\n}\n\nfunction saveScrollPosition(href: string): void {\n if (!scrollRestorationOptions.enabled || typeof window === 'undefined') {\n return;\n }\n\n const position = readScrollPosition();\n scrollPositions.set(href, position);\n writeHistoryScrollPosition(href, position);\n}\n\nfunction scrollToPosition(position: { x: number; y: number }): void {\n if (typeof window === 'undefined' || typeof window.scrollTo !== 'function') {\n return;\n }\n\n window.scrollTo(position.x, position.y);\n}\n\nfunction applyNavigationScroll(behavior: NavigationScrollBehavior): void {\n if (!scrollRestorationOptions.enabled || behavior === 'preserve') {\n return;\n }\n\n scrollToPosition({ x: 0, y: 0 });\n}\n\nfunction applyHistoryScroll(href: string, state: PopStateEvent['state']): void {\n if (!scrollRestorationOptions.enabled) {\n return;\n }\n\n if (scrollRestorationOptions.history === 'preserve') {\n return;\n }\n\n if (scrollRestorationOptions.history === 'top') {\n scrollToPosition({ x: 0, y: 0 });\n return;\n }\n\n const fromState =\n state && typeof state === 'object' && 'scroll' in state\n ? (state.scroll as { x?: unknown; y?: unknown })\n : undefined;\n const saved =\n fromState &&\n typeof fromState.x === 'number' &&\n typeof fromState.y === 'number'\n ? { x: fromState.x, y: fromState.y }\n : scrollPositions.get(href);\n\n scrollToPosition(saved ?? { x: 0, y: 0 });\n}\n\nexport function configureScrollRestoration(\n options?: boolean | ScrollRestorationOptions\n): void {\n scrollRestorationOptions = normalizeScrollRestorationOptions(options);\n\n if (typeof window === 'undefined') {\n return;\n }\n\n if ('scrollRestoration' in window.history) {\n try {\n window.history.scrollRestoration = scrollRestorationOptions.enabled\n ? 'manual'\n : 'auto';\n } catch {\n // Ignore environments that expose but do not allow setting scrollRestoration.\n }\n }\n}\n\nfunction parseTargetUrl(path: string): URL {\n const pathname = window.location.pathname || '/';\n const search = window.location.search || '';\n const hash = window.location.hash || '';\n const href =\n typeof window.location.href === 'string' ? window.location.href : '';\n const base =\n href &&\n href !== 'about:blank' &&\n !href.startsWith('about:') &&\n !href.startsWith('data:')\n ? href\n : `http://localhost${pathname}${search}${hash}`;\n\n return new URL(path, base);\n}\n\nfunction isRenderResult(\n result: RouteRequestResult\n): result is RouteRenderResult {\n return result !== null && result.kind === 'render';\n}\n\nfunction isPromise<T>(value: T | Promise<T>): value is Promise<T> {\n return value instanceof Promise;\n}\n\nfunction createDeniedResolvedRoute(status: number): ResolvedRoute {\n return {\n handler: () => ({\n type: 'div',\n props: {\n 'data-route-denied': String(status),\n },\n children: [String(status)],\n }),\n params: {},\n };\n}\n\nfunction bindResolvedRouteHandler(\n resolved: ResolvedRoute\n): ComponentInstance['fn'] {\n return () =>\n resolved.handler(resolved.params) as ReturnType<ComponentInstance['fn']>;\n}\n\nfunction wrapRootRouteHandler(\n componentFn: ComponentInstance['fn']\n): ComponentInstance['fn'] {\n const wrappedFn: ComponentInstance['fn'] = (props, ctx) => {\n const out = componentFn(props, ctx);\n const portalVNode = {\n $$typeof: ELEMENT_TYPE,\n type: DefaultPortal,\n props: {},\n key: '__default_portal',\n } as unknown;\n\n return {\n $$typeof: ELEMENT_TYPE,\n type: Fragment,\n props: {\n children:\n out === undefined || out === null\n ? [portalVNode]\n : [out, portalVNode],\n },\n } as ReturnType<ComponentInstance['fn']>;\n };\n\n Object.defineProperty(wrappedFn, 'name', {\n value: componentFn.name || 'Component',\n });\n\n return wrappedFn;\n}\n\nfunction remountResolvedRoute(\n resolved: ResolvedRoute,\n pathname: string,\n href: string\n): boolean {\n if (!currentInstance) {\n return false;\n }\n\n // The route handler IS the component function\n // It takes params as props and renders the route\n currentInstance.fn = wrapRootRouteHandler(bindResolvedRouteHandler(resolved));\n currentInstance.props = {};\n\n // Reset state to prevent leakage from previous route\n // Each route navigation starts completely fresh\n currentInstance.stateValues = [];\n currentInstance.expectedStateIndices = [];\n currentInstance.firstRenderComplete = false;\n currentInstance.stateIndexCheck = -1;\n // Increment generation to invalidate pending async evaluations from previous route\n currentInstance.evaluationGeneration++;\n currentInstance.notifyUpdate = null;\n currentInstance.mountOperations = [];\n currentInstance._placeholder = undefined;\n currentInstance.hasPendingUpdate = false;\n\n // Route-local async work should create a fresh abort controller lazily.\n currentInstance.abortController = null;\n\n // Re-execute component against the existing host so reconciliation can\n // preserve any shared layout DOM between sibling routes.\n mountComponent(currentInstance);\n currentPathname = pathname;\n currentHref = href;\n return true;\n}\n\nfunction rerenderResolvedRoute(pathname: string, href: string): boolean {\n if (!currentInstance) {\n return false;\n }\n\n const resolved = resolveRoute(pathname);\n if (!resolved) {\n return false;\n }\n\n currentPathname = pathname;\n currentHref = href;\n currentInstance._enqueueRun?.();\n return true;\n}\n\nfunction resolveNavigationTarget(path: string) {\n const target = parseTargetUrl(path);\n const pathname = target.pathname;\n const href = `${target.pathname}${target.search}${target.hash}`;\n const resolved = resolveRouteRequest(href);\n\n if (isPromise(resolved)) {\n return resolved.then((next) => ({\n href,\n pathname,\n resolved: next,\n }));\n }\n\n return {\n href,\n pathname,\n resolved,\n };\n}\n\nfunction applyNavigationTarget(\n path: string,\n options: NavigateOptions,\n target: {\n href: string;\n pathname: string;\n resolved: RouteRequestResult;\n }\n): void {\n const { href, pathname, resolved } = target;\n const previousPathname = currentPathname;\n\n saveScrollPosition(currentHref);\n\n if (!resolved) {\n if (isDevelopmentEnvironment()) {\n logger.warn(`No route found for path: ${path}`);\n }\n return;\n }\n\n if (resolved.kind === 'redirect') {\n const redirectTarget = parseTargetUrl(resolved.to);\n const redirectHref = `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}`;\n if (redirectHref === href) {\n if (isDevelopmentEnvironment()) {\n logger.warn(\n `Navigation guard redirected to the same path: ${redirectHref}`\n );\n }\n return;\n }\n\n navigate(redirectHref, { history: 'replace' });\n return;\n }\n\n const nextResolved =\n resolved.kind === 'deny'\n ? createDeniedResolvedRoute(resolved.status)\n : {\n handler: resolved.handler,\n params: resolved.params,\n };\n\n const historyMethod =\n options.history === 'replace' ? 'replaceState' : 'pushState';\n window.history[historyMethod]({ path: href }, '', href);\n syncCurrentRouteSnapshot(\n window.location.pathname,\n window.location.search,\n window.location.hash\n );\n\n if (currentInstance) {\n if (pathname === currentPathname && isRenderResult(resolved)) {\n rerenderResolvedRoute(pathname, href);\n return;\n }\n\n remountResolvedRoute(nextResolved, pathname, href);\n if (pathname !== previousPathname) {\n applyNavigationScroll(\n options.scroll ?? scrollRestorationOptions.navigation\n );\n }\n }\n}\n\n/** Register the current app instance (called by createSPA/hydrateSPA). */\nexport function registerAppInstance(\n instance: ComponentInstance,\n path: string\n): void {\n currentInstance = instance;\n currentPathname = path;\n currentHref = getWindowHref();\n syncCurrentRouteSnapshot(\n window.location.pathname,\n window.location.search,\n window.location.hash\n );\n saveScrollPosition(currentHref);\n // Lock further route registrations after the app has started — but allow tests to register routes.\n // Enforce only in production to avoid breaking test infra which registers routes dynamically.\n if (isProductionEnvironment()) {\n lockRouteRegistration();\n }\n}\n\n/**\n * Navigate to a new path\n * Updates URL, resolves route, and re-mounts app with new handler\n */\nexport function navigate(path: string, options: NavigateOptions = {}): void {\n if (typeof window === 'undefined') {\n // SSR context\n return;\n }\n\n const target = resolveNavigationTarget(path);\n if (isPromise(target)) {\n void target.then((resolvedTarget) =>\n applyNavigationTarget(path, options, resolvedTarget)\n );\n return;\n }\n\n applyNavigationTarget(path, options, target);\n}\n\n/**\n * Handle browser back/forward buttons\n */\nfunction handlePopState(_event: PopStateEvent): void {\n const previousHref = currentHref;\n const pathname = window.location.pathname;\n const href = `${window.location.pathname}${window.location.search}${window.location.hash}`;\n\n saveScrollPosition(previousHref);\n\n if (!currentInstance) {\n return;\n }\n\n const resolved = resolveRouteRequest(href);\n\n const applyResolved = (resolved: RouteRequestResult) => {\n if (!resolved) {\n if (isDevelopmentEnvironment()) {\n logger.warn(`No route found for path: ${pathname}`);\n }\n return;\n }\n\n if (resolved.kind === 'redirect') {\n navigate(resolved.to, { history: 'replace' });\n return;\n }\n\n syncCurrentRouteSnapshot(\n window.location.pathname,\n window.location.search,\n window.location.hash\n );\n\n if (pathname === currentPathname && isRenderResult(resolved)) {\n rerenderResolvedRoute(pathname, href);\n return;\n }\n\n remountResolvedRoute(\n resolved.kind === 'deny'\n ? createDeniedResolvedRoute(resolved.status)\n : {\n handler: resolved.handler,\n params: resolved.params,\n },\n pathname,\n href\n );\n\n applyHistoryScroll(href, _event.state);\n };\n\n if (isPromise(resolved)) {\n void resolved.then((next) => applyResolved(next));\n return;\n }\n\n applyResolved(resolved);\n}\n\n/**\n * Setup popstate listener for browser navigation\n */\nexport function initializeNavigation(): void {\n if (typeof window !== 'undefined') {\n window.addEventListener('popstate', handlePopState);\n }\n}\n\n/**\n * Cleanup navigation listeners\n */\nexport function cleanupNavigation(): void {\n if (typeof window !== 'undefined') {\n window.removeEventListener('popstate', handlePopState);\n }\n}\n"],"mappings":"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"}
@@ -1,11 +1,16 @@
1
- import { AccessDecision, AccessDenyDecision, AccessRedirectDecision, GroupHelperOptions, LayoutScopeRecord, ParsedSegment, RegisterRoutesOptions, ResolvedRoute, Route, RouteAuthOptions, RouteComponent, RouteContext, RouteDefinition, RouteHandler, RouteManifest, RouteMatch, RouteOptions, RoutePolicy, RouteQuery, RouteRecord, RouteRenderResult, RouteRequestOptions, RouteRequestResult, RouteSnapshot } from "../common/router.js";
1
+ import { AccessDecision, AccessDenyDecision, AccessRedirectDecision, GroupHelperOptions, LayoutScopeRecord, PageHelperOptions, PageScopeRecord, ParsedSegment, RegisterRoutesOptions, ResolvedRoute, Route, RouteAuthOptions, RouteComponent, RouteContext, RouteDefinition, RouteHandler, RouteManifest, RouteMatch, RouteOptions, RoutePolicy, RouteQuery, RouteRecord, RouteRenderResult, RouteRequestOptions, RouteRequestResult, RouteSnapshot } from "../common/router.js";
2
2
 
3
3
  //#region src/router/route.d.ts
4
4
  declare function setServerLocation(url: string | null): void;
5
5
  declare function _setActiveRouteAuthOptions(auth: RouteAuthOptions | undefined): void;
6
+ /**
7
+ * Prevent route registrations after the app has started.
8
+ * Enforced in production; tests may unlock explicitly.
9
+ */
6
10
  declare function lockRouteRegistration(): void;
7
11
  declare function _lockRouteRegistrationForTests(): void;
8
12
  declare function _unlockRouteRegistrationForTests(): void;
13
+ declare function Outlet(): unknown;
9
14
  /**
10
15
  * Snapshot the current in-flight lazy() imports.
11
16
  * Boot uses this before clearing route state so manifest-based startup can
@@ -42,9 +47,13 @@ declare function lazy(factory: () => Promise<{
42
47
  */
43
48
  declare function _drainLazy(additionalPending?: Iterable<Promise<unknown>>): Promise<void>;
44
49
  declare function group(options: GroupHelperOptions, fn: RouteDefinition): void;
50
+ declare function page(path: string, Component: RouteComponent, fn: RouteDefinition): void;
51
+ declare function page(path: string, Component: RouteComponent, options: PageHelperOptions, fn: RouteDefinition): void;
52
+ declare function index(Component: RouteComponent, options?: RouteOptions): void;
45
53
  declare function fallback(Component: RouteComponent): void;
46
54
  declare function registerRoutes(definition: RouteDefinition, options?: RegisterRoutesOptions): void;
47
55
  declare function currentRoute(): RouteSnapshot;
56
+ declare function syncCurrentRouteSnapshot(pathname: string, search: string, hash: string): void;
48
57
  /**
49
58
  * Register a route.
50
59
  *
@@ -116,5 +125,5 @@ declare function resolveRouteRequest(target: string, options?: RouteRequestOptio
116
125
  */
117
126
  declare function resolveRouteFromRoutes(pathname: string, routeList: readonly Route[]): ResolvedRoute | null;
118
127
  //#endregion
119
- export { _applyManifest, _drainLazy, clearRoutes, currentRoute, fallback, getLoadedNamespaces, getManifest, getNamespaceRoutes, getRoutes, group, lazy, registerRoutes, resolveRouteRequest, route, setServerLocation, unloadNamespace };
128
+ export { Outlet, _applyManifest, _drainLazy, clearRoutes, currentRoute, fallback, getLoadedNamespaces, getManifest, getNamespaceRoutes, getRoutes, group, index, lazy, page, registerRoutes, resolveRouteRequest, route, setServerLocation, unloadNamespace };
120
129
  //# sourceMappingURL=route.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","names":[],"sources":["../../src/router/route.ts"],"mappings":";;;iBAiMgB,iBAAA,CAAkB,GAAA;AAAA,iBAuElB,0BAAA,CACd,IAAA,EAAM,gBAAA;AAAA,iBAwDQ,qBAAA,CAAA;AAAA,iBAIA,8BAAA,CAAA;AAAA,iBAIA,gCAAA,CAAA;AAJhB;;;;;AAAA,iBA6EgB,aAAA,CAAA,GAAiB,OAAA;;;;;AAAjC;;;;;AAyBA;;;;;;;;;;;;iBAAgB,IAAA,CACd,OAAA,QAAe,OAAA;EAAU,OAAA,EAAS,cAAA;AAAA,IAAmB,cAAA,IACpD,cAAA;;;AAmCH;;iBAAgB,UAAA,CACd,iBAAA,GAAmB,QAAA,CAAS,OAAA,aAC3B,OAAA;AAAA,iBASa,KAAA,CAAM,OAAA,EAAS,kBAAA,EAAoB,EAAA,EAAI,eAAA;AAAA,iBAKvC,QAAA,CAAS,SAAA,EAAW,cAAA;AAAA,iBAmLpB,cAAA,CACd,UAAA,EAAY,eAAA,EACZ,OAAA,GAAS,qBAAA;AAAA,iBAoEK,YAAA,CAAA,GAAgB,aAAA;;;;;;;;AA9PhC;;;;;iBA8QgB,KAAA,CACd,IAAA,UACA,SAAA,EAAW,cAAA,EACX,OAAA,GAAU,YAAA;;;;;AA5QZ;;;;;AAmLA;;iBAyMgB,WAAA,CAAA,GAAe,aAAA;;;;;iBAWf,cAAA,CAAe,QAAA,EAAU,aAAA;;;AA9IzC;;iBAoKgB,SAAA,CAAA,GAAa,KAAA;;iBAKb,kBAAA,CAAmB,SAAA,WAAoB,KAAA;AAzJvD;AAAA,iBA8JgB,eAAA,CAAgB,SAAA;;iBA6BhB,WAAA,CAAA;;;;iBAiBA,mBAAA,CAAA;;;;AAzFhB;;;;;iBAqGgB,YAAA,CAAa,QAAA,WAAmB,aAAA;AAAA,iBA0GhC,mBAAA,CACd,MAAA,UACA,OAAA,GAAS,mBAAA,GACR,kBAAA,GAAqB,OAAA,CAAQ,kBAAA;;;;AAjLhC;;;;;AAKA;;;;;AAKA;iBA2OgB,sBAAA,CACd,QAAA,UACA,SAAA,WAAoB,KAAA,KACnB,aAAA"}
1
+ {"version":3,"file":"route.d.ts","names":[],"sources":["../../src/router/route.ts"],"mappings":";;;iBAoOgB,iBAAA,CAAkB,GAAA;AAAA,iBA8MlB,0BAAA,CACd,IAAA,EAAM,gBAAA;AAuFR;;;;AAAA,iBAJgB,qBAAA,CAAA;AAAA,iBAIA,8BAAA,CAAA;AAAA,iBAIA,gCAAA,CAAA;AAAA,iBAmIA,MAAA,CAAA;;;AAAhB;;;iBASgB,aAAA,CAAA,GAAiB,OAAA;;AAAjC;;;;;AAyBA;;;;;;;;;;;;;;;iBAAgB,IAAA,CACd,OAAA,QAAe,OAAA;EAAU,OAAA,EAAS,cAAA;AAAA,IAAmB,cAAA,IACpD,cAAA;;;;;iBAmCa,UAAA,CACd,iBAAA,GAAmB,QAAA,CAAS,OAAA,aAC3B,OAAA;AAAA,iBASa,KAAA,CAAM,OAAA,EAAS,kBAAA,EAAoB,EAAA,EAAI,eAAA;AAAA,iBAKvC,IAAA,CACd,IAAA,UACA,SAAA,EAAW,cAAA,EACX,EAAA,EAAI,eAAA;AAAA,iBAEU,IAAA,CACd,IAAA,UACA,SAAA,EAAW,cAAA,EACX,OAAA,EAAS,iBAAA,EACT,EAAA,EAAI,eAAA;AAAA,iBA2BU,KAAA,CAAM,SAAA,EAAW,cAAA,EAAgB,OAAA,GAAU,YAAA;AAAA,iBAI3C,QAAA,CAAS,SAAA,EAAW,cAAA;AAAA,iBAkUpB,cAAA,CACd,UAAA,EAAY,eAAA,EACZ,OAAA,GAAS,qBAAA;AAAA,iBAoEK,YAAA,CAAA,GAAgB,aAAA;AAAA,iBAsBhB,wBAAA,CACd,QAAA,UACA,MAAA,UACA,IAAA;AA9cF;;;;;;;;;;AAKA;;AALA,iBA+dgB,KAAA,CACd,IAAA,UACA,SAAA,EAAW,cAAA,EACX,OAAA,GAAU,YAAA;;;;;;;;;AAxdZ;;;iBA0hBgB,WAAA,CAAA,GAAe,aAAA;;;;;iBAWf,cAAA,CAAe,QAAA,EAAU,aAAA;;;;;iBA0BzB,SAAA,CAAA,GAAa,KAAA;;iBAKb,kBAAA,CAAmB,SAAA,WAAoB,KAAA;;iBAKvC,eAAA,CAAgB,SAAA;;iBA6BhB,WAAA,CAAA;;;;iBAiBA,mBAAA,CAAA;;;;AAplBhB;;;;;iBA+nBgB,YAAA,CAAa,QAAA,WAAmB,aAAA;AAAA,iBAuHhC,mBAAA,CACd,MAAA,UACA,OAAA,GAAS,mBAAA,GACR,kBAAA,GAAqB,OAAA,CAAQ,kBAAA;;;;;;;;;AAjXhC;;;;;AAsBA;iBA+ZgB,sBAAA,CACd,QAAA,UACA,SAAA,WAAoB,KAAA,KACnB,aAAA"}