@askrjs/askr 0.0.77 → 0.0.79

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 (61) hide show
  1. package/dist/actions/index.d.ts +1 -1
  2. package/dist/actions/index.js +2 -2
  3. package/dist/{activity-DaqZEUPh.d.ts → activity-CXEaU-Al.d.ts} +1 -1
  4. package/dist/{activity-BWQeEtNt.js → activity-DfrtblJA.js} +3 -3
  5. package/dist/{authoring-prA293gq.js → authoring-DE-LH_s-.js} +2 -2
  6. package/dist/benchmark.js +5 -5
  7. package/dist/boot/index.d.ts +4 -4
  8. package/dist/boot/index.js +1 -1
  9. package/dist/{boot-Cc5axhVo.js → boot-jD3P49ST.js} +12 -12
  10. package/dist/{component-internal-TurioKgs.js → component-internal-BuTG89T8.js} +11 -7
  11. package/dist/components/index.d.ts +1 -1
  12. package/dist/control/index.d.ts +1 -1
  13. package/dist/control/index.js +1 -1
  14. package/dist/control-C-ApnFs1.js +20 -0
  15. package/dist/{control-Uvq1GU4K.d.ts → control-Cix3yjVf.d.ts} +7 -1
  16. package/dist/{control-D9UorK1Q.js → control-DQIsWvhr.js} +2 -2
  17. package/dist/{csp-nonce-DYspaK0L.js → csp-nonce-BUsz0Em7.js} +1 -1
  18. package/dist/data/index.d.ts +1 -1
  19. package/dist/data/index.js +3 -3
  20. package/dist/{data-vaClyf1I.js → data-eBdygZ1f.js} +3 -3
  21. package/dist/{data-runtime-rpV6-3XW.js → data-runtime-D68YOo9L.js} +1 -1
  22. package/dist/{deferred-Dfk7vuTX.js → deferred-t79wHqe1.js} +5 -5
  23. package/dist/{for-internal-BM8KbadU.js → for-internal-BnXrQbgo.js} +1 -1
  24. package/dist/foundations/index.d.ts +1 -1
  25. package/dist/foundations/index.js +1 -1
  26. package/dist/foundations/state/index.d.ts +1 -1
  27. package/dist/foundations/structures/index.d.ts +1 -1
  28. package/dist/foundations/structures/index.js +1 -1
  29. package/dist/{index-Bki2rqcr.d.ts → index-5S-fi2xy.d.ts} +17 -17
  30. package/dist/{router-Dd-LoJmM.d.ts → index-TlEvex-C.d.ts} +162 -4
  31. package/dist/{index-CbRdEUPB.d.ts → index-Vs1EeWEr.d.ts} +2 -2
  32. package/dist/{index-acX2RLxD.d.ts → index-ho0Htetw.d.ts} +1 -1
  33. package/dist/index.d.ts +5 -5
  34. package/dist/index.js +8 -7
  35. package/dist/jsx-dev-runtime.js +1 -1
  36. package/dist/jsx-runtime.js +1 -1
  37. package/dist/{lifecycle-operations-DxwqHvyx.js → lifecycle-operations-CN0PiLE_.js} +1 -1
  38. package/dist/{navigate-DlaVqF4n.d.ts → navigate-DfY1BUYc.d.ts} +1 -1
  39. package/dist/{navigate-CwddZjwU.js → navigate-ype2B_M8.js} +6 -6
  40. package/dist/{portal-BsSJusvs.js → portal-BZ_rN17v.js} +14 -13
  41. package/dist/portal-w3oQ6oQU.js +29 -0
  42. package/dist/{query-registry-Bu_cuMch.js → query-registry-BL6rjGsO.js} +1 -1
  43. package/dist/{render-context-Clw3rFiO.js → render-context-D-DFk2WN.js} +13 -1
  44. package/dist/{renderer-Ba-KixHB.js → renderer-CDw8ltTw.js} +952 -763
  45. package/dist/resources/index.d.ts +2 -2
  46. package/dist/resources/index.js +4 -4
  47. package/dist/{route-matching-DqM8k4tZ.js → route-matching-jqwlQJ5g.js} +2 -2
  48. package/dist/router/index.d.ts +4 -4
  49. package/dist/router/index.js +5 -5
  50. package/dist/ssg/index.d.ts +2 -2
  51. package/dist/ssg/index.js +7 -5
  52. package/dist/ssr/index.d.ts +8 -25
  53. package/dist/ssr/index.js +2 -2
  54. package/dist/{ssr-Cv5qWPxz.js → ssr-Dcmi1bZi.js} +53 -24
  55. package/dist/testing/index.d.ts +3 -3
  56. package/dist/testing/index.js +3 -3
  57. package/dist/{verify-hydration-BC5bmXr4.js → verify-hydration-Dfx-ZhbX.js} +1 -1
  58. package/package.json +1 -1
  59. package/dist/control-DhFRTIoE.js +0 -12
  60. package/dist/index-CDACZEF2.d.ts +0 -102
  61. package/dist/index-vo_FDR-B.d.ts +0 -40
@@ -1,17 +1,18 @@
1
- import { t as __CONTROL_BOUNDARY__ } from "./control-DhFRTIoE.js";
1
+ import { n as hasTransparentComponentResult, t as __CONTROL_BOUNDARY__ } from "./control-C-ApnFs1.js";
2
2
  import { n as _isDOMElement, t as __ERROR_BOUNDARY__ } from "./vnode-D24p6IW2.js";
3
3
  import { b as isDevelopmentEnvironment, c as runRuntimeHandlerScope, f as configureRuntimeRenderer, i as getDefaultRuntimeInstance, n as enqueueRuntimeTask, v as logger, y as getRuntimeEnv } from "./access-ePqdQqkV.js";
4
4
  import { n as markFastPathApplied, t as isBulkCommitActive } from "./fastlane-CkBXZ_3K.js";
5
5
  import { i as isFragmentType, r as STATIC_CHILDREN } from "./jsx-C3IECePB.js";
6
- import { d as getCurrentContextFrame, h as markVNodeTreeWithContextFrame, i as mountInstanceInline, n as createComponentInstance, o as renderComponentInline, p as getVNodeContextFrame, t as captureInlineRenderSnapshot, y as withContext } from "./component-internal-TurioKgs.js";
6
+ import { d as getCurrentContextFrame, h as markVNodeTreeWithContextFrame, i as mountInstanceInline, n as createComponentInstance, o as renderComponentInline, p as getVNodeContextFrame, t as captureInlineRenderSnapshot, y as withContext } from "./component-internal-BuTG89T8.js";
7
7
  import { b as restoreDomCommitScope, c as enterDomCommitScope, p as getCurrentInstance } from "./component-scope-DzJEbYGE.js";
8
8
  import { t as cleanupComponent } from "./component-cleanup-oWN45rfa.js";
9
9
  import { c as getCurrentLifecycleCommitBatch, l as registerLifecycleRollback, p as isPromiseLike, r as discardLifecycleCommitBatch, s as flushLifecycleCommitBatch, t as beginLifecycleCommitBatch, u as registerLifecycleTransaction } from "./lifecycle-batch-CwHCDseR.js";
10
- import { _ as setControlBoundaryContextFrame, a as registerForStateTransaction, b as rerenderChildScope, c as captureForFallbackTransactionSnapshot, d as clearCaseDomUpdateState, f as clearShowDomUpdateState, g as evaluateShowState, h as evaluateCaseState, i as evaluateForState, l as captureForItemTransactionSnapshot, n as clearForDomUpdateState, o as rollbackForStateTransaction, t as beginForStateTransaction, v as createChildScope, y as disposeChildScope } from "./for-internal-BM8KbadU.js";
10
+ import { _ as setControlBoundaryContextFrame, a as registerForStateTransaction, b as rerenderChildScope, c as captureForFallbackTransactionSnapshot, d as clearCaseDomUpdateState, f as clearShowDomUpdateState, g as evaluateShowState, h as evaluateCaseState, i as evaluateForState, l as captureForItemTransactionSnapshot, n as clearForDomUpdateState, o as rollbackForStateTransaction, t as beginForStateTransaction, v as createChildScope, y as disposeChildScope } from "./for-internal-BnXrQbgo.js";
11
11
  import { n as createOwnedFineGrainedEffect, r as markFineGrainedEffectsDirtySource, t as createFineGrainedEffect } from "./effect-2IIWvUjG.js";
12
12
  import { C as getDelegatedHandlersForElement, D as updateDelegatedListener, E as removeDelegatedListener, S as getDelegatedHandlerForElement, T as isEventDelegationEnabled, _ as teardownNodeSubtree, a as elementReactivePropsCleanup, b as addFreshDelegatedListener, c as forEachElementReactivePropCleanup, d as removeAllListeners, f as removeElementReactiveProps, g as setFreshElementReactivePropCleanup, h as setDirectElementReactivePropCleanup, i as elementListeners, l as getElementReactivePropCleanupSize, m as replaceElementRefBookkeeping, n as cleanupInstanceIfPresent, o as elementRefs, p as removeElementRef, r as cleanupInstancesUnder, s as forEachDescendantNode, t as REACTIVE_CHILDREN_KEY, u as getElementReactivePropsCleanupMap, v as updateElementRef, w as isDelegatedEvent, x as clearDelegatedHandlersForElement, y as addDelegatedListener } from "./cleanup-CyY1ROBI.js";
13
+ import { a as isSSRPortalHydrationAnchor } from "./portal-w3oQ6oQU.js";
13
14
  import { n as getPublicAttributeName, t as sanitizeCssValue } from "./css-BuPaTOfe.js";
14
- import { a as isSafeHref, i as ROUTE_ROOT_COMPONENT } from "./csp-nonce-DYspaK0L.js";
15
+ import { a as isSafeHref, i as ROUTE_ROOT_COMPONENT } from "./csp-nonce-BUsz0Em7.js";
15
16
  //#region src/runtime/error-boundary.ts
16
17
  function createBoundaryReset(instance) {
17
18
  return () => {
@@ -1820,6 +1821,12 @@ function performFullReconciliation(parent, newChildren, oldKeyMap) {
1820
1821
  function isFragmentVNode(node) {
1821
1822
  return _isDOMElement(node) && isFragmentType(node.type);
1822
1823
  }
1824
+ function isTransparentComponentResult(result) {
1825
+ return Array.isArray(result) || _isDOMElement(result) && (result.type === __CONTROL_BOUNDARY__ || hasTransparentComponentResult(result.type) || isFragmentVNode(result));
1826
+ }
1827
+ function isTransparentComponentRangeResult(result) {
1828
+ return Array.isArray(result) || isFragmentVNode(result);
1829
+ }
1823
1830
  function normalizeComponentChildren(result) {
1824
1831
  if (result === null || result === void 0 || result === false) return [];
1825
1832
  if (Array.isArray(result)) {
@@ -2569,357 +2576,410 @@ function canReuseStaticSubtree(el, vnode) {
2569
2576
  return true;
2570
2577
  }
2571
2578
  //#endregion
2572
- //#region src/renderer/element-children.ts
2573
- const rendererReactiveChildDOMHost = {
2574
- createDOMNode: (node, parentNamespace) => getRendererDOMHost().createDOMNode(node, parentNamespace),
2575
- updateElementChildren: (el, children, forceUpdate) => updateElementChildren$1(el, children, forceUpdate)
2576
- };
2577
- function updateElementChildren$1(el, children, forceUpdate = false) {
2578
- const directControlBoundary = getDirectControlBoundaryVNode(children);
2579
- if (directControlBoundary) {
2580
- const controlState = getControlBoundaryState(directControlBoundary);
2581
- if (!controlState) throw new Error("[updateElementChildren] Control boundary missing internal state");
2582
- registerControlBoundaryCommitOwner(el, controlState);
2583
- commitForBoundaryChildren(el, controlState, evaluateControlBoundaryState(controlState));
2584
- return;
2585
- }
2586
- clearControlBoundaryCommitOwner(el);
2587
- if (children === null || children === void 0) {
2588
- for (let n = el.firstChild; n;) {
2589
- const next = n.nextSibling;
2590
- teardownNodeSubtree(n);
2591
- n = next;
2579
+ //#region src/renderer/hydration-listener-transaction.ts
2580
+ let currentTransaction = null;
2581
+ function beginHydrationListenerTransaction() {
2582
+ const transaction = {
2583
+ parent: currentTransaction,
2584
+ stages: [],
2585
+ active: true
2586
+ };
2587
+ currentTransaction = transaction;
2588
+ return transaction;
2589
+ }
2590
+ function stageHydrationListener(stage) {
2591
+ if (!currentTransaction?.active) return false;
2592
+ currentTransaction.stages.push(stage);
2593
+ return true;
2594
+ }
2595
+ function hasStagedHydrationListener(target, eventName, capture) {
2596
+ return !!currentTransaction?.stages.some((stage) => stage.target === target && stage.eventName === eventName && stage.capture === capture);
2597
+ }
2598
+ function closeTransaction(transaction) {
2599
+ if (!transaction.active) return false;
2600
+ transaction.active = false;
2601
+ currentTransaction = transaction.parent;
2602
+ return true;
2603
+ }
2604
+ function commitHydrationListenerTransaction(transaction) {
2605
+ if (!closeTransaction(transaction)) return;
2606
+ const stages = [...transaction.stages].sort((left, right) => {
2607
+ if (left.kind !== right.kind) return left.kind === "delegated" ? -1 : 1;
2608
+ return left.eventName.localeCompare(right.eventName);
2609
+ });
2610
+ const published = [];
2611
+ try {
2612
+ for (const stage of stages) {
2613
+ stage.publish();
2614
+ published.push(stage);
2592
2615
  }
2593
- el.textContent = "";
2594
- return;
2595
- }
2596
- if (!Array.isArray(children) && isFragmentVNode(children)) {
2597
- updateUnkeyedChildren(el, normalizeComponentChildren(children), forceUpdate);
2598
- return;
2616
+ } catch (error) {
2617
+ for (let index = published.length - 1; index >= 0; index -= 1) try {
2618
+ published[index].rollback();
2619
+ } catch {}
2620
+ throw error;
2599
2621
  }
2600
- if (!Array.isArray(children) && (typeof children === "string" || typeof children === "number")) {
2601
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2602
- const s = String(children);
2603
- const t = el.firstChild;
2604
- if (t.data !== s) t.data = s;
2605
- } else {
2606
- for (let n = el.firstChild; n;) {
2607
- const next = n.nextSibling;
2608
- teardownNodeSubtree(n);
2609
- n = next;
2610
- }
2611
- el.textContent = String(children);
2612
- }
2613
- return;
2622
+ }
2623
+ function discardHydrationListenerTransaction(transaction) {
2624
+ if (!closeTransaction(transaction)) return;
2625
+ for (let index = transaction.stages.length - 1; index >= 0; index -= 1) try {
2626
+ transaction.stages[index].rollback();
2627
+ } catch {}
2628
+ }
2629
+ function getCurrentHydrationListenerTransaction() {
2630
+ return currentTransaction?.active ? currentTransaction : null;
2631
+ }
2632
+ //#endregion
2633
+ //#region src/renderer/prop-bindings.ts
2634
+ const reactivePropRegistry = /* @__PURE__ */ new Set();
2635
+ const hydrationDirectListeners = /* @__PURE__ */ new WeakMap();
2636
+ let hydrationDirectListenerDepth = 0;
2637
+ function beginHydrationDirectListenerMode() {
2638
+ hydrationDirectListenerDepth += 1;
2639
+ }
2640
+ function endHydrationDirectListenerMode() {
2641
+ hydrationDirectListenerDepth = Math.max(0, hydrationDirectListenerDepth - 1);
2642
+ }
2643
+ function markHydrationDirectListener(element, listenerKey) {
2644
+ let listeners = hydrationDirectListeners.get(element);
2645
+ if (!listeners) {
2646
+ listeners = /* @__PURE__ */ new Set();
2647
+ hydrationDirectListeners.set(element, listeners);
2614
2648
  }
2615
- if (Array.isArray(children)) {
2616
- const normalizedChildren = normalizeComponentChildren(children);
2617
- if (trySyncScalarChildSequenceInPlace(el, normalizedChildren, rendererReactiveChildDOMHost)) {
2618
- keyedElements.delete(el);
2619
- return;
2620
- }
2621
- if (normalizedChildren.some(isControlBoundaryVNode)) {
2622
- updateMixedControlChildren(el, normalizedChildren, forceUpdate);
2623
- keyedElements.delete(el);
2624
- return;
2625
- }
2626
- if (hasKeyedVNodeChildren(normalizedChildren)) {
2627
- const newKeyMap = reconcileKeyedChildren(el, normalizedChildren, getOrBuildDomKeyMap(el));
2628
- keyedElements.set(el, newKeyMap);
2629
- return;
2630
- }
2631
- if (isBulkTextFastPathEligible(el, normalizedChildren)) {
2632
- performBulkTextReplace(el, normalizedChildren);
2633
- keyedElements.delete(el);
2649
+ listeners.add(listenerKey);
2650
+ }
2651
+ function isHydrationDirectListener(element, listenerKey) {
2652
+ return hydrationDirectListeners.get(element)?.has(listenerKey) ?? false;
2653
+ }
2654
+ function clearHydrationDirectListener(element, listenerKey) {
2655
+ const listeners = hydrationDirectListeners.get(element);
2656
+ listeners?.delete(listenerKey);
2657
+ if (listeners?.size === 0) hydrationDirectListeners.delete(element);
2658
+ }
2659
+ function addTrackedListener(el, eventName, handler, capture = false, fresh = false, forceDirect = false) {
2660
+ const effectiveForceDirect = forceDirect || hydrationDirectListenerDepth > 0;
2661
+ const useDelegation = !effectiveForceDirect && !capture && isEventDelegationEnabled() && isDelegatedEvent(eventName);
2662
+ const listenerKey = getEventListenerKey(eventName, capture);
2663
+ if (effectiveForceDirect && isHydrationDirectListener(el, listenerKey)) {
2664
+ const existing = elementListeners.get(el)?.get(listenerKey);
2665
+ if (existing) {
2666
+ existing.updateHandler?.(handler);
2667
+ existing.original = handler;
2634
2668
  return;
2635
2669
  }
2636
- updateUnkeyedChildren(el, normalizedChildren, forceUpdate);
2637
- return;
2638
2670
  }
2639
- if (_isDOMElement(children)) {
2640
- updateUnkeyedChildren(el, [children], forceUpdate);
2671
+ if (!effectiveForceDirect && !hasStagedHydrationListener(el, eventName, capture) && stageHydrationListener({
2672
+ kind: "direct",
2673
+ target: el,
2674
+ eventName,
2675
+ capture,
2676
+ publish: () => addTrackedListener(el, eventName, handler, capture, fresh, true),
2677
+ rollback: () => {
2678
+ removeTrackedListener(el, eventName, capture);
2679
+ clearHydrationDirectListener(el, listenerKey);
2680
+ }
2681
+ })) return;
2682
+ if (useDelegation) {
2683
+ if (fresh) addFreshDelegatedListener(el, eventName, handler, handler, void 0);
2684
+ else addDelegatedListener(el, eventName, handler, handler, void 0);
2641
2685
  return;
2642
2686
  }
2643
- for (let n = el.firstChild; n;) {
2644
- const next = n.nextSibling;
2645
- teardownNodeSubtree(n);
2646
- n = next;
2687
+ const options = getEventListenerOptions(eventName, capture);
2688
+ const mutableHandler = createMutableWrappedHandler(handler, true);
2689
+ const trackedHandler = mutableHandler.handler;
2690
+ if (options !== void 0) el.addEventListener(eventName, trackedHandler, options);
2691
+ else el.addEventListener(eventName, trackedHandler);
2692
+ if (effectiveForceDirect) {
2693
+ if (!capture && getDelegatedHandlerForElement(el, eventName)) removeDelegatedListener(el, eventName);
2694
+ markHydrationDirectListener(el, listenerKey);
2647
2695
  }
2648
- el.textContent = "";
2649
- const dom = getRendererDOMHost().createDOMNode(children);
2650
- if (dom) el.appendChild(dom);
2696
+ if (!elementListeners.has(el)) elementListeners.set(el, /* @__PURE__ */ new Map());
2697
+ elementListeners.get(el).set(listenerKey, {
2698
+ handler: trackedHandler,
2699
+ original: handler,
2700
+ eventName,
2701
+ options,
2702
+ isDelegated: false,
2703
+ updateHandler: mutableHandler?.updateHandler
2704
+ });
2651
2705
  }
2652
- function hasKeyedVNodeChildren(children) {
2653
- for (let i = 0; i < children.length; i++) if (extractKey(children[i]) !== void 0) return true;
2654
- return false;
2706
+ function removeTrackedListener(el, eventName, capture) {
2707
+ if (!capture && getDelegatedHandlerForElement(el, eventName)) {
2708
+ removeDelegatedListener(el, eventName);
2709
+ return;
2710
+ }
2711
+ const listenerKey = getEventListenerKey(eventName, capture);
2712
+ const entry = elementListeners.get(el)?.get(listenerKey);
2713
+ if (!entry) return;
2714
+ if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
2715
+ else el.removeEventListener(entry.eventName, entry.handler);
2716
+ const listeners = elementListeners.get(el);
2717
+ listeners?.delete(listenerKey);
2718
+ if (listeners?.size === 0) elementListeners.delete(el);
2655
2719
  }
2656
- function isControlBoundaryVNode(child) {
2657
- return _isDOMElement(child) && child.type === __CONTROL_BOUNDARY__;
2720
+ function markReactivePropsDirtySource(source) {
2721
+ markFineGrainedEffectsDirtySource(source);
2658
2722
  }
2659
- function removeRangeAtCursor(parent, cursor) {
2660
- if (!isRangeStart(cursor)) return cursor;
2661
- const end = findRangeEnd(cursor);
2662
- if (!end) return cursor;
2663
- const range = {
2664
- start: cursor,
2665
- end,
2666
- single: false
2723
+ function setupReactiveProp(el, propName, propFn, tagName) {
2724
+ const descriptor = {
2725
+ el,
2726
+ propName,
2727
+ propFn,
2728
+ tagName,
2729
+ lastClassTokens: null
2667
2730
  };
2668
- const next = end.nextSibling;
2669
- removeRange(range, (node) => {
2670
- if (node === range.start || node === range.end) {
2671
- node.parentNode?.removeChild(node);
2672
- return;
2731
+ let effectHandle = null;
2732
+ reactivePropRegistry.add(descriptor);
2733
+ effectHandle = createFineGrainedEffect({
2734
+ lane: "reactive",
2735
+ compute: () => descriptor.propFn(),
2736
+ commit: (value, previousValue) => {
2737
+ applyScalarPropValue(el, propName, value, tagName, previousValue, descriptor);
2738
+ },
2739
+ equals: (previousValue, nextValue) => {
2740
+ if (Object.is(previousValue, nextValue)) return true;
2741
+ return false;
2742
+ },
2743
+ onError: (err) => {
2744
+ if (getRuntimeEnv().NODE_ENV !== "production") logger.warn("[Askr] Reactive prop update failed:", err);
2673
2745
  }
2674
- teardownNodeSubtree(node);
2675
- node.parentNode?.removeChild(node);
2676
2746
  });
2677
- return next;
2678
- }
2679
- function updateMixedControlChildren(parent, children, forceUpdate) {
2680
- clearControlBoundaryCommitOwner(parent);
2681
- const parentNamespace = getParentNamespace(parent);
2682
- const domHost = getRendererDOMHost();
2683
- let cursor = parent.firstChild;
2684
- for (const child of children) {
2685
- if (isControlBoundaryVNode(child)) {
2686
- const controlState = getControlBoundaryState(child);
2687
- if (!controlState) throw new Error("[updateElementChildren] Control boundary missing internal state");
2688
- const childVNodes = evaluateControlBoundaryState(controlState);
2689
- const cursorAfterBoundary = cursor ? isRangeStart(cursor) ? findRangeEnd(cursor)?.nextSibling ?? cursor.nextSibling : cursor.nextSibling : null;
2690
- const ranges = syncControlBoundaryInMixedParent(parent, controlState, childVNodes, cursor);
2691
- if (controlState.kind === "for") {
2692
- const last = ranges[ranges.length - 1];
2693
- cursor = last ? last.end.nextSibling : cursorAfterBoundary?.parentNode === parent ? cursorAfterBoundary : cursor?.parentNode === parent ? cursor : null;
2694
- continue;
2695
- }
2696
- for (const range of ranges) if (range.start.parentNode !== parent) moveRange(parent, range, cursor?.parentNode === parent ? cursor : null);
2697
- else if (cursor?.parentNode === parent) moveRange(parent, range, cursor);
2698
- const last = ranges[ranges.length - 1];
2699
- if (last) {
2700
- const cursorWasReplaced = cursor && !ranges.some((range) => range.start === cursor);
2701
- if (!cursor?.parentNode) cursor = last.end.nextSibling;
2702
- else if (cursorWasReplaced && isRangeStart(cursor)) cursor = findRangeEnd(cursor)?.nextSibling ?? last.end.nextSibling;
2703
- else if (isRangeStart(cursor)) cursor = findRangeEnd(cursor)?.nextSibling ?? last.end.nextSibling;
2704
- else cursor = last.end.nextSibling;
2705
- } else if (cursor?.parentNode !== parent) cursor = cursorAfterBoundary?.parentNode === parent ? cursorAfterBoundary : null;
2706
- continue;
2747
+ const cleanup = () => {
2748
+ reactivePropRegistry.delete(descriptor);
2749
+ effectHandle?.cleanup();
2750
+ effectHandle = null;
2751
+ };
2752
+ const updateFn = (nextFn) => {
2753
+ if (!effectHandle) return;
2754
+ descriptor.propFn = nextFn;
2755
+ try {
2756
+ effectHandle.updateCompute(nextFn);
2757
+ } catch (err) {
2758
+ if (getRuntimeEnv().NODE_ENV !== "production") logger.warn("[Askr] Reactive prop update failed:", err);
2707
2759
  }
2708
- if (cursor) cursor = removeRangeAtCursor(parent, cursor);
2709
- if (cursor && (typeof child === "string" || typeof child === "number") && cursor.nodeType === 3) {
2710
- cursor.data = String(child);
2711
- cursor = cursor.nextSibling;
2712
- continue;
2760
+ };
2761
+ return {
2762
+ cleanup,
2763
+ updateFn
2764
+ };
2765
+ }
2766
+ function getOrCreateReactivePropsCleanupMap(el) {
2767
+ return getElementReactivePropsCleanupMap(el, true);
2768
+ }
2769
+ /** @internal Create a standalone reactive prop entry for rollback restoration. */
2770
+ function createReactivePropCleanupEntry(el, propName, propFn, tagName) {
2771
+ const reactive = setupReactiveProp(el, propName, propFn, tagName);
2772
+ return {
2773
+ cleanup: reactive.cleanup,
2774
+ updateFn: (nextValue) => {
2775
+ reactive.updateFn(nextValue);
2776
+ },
2777
+ restoreFn: (nextValue) => createReactivePropCleanupEntry(el, propName, nextValue, tagName),
2778
+ fnRef: propFn
2779
+ };
2780
+ }
2781
+ function hasTrackedElementPropBindings(el) {
2782
+ const existingListeners = elementListeners.get(el);
2783
+ return existingListeners !== void 0 && existingListeners.size > 0 || getElementReactivePropCleanupSize(el) > 0;
2784
+ }
2785
+ function hasAnyElementBindingState(el) {
2786
+ return hasTrackedElementPropBindings(el) || getDelegatedHandlersForElement(el) !== void 0 || elementRefs.has(el);
2787
+ }
2788
+ function applyMatchingElementBindings(el, props) {
2789
+ if (Object.prototype.hasOwnProperty.call(props, "ref")) updateElementRef(el, props.ref);
2790
+ for (const key in props) {
2791
+ const eventProp = parseEventProp(key);
2792
+ if (!eventProp) continue;
2793
+ const value = props[key];
2794
+ if (typeof value === "function") addTrackedListener(el, eventProp.eventName, value, eventProp.capture);
2795
+ }
2796
+ }
2797
+ /** @internal Attach stateful bindings to a newly cloned blueprint element. */
2798
+ function applyFreshElementBindings(el, props) {
2799
+ if (Object.prototype.hasOwnProperty.call(props, "ref")) updateElementRef(el, props.ref);
2800
+ for (const key in props) {
2801
+ const eventProp = parseEventProp(key);
2802
+ if (eventProp) {
2803
+ const value = props[key];
2804
+ if (typeof value === "function") addTrackedListener(el, eventProp.eventName, value, eventProp.capture, true);
2713
2805
  }
2714
- if (cursor instanceof Element && _isDOMElement(child) && typeof child.type === "string" && tagsEqualIgnoreCase(cursor.tagName, child.type)) {
2715
- domHost.updateElementFromVnode(cursor, child, true, forceUpdate);
2716
- cursor = cursor.nextSibling;
2806
+ }
2807
+ }
2808
+ function applyPropsToElement$1(el, props, tagName, isHydrationSkipped) {
2809
+ if (isHydrationSkipped(el)) return;
2810
+ for (const key in props) {
2811
+ const value = props[key];
2812
+ if (key === "ref") {
2813
+ updateElementRef(el, value);
2717
2814
  continue;
2718
2815
  }
2719
- if (cursor && _isDOMElement(child) && typeof child.type === "function") {
2720
- const synced = domHost.syncComponentElement(cursor, child, child.type, (child.props ?? {}) || {}, parentNamespace, forceUpdate);
2721
- if (synced) {
2722
- cursor = synced.nextSibling;
2723
- continue;
2724
- }
2725
- }
2726
- const created = domHost.createDOMNode(child, parentNamespace);
2727
- if (!created) {
2728
- cursor = cursor?.nextSibling ?? null;
2816
+ if (isSkippedProp(key)) continue;
2817
+ if (value === void 0 || value === null || value === false) continue;
2818
+ const eventProp = parseEventProp(key);
2819
+ if (eventProp) {
2820
+ addTrackedListener(el, eventProp.eventName, value, eventProp.capture);
2729
2821
  continue;
2730
2822
  }
2731
- const materialized = createDetachedRange(created);
2732
- const old = cursor;
2733
- moveRange(parent, materialized.range, old);
2734
- if (old) {
2735
- teardownNodeSubtree(old);
2736
- old.parentNode?.removeChild(old);
2737
- }
2738
- cursor = materialized.range.end.nextSibling;
2739
- }
2740
- while (cursor) {
2741
- const next = removeRangeAtCursor(parent, cursor);
2742
- cursor = next === cursor ? cursor.nextSibling : next;
2743
- }
2744
- }
2745
- function isEmptyChild(child) {
2746
- return child === null || child === void 0 || child === false;
2747
- }
2748
- function getOrBuildDomKeyMap(parent) {
2749
- let keyMap = keyedElements.get(parent);
2750
- if (!keyMap) {
2751
- keyMap = /* @__PURE__ */ new Map();
2752
- for (let child = parent.firstElementChild; child; child = child.nextElementSibling) {
2753
- const key = getMaterializedKey(child);
2754
- if (key !== void 0) keyMap.set(key, child);
2823
+ if (typeof value === "function" && key !== "ref") {
2824
+ getOrCreateReactivePropsCleanupMap(el).set(key, createReactivePropCleanupEntry(el, key, value, tagName));
2825
+ continue;
2755
2826
  }
2756
- if (keyMap.size > 0) keyedElements.set(parent, keyMap);
2827
+ applyScalarPropValue(el, key, value, tagName);
2757
2828
  }
2758
- return keyMap.size > 0 ? keyMap : void 0;
2759
2829
  }
2760
- function updateUnkeyedChildren(parent, newChildren, forceUpdate = false) {
2761
- const parentNamespace = getParentNamespace(parent);
2762
- const domHost = getRendererDOMHost();
2763
- const trySyncComponentChild = (currentDom, next) => {
2764
- if (typeof next.type !== "function") return null;
2765
- return domHost.syncComponentElement(currentDom, next, next.type, (next.props ?? {}) || {}, parentNamespace, forceUpdate);
2766
- };
2767
- const hasText = newChildren.some((c) => typeof c === "string" || typeof c === "number");
2768
- const hasElements = newChildren.some((c) => _isDOMElement(c));
2769
- const hasEmptyChildren = newChildren.some(isEmptyChild);
2770
- const hasComponentChildren = newChildren.some((c) => _isDOMElement(c) && typeof c.type === "function");
2771
- const hasNonElementDomChildren = parent.childNodes.length !== parent.children.length;
2772
- if (!hasEmptyChildren && !hasText && !hasComponentChildren && !hasNonElementDomChildren && hasElements && parent.children.length === newChildren.length) {
2773
- const c = parent.children;
2774
- for (let i = 0; i < newChildren.length; i++) {
2775
- const next = newChildren[i];
2776
- const current = c[i];
2777
- if (!current || next === void 0) continue;
2778
- if (_isDOMElement(next) && typeof next.type === "string") if (tagsEqualIgnoreCase(current.tagName, next.type)) domHost.updateElementFromVnode(current, next, true, forceUpdate);
2779
- else {
2780
- const dom = domHost.createDOMNode(next, parentNamespace);
2781
- if (dom) {
2782
- teardownNodeSubtree(current);
2783
- parent.replaceChild(dom, current);
2784
- }
2785
- }
2786
- else if (_isDOMElement(next)) {
2787
- if (trySyncControlBoundaryChild(parent, current, next)) continue;
2788
- if (!trySyncComponentChild(current, next)) {
2789
- const dom = domHost.createDOMNode(next, parentNamespace);
2790
- if (dom) {
2791
- teardownNodeSubtree(current);
2792
- if (current.parentNode === parent) parent.replaceChild(dom, current);
2793
- else if (dom.parentNode !== parent) parent.insertBefore(dom, parent.children[i] ?? null);
2794
- }
2795
- }
2830
+ function syncElementPropBindings(el, domVNode, props, usesReactiveChildren) {
2831
+ const existingListeners = elementListeners.get(el);
2832
+ const existingReactiveProps = getElementReactivePropsCleanupMap(el);
2833
+ let desiredListenerKeys = null;
2834
+ let desiredDelegatedEventNames = null;
2835
+ let desiredReactivePropNames = null;
2836
+ if (usesReactiveChildren) (desiredReactivePropNames ??= /* @__PURE__ */ new Set()).add(REACTIVE_CHILDREN_KEY);
2837
+ for (const key in props) {
2838
+ const value = props[key];
2839
+ if (key === "ref") continue;
2840
+ if (isSkippedProp(key)) continue;
2841
+ const eventProp = parseEventProp(key);
2842
+ const eventName = eventProp?.eventName;
2843
+ const eventCapture = eventProp?.capture ?? false;
2844
+ const listenerKey = eventName === void 0 ? null : getEventListenerKey(eventName, eventCapture);
2845
+ if (value === void 0 || value === null || value === false) {
2846
+ if (listenerKey && existingListeners?.has(listenerKey)) {
2847
+ const entry = existingListeners.get(listenerKey);
2848
+ if (entry.isDelegated) removeDelegatedListener(el, entry.eventName);
2849
+ else if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
2850
+ else el.removeEventListener(entry.eventName, entry.handler);
2851
+ existingListeners.delete(listenerKey);
2796
2852
  } else {
2797
- const dom = domHost.createDOMNode(next, parentNamespace);
2798
- if (dom) {
2799
- teardownNodeSubtree(current);
2800
- parent.replaceChild(dom, current);
2801
- }
2853
+ const entry = existingReactiveProps?.get(key);
2854
+ if (entry) {
2855
+ entry.cleanup();
2856
+ existingReactiveProps?.delete(key);
2857
+ } else applyScalarPropValue(el, key, value, domVNode.type);
2802
2858
  }
2859
+ continue;
2803
2860
  }
2804
- return;
2805
- }
2806
- const existing = Array.from(parent.children);
2807
- if (hasText || hasComponentChildren || hasEmptyChildren || hasNonElementDomChildren) {
2808
- const allNodes = Array.from(parent.childNodes);
2809
- const max = Math.max(allNodes.length, newChildren.length);
2810
- for (let i = 0; i < max; i++) {
2811
- const currentNode = allNodes[i];
2812
- const next = newChildren[i];
2813
- const nextIsEmpty = isEmptyChild(next);
2814
- if (nextIsEmpty && currentNode) {
2815
- teardownNodeSubtree(currentNode);
2816
- currentNode.remove();
2861
+ if (typeof value === "function" && !eventProp && key !== "ref") {
2862
+ const existingEntry = existingReactiveProps?.get(key);
2863
+ if (existingReactiveProps && existingReactiveProps.size > 0) (desiredReactivePropNames ??= /* @__PURE__ */ new Set()).add(key);
2864
+ if (existingEntry && existingEntry.fnRef === value) continue;
2865
+ if (existingEntry?.updateFn) {
2866
+ existingEntry.updateFn(value);
2867
+ existingEntry.fnRef = value;
2817
2868
  continue;
2818
2869
  }
2819
- if (!currentNode && !nextIsEmpty) {
2820
- const dom = domHost.createDOMNode(next, parentNamespace);
2821
- if (dom) parent.appendChild(dom);
2870
+ if (existingEntry) existingEntry.cleanup();
2871
+ getOrCreateReactivePropsCleanupMap(el).set(key, createReactivePropCleanupEntry(el, key, value, domVNode.type));
2872
+ continue;
2873
+ }
2874
+ const existingReactiveEntry = existingReactiveProps?.get(key);
2875
+ if (existingReactiveEntry) {
2876
+ existingReactiveEntry.cleanup();
2877
+ existingReactiveProps?.delete(key);
2878
+ }
2879
+ if (eventProp && listenerKey) {
2880
+ const eventName = eventProp.eventName;
2881
+ const eventCapture = eventProp.capture;
2882
+ const preserveHydrationDirect = existingListeners?.get(listenerKey)?.isDelegated === false && isHydrationDirectListener(el, listenerKey);
2883
+ if (getCurrentHydrationListenerTransaction() && !existingListeners?.has(listenerKey) && !getDelegatedHandlerForElement(el, eventName) && !isHydrationDirectListener(el, listenerKey)) {
2884
+ if (!hasStagedHydrationListener(el, eventName, eventCapture)) stageHydrationListener({
2885
+ kind: "direct",
2886
+ target: el,
2887
+ eventName,
2888
+ capture: eventCapture,
2889
+ publish: () => addTrackedListener(el, eventName, value, eventCapture, false, true),
2890
+ rollback: () => {
2891
+ removeTrackedListener(el, eventName, eventCapture);
2892
+ clearHydrationDirectListener(el, listenerKey);
2893
+ }
2894
+ });
2822
2895
  continue;
2823
2896
  }
2824
- if (!currentNode || nextIsEmpty) continue;
2825
- if (typeof next === "string" || typeof next === "number") if (currentNode.nodeType === 3) currentNode.data = String(next);
2826
- else {
2827
- const textNode = document.createTextNode(String(next));
2828
- teardownNodeSubtree(currentNode);
2829
- parent.replaceChild(textNode, currentNode);
2897
+ if (hydrationDirectListenerDepth > 0 && !existingListeners?.has(listenerKey) && !getDelegatedHandlerForElement(el, eventName)) {
2898
+ addTrackedListener(el, eventName, value, eventCapture, false, true);
2899
+ continue;
2830
2900
  }
2831
- else if (_isDOMElement(next)) if (currentNode.nodeType === 1) {
2832
- const currentEl = currentNode;
2833
- if (typeof next.type === "string") if (tagsEqualIgnoreCase(currentEl.tagName, next.type)) domHost.updateElementFromVnode(currentEl, next, true, forceUpdate);
2834
- else {
2835
- const dom = domHost.createDOMNode(next, parentNamespace);
2836
- if (dom) {
2837
- teardownNodeSubtree(currentEl);
2838
- if (currentNode.parentNode === parent) parent.replaceChild(dom, currentNode);
2839
- else if (dom.parentNode !== parent) parent.insertBefore(dom, parent.childNodes[i] ?? null);
2840
- }
2841
- }
2842
- else {
2843
- if (trySyncControlBoundaryChild(parent, currentNode, next)) continue;
2844
- if (!trySyncComponentChild(currentEl, next)) {
2845
- const dom = domHost.createDOMNode(next, parentNamespace);
2846
- if (dom) {
2847
- teardownNodeSubtree(currentEl);
2848
- parent.replaceChild(dom, currentNode);
2849
- }
2850
- }
2901
+ const useDelegation = !preserveHydrationDirect && !eventCapture && isEventDelegationEnabled() && isDelegatedEvent(eventName);
2902
+ if (useDelegation) (desiredDelegatedEventNames ??= /* @__PURE__ */ new Set()).add(eventName);
2903
+ if (useDelegation) {
2904
+ const existingDelegated = getDelegatedHandlerForElement(el, eventName);
2905
+ if (existingDelegated?.original === value) continue;
2906
+ if (existingDelegated && updateDelegatedListener(el, eventName, value, value, void 0)) continue;
2907
+ addDelegatedListener(el, eventName, value, value, void 0);
2908
+ continue;
2909
+ }
2910
+ (desiredListenerKeys ??= /* @__PURE__ */ new Set()).add(listenerKey);
2911
+ const existing = existingListeners?.get(listenerKey);
2912
+ if (existing && existing.original === value) continue;
2913
+ if (existing) {
2914
+ if (useDelegation && existing.isDelegated && updateDelegatedListener(el, eventName, value, value, void 0)) {
2915
+ existing.handler = value;
2916
+ existing.original = value;
2917
+ existing.options = void 0;
2918
+ continue;
2851
2919
  }
2852
- } else {
2853
- if (typeof next.type === "function" && trySyncComponentChild(currentNode, next)) continue;
2854
- const dom = domHost.createDOMNode(next, parentNamespace);
2855
- if (dom) {
2856
- teardownNodeSubtree(currentNode);
2857
- parent.replaceChild(dom, currentNode);
2920
+ if (!useDelegation && !existing.isDelegated && existing.updateHandler) {
2921
+ existing.updateHandler(value);
2922
+ existing.original = value;
2923
+ continue;
2858
2924
  }
2925
+ if (existing.isDelegated) removeDelegatedListener(el, existing.eventName);
2926
+ else if (existing.options !== void 0) el.removeEventListener(existing.eventName, existing.handler, existing.options);
2927
+ else el.removeEventListener(existing.eventName, existing.handler);
2859
2928
  }
2860
- }
2861
- return;
2929
+ const options = getEventListenerOptions(eventName, eventCapture);
2930
+ const mutableHandler = createMutableWrappedHandler(value, true);
2931
+ const trackedHandler = mutableHandler.handler;
2932
+ if (options !== void 0) el.addEventListener(eventName, trackedHandler, options);
2933
+ else el.addEventListener(eventName, trackedHandler);
2934
+ const listenerEntry = {
2935
+ handler: trackedHandler,
2936
+ original: value,
2937
+ eventName,
2938
+ options,
2939
+ isDelegated: false,
2940
+ updateHandler: mutableHandler?.updateHandler
2941
+ };
2942
+ if (!elementListeners.has(el)) elementListeners.set(el, /* @__PURE__ */ new Map());
2943
+ elementListeners.get(el).set(listenerKey, listenerEntry);
2944
+ } else applyScalarPropValue(el, key, value, domVNode.type);
2862
2945
  }
2863
- if (newChildren.length === 1 && existing.length === 0 && parent.childNodes.length === 1) {
2864
- const firstNewChild = newChildren[0];
2865
- const firstExisting = parent.firstChild;
2866
- if ((typeof firstNewChild === "string" || typeof firstNewChild === "number") && firstExisting?.nodeType === 3) {
2867
- firstExisting.data = String(firstNewChild);
2868
- return;
2869
- }
2946
+ removeStaleAttributes(el, domVNode, props);
2947
+ if (existingListeners && existingListeners.size > 0) if (desiredListenerKeys === null) {
2948
+ existingListeners.forEach((entry) => {
2949
+ if (entry.isDelegated) removeDelegatedListener(el, entry.eventName);
2950
+ else if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
2951
+ else el.removeEventListener(entry.eventName, entry.handler);
2952
+ });
2953
+ elementListeners.delete(el);
2954
+ } else {
2955
+ existingListeners.forEach((entry, listenerKey) => {
2956
+ if (!desiredListenerKeys.has(listenerKey)) {
2957
+ if (entry.isDelegated) removeDelegatedListener(el, entry.eventName);
2958
+ else if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
2959
+ else el.removeEventListener(entry.eventName, entry.handler);
2960
+ existingListeners.delete(listenerKey);
2961
+ }
2962
+ });
2963
+ if (existingListeners.size === 0) elementListeners.delete(el);
2870
2964
  }
2871
- if (existing.length === 0 && parent.childNodes.length > 0) {
2872
- for (let n = parent.firstChild; n;) {
2873
- const next = n.nextSibling;
2874
- teardownNodeSubtree(n);
2875
- n = next;
2876
- }
2877
- parent.textContent = "";
2965
+ const delegatedHandlers = getDelegatedHandlersForElement(el);
2966
+ if (delegatedHandlers && delegatedHandlers.size > 0) {
2967
+ if (desiredDelegatedEventNames === null) for (const eventName of delegatedHandlers.keys()) removeDelegatedListener(el, eventName);
2968
+ else for (const eventName of delegatedHandlers.keys()) if (!desiredDelegatedEventNames.has(eventName)) removeDelegatedListener(el, eventName);
2878
2969
  }
2879
- const max = Math.max(existing.length, newChildren.length);
2880
- for (let i = 0; i < max; i++) {
2881
- const current = existing[i];
2882
- const next = newChildren[i];
2883
- const nextIsEmpty = isEmptyChild(next);
2884
- if (nextIsEmpty && current) {
2885
- teardownNodeSubtree(current);
2886
- current.remove();
2887
- continue;
2888
- }
2889
- if (!current && !nextIsEmpty) {
2890
- const dom = domHost.createDOMNode(next, parentNamespace);
2891
- if (dom) parent.appendChild(dom);
2892
- continue;
2893
- }
2894
- if (!current || nextIsEmpty) continue;
2895
- if (typeof next === "string" || typeof next === "number") {
2896
- const textNode = document.createTextNode(String(next));
2897
- teardownNodeSubtree(current);
2898
- parent.replaceChild(textNode, current);
2899
- } else if (_isDOMElement(next)) {
2900
- if (typeof next.type === "string") if (tagsEqualIgnoreCase(current.tagName, next.type)) domHost.updateElementFromVnode(current, next, true, forceUpdate);
2901
- else {
2902
- const dom = domHost.createDOMNode(next, parentNamespace);
2903
- if (dom) {
2904
- teardownNodeSubtree(current);
2905
- parent.replaceChild(dom, current);
2906
- }
2907
- }
2908
- else if (!trySyncComponentChild(current, next)) {
2909
- const dom = domHost.createDOMNode(next, parentNamespace);
2910
- if (dom) {
2911
- teardownNodeSubtree(current);
2912
- if (current.parentNode === parent) parent.replaceChild(dom, current);
2913
- else if (dom.parentNode !== parent) parent.insertBefore(dom, parent.children[i] ?? null);
2914
- }
2915
- }
2916
- } else {
2917
- const dom = domHost.createDOMNode(next);
2918
- if (dom) {
2919
- teardownNodeSubtree(current);
2920
- parent.replaceChild(dom, current);
2970
+ if (existingReactiveProps && existingReactiveProps.size > 0) if (desiredReactivePropNames === null) {
2971
+ existingReactiveProps.forEach((entry) => {
2972
+ entry.cleanup();
2973
+ });
2974
+ elementReactivePropsCleanup.delete(el);
2975
+ } else {
2976
+ existingReactiveProps.forEach((entry, key) => {
2977
+ if (!desiredReactivePropNames.has(key)) {
2978
+ entry.cleanup();
2979
+ existingReactiveProps.delete(key);
2921
2980
  }
2922
- }
2981
+ });
2982
+ if (existingReactiveProps.size === 0) elementReactivePropsCleanup.delete(el);
2923
2983
  }
2924
2984
  }
2925
2985
  //#endregion
@@ -3204,471 +3264,467 @@ function runRetainedElementUpdate(element, cleanupRangeNode, update, onError, bi
3204
3264
  }
3205
3265
  }
3206
3266
  //#endregion
3207
- //#region src/renderer/hydration-listener-transaction.ts
3208
- let currentTransaction = null;
3209
- function beginHydrationListenerTransaction() {
3210
- const transaction = {
3211
- parent: currentTransaction,
3212
- stages: [],
3213
- active: true
3214
- };
3215
- currentTransaction = transaction;
3216
- return transaction;
3267
+ //#region src/renderer/intrinsic-hydration-adoption.ts
3268
+ let hydrationAdoptionDepth = 0;
3269
+ const adoptedHydrationHosts = /* @__PURE__ */ new WeakSet();
3270
+ function isHydrationAdoptionScopeActive() {
3271
+ return hydrationAdoptionDepth > 0;
3217
3272
  }
3218
- function stageHydrationListener(stage) {
3219
- if (!currentTransaction?.active) return false;
3220
- currentTransaction.stages.push(stage);
3221
- return true;
3273
+ function markHydrationHostAdopted(host) {
3274
+ adoptedHydrationHosts.add(host);
3222
3275
  }
3223
- function hasStagedHydrationListener(target, eventName, capture) {
3224
- return !!currentTransaction?.stages.some((stage) => stage.target === target && stage.eventName === eventName && stage.capture === capture);
3276
+ function isAdoptedHydrationHost(host) {
3277
+ return adoptedHydrationHosts.has(host);
3225
3278
  }
3226
- function closeTransaction(transaction) {
3227
- if (!transaction.active) return false;
3228
- transaction.active = false;
3229
- currentTransaction = transaction.parent;
3230
- return true;
3279
+ function canReconcileComponentHost(host, hasMatchingInstance) {
3280
+ return hasMatchingInstance || Boolean(host.__ASKR_INSTANCE) || Boolean(host.__ASKR_INSTANCES?.length) || isAdoptedHydrationHost(host) || isHydrationAdoptionScopeActive();
3231
3281
  }
3232
- function commitHydrationListenerTransaction(transaction) {
3233
- if (!closeTransaction(transaction)) return;
3234
- const stages = [...transaction.stages].sort((left, right) => {
3235
- if (left.kind !== right.kind) return left.kind === "delegated" ? -1 : 1;
3236
- return left.eventName.localeCompare(right.eventName);
3237
- });
3238
- const published = [];
3282
+ function withIntrinsicHydrationAdoption(work) {
3283
+ hydrationAdoptionDepth += 1;
3239
3284
  try {
3240
- for (const stage of stages) {
3241
- stage.publish();
3242
- published.push(stage);
3243
- }
3244
- } catch (error) {
3245
- for (let index = published.length - 1; index >= 0; index -= 1) try {
3246
- published[index].rollback();
3247
- } catch {}
3248
- throw error;
3285
+ return work();
3286
+ } finally {
3287
+ hydrationAdoptionDepth -= 1;
3249
3288
  }
3250
3289
  }
3251
- function discardHydrationListenerTransaction(transaction) {
3252
- if (!closeTransaction(transaction)) return;
3253
- for (let index = transaction.stages.length - 1; index >= 0; index -= 1) try {
3254
- transaction.stages[index].rollback();
3255
- } catch {}
3256
- }
3257
- function getCurrentHydrationListenerTransaction() {
3258
- return currentTransaction?.active ? currentTransaction : null;
3259
- }
3260
- //#endregion
3261
- //#region src/renderer/prop-bindings.ts
3262
- const reactivePropRegistry = /* @__PURE__ */ new Set();
3263
- const hydrationDirectListeners = /* @__PURE__ */ new WeakMap();
3264
- let hydrationDirectListenerDepth = 0;
3265
- function beginHydrationDirectListenerMode() {
3266
- hydrationDirectListenerDepth += 1;
3267
- }
3268
- function endHydrationDirectListenerMode() {
3269
- hydrationDirectListenerDepth = Math.max(0, hydrationDirectListenerDepth - 1);
3290
+ function getDirectVNodeChildren(vnode) {
3291
+ const children = vnode.props?.children ?? vnode.children;
3292
+ if (children === null || children === void 0 || children === false) return [];
3293
+ return Array.isArray(children) ? children : [children];
3270
3294
  }
3271
- function markHydrationDirectListener(element, listenerKey) {
3272
- let listeners = hydrationDirectListeners.get(element);
3273
- if (!listeners) {
3274
- listeners = /* @__PURE__ */ new Set();
3275
- hydrationDirectListeners.set(element, listeners);
3295
+ function canAdoptMatchingIntrinsicSubtree(element, vnode) {
3296
+ if (typeof vnode.type !== "string" || !tagNamesEqualIgnoreCase$1(element.tagName, vnode.type) || hasAnyElementBindingState(element) || extractKey(vnode) !== void 0) return false;
3297
+ const props = vnode.props || {};
3298
+ if (!hasMatchingStaticPropsIgnoringEvents(element, props, vnode.type)) return false;
3299
+ for (const key in props) {
3300
+ const value = props[key];
3301
+ const eventProp = parseEventProp(key);
3302
+ if (eventProp && typeof value !== "function") return false;
3303
+ if (!eventProp && key !== "ref" && typeof value === "function") return false;
3276
3304
  }
3277
- listeners.add(listenerKey);
3305
+ const children = getDirectVNodeChildren(vnode);
3306
+ if (element.childNodes.length !== children.length) return false;
3307
+ for (let index = 0; index < children.length; index += 1) {
3308
+ const child = children[index];
3309
+ const current = element.childNodes[index];
3310
+ if (typeof child === "string" || typeof child === "number") {
3311
+ if (current.nodeType !== 3 || current.data !== String(child)) return false;
3312
+ continue;
3313
+ }
3314
+ if (!_isDOMElement(child) || typeof child.type !== "string" || !(current instanceof Element) || extractKey(child) !== void 0 || !canAdoptMatchingIntrinsicSubtree(current, child)) return false;
3315
+ }
3316
+ return true;
3278
3317
  }
3279
- function isHydrationDirectListener(element, listenerKey) {
3280
- return hydrationDirectListeners.get(element)?.has(listenerKey) ?? false;
3318
+ function adoptMatchingIntrinsicSubtree(element, vnode) {
3319
+ const props = vnode.props || {};
3320
+ let hasBindings = Object.prototype.hasOwnProperty.call(props, "ref");
3321
+ for (const key in props) if (parseEventProp(key)) {
3322
+ hasBindings = true;
3323
+ break;
3324
+ }
3325
+ if (hasBindings) runRetainedElementUpdate(element, () => void 0, () => applyMatchingElementBindings(element, props), void 0, true);
3326
+ const children = getDirectVNodeChildren(vnode);
3327
+ for (let index = 0; index < children.length; index += 1) {
3328
+ const child = children[index];
3329
+ if (_isDOMElement(child)) adoptMatchingIntrinsicSubtree(element.childNodes[index], child);
3330
+ }
3281
3331
  }
3282
- function clearHydrationDirectListener(element, listenerKey) {
3283
- const listeners = hydrationDirectListeners.get(element);
3284
- listeners?.delete(listenerKey);
3285
- if (listeners?.size === 0) hydrationDirectListeners.delete(element);
3332
+ function tryAdoptMatchingIntrinsicSubtree(element, vnode) {
3333
+ if (!isHydrationAdoptionScopeActive()) return false;
3334
+ if (!canAdoptMatchingIntrinsicSubtree(element, vnode)) return false;
3335
+ adoptMatchingIntrinsicSubtree(element, vnode);
3336
+ return true;
3286
3337
  }
3287
- function addTrackedListener(el, eventName, handler, capture = false, fresh = false, forceDirect = false) {
3288
- const effectiveForceDirect = forceDirect || hydrationDirectListenerDepth > 0;
3289
- const useDelegation = !effectiveForceDirect && !capture && isEventDelegationEnabled() && isDelegatedEvent(eventName);
3290
- const listenerKey = getEventListenerKey(eventName, capture);
3291
- if (effectiveForceDirect && isHydrationDirectListener(el, listenerKey)) {
3292
- const existing = elementListeners.get(el)?.get(listenerKey);
3293
- if (existing) {
3294
- existing.updateHandler?.(handler);
3295
- existing.original = handler;
3296
- return;
3297
- }
3338
+ //#endregion
3339
+ //#region src/renderer/element-children.ts
3340
+ const rendererReactiveChildDOMHost = {
3341
+ createDOMNode: (node, parentNamespace) => getRendererDOMHost().createDOMNode(node, parentNamespace),
3342
+ updateElementChildren: (el, children, forceUpdate) => updateElementChildren$1(el, children, forceUpdate)
3343
+ };
3344
+ function updateElementChildren$1(el, children, forceUpdate = false) {
3345
+ const directControlBoundary = getDirectControlBoundaryVNode(children);
3346
+ if (directControlBoundary) {
3347
+ const controlState = getControlBoundaryState(directControlBoundary);
3348
+ if (!controlState) throw new Error("[updateElementChildren] Control boundary missing internal state");
3349
+ registerControlBoundaryCommitOwner(el, controlState);
3350
+ commitForBoundaryChildren(el, controlState, evaluateControlBoundaryState(controlState));
3351
+ return;
3298
3352
  }
3299
- if (!effectiveForceDirect && !hasStagedHydrationListener(el, eventName, capture) && stageHydrationListener({
3300
- kind: "direct",
3301
- target: el,
3302
- eventName,
3303
- capture,
3304
- publish: () => addTrackedListener(el, eventName, handler, capture, fresh, true),
3305
- rollback: () => {
3306
- removeTrackedListener(el, eventName, capture);
3307
- clearHydrationDirectListener(el, listenerKey);
3353
+ clearControlBoundaryCommitOwner(el);
3354
+ if (children === null || children === void 0) {
3355
+ for (let n = el.firstChild; n;) {
3356
+ const next = n.nextSibling;
3357
+ teardownNodeSubtree(n);
3358
+ n = next;
3308
3359
  }
3309
- })) return;
3310
- if (useDelegation) {
3311
- if (fresh) addFreshDelegatedListener(el, eventName, handler, handler, void 0);
3312
- else addDelegatedListener(el, eventName, handler, handler, void 0);
3360
+ el.textContent = "";
3313
3361
  return;
3314
3362
  }
3315
- const options = getEventListenerOptions(eventName, capture);
3316
- const mutableHandler = createMutableWrappedHandler(handler, true);
3317
- const trackedHandler = mutableHandler.handler;
3318
- if (options !== void 0) el.addEventListener(eventName, trackedHandler, options);
3319
- else el.addEventListener(eventName, trackedHandler);
3320
- if (effectiveForceDirect) {
3321
- if (!capture && getDelegatedHandlerForElement(el, eventName)) removeDelegatedListener(el, eventName);
3322
- markHydrationDirectListener(el, listenerKey);
3363
+ if (!Array.isArray(children) && isFragmentVNode(children)) {
3364
+ updateUnkeyedChildren(el, normalizeComponentChildren(children), forceUpdate);
3365
+ return;
3323
3366
  }
3324
- if (!elementListeners.has(el)) elementListeners.set(el, /* @__PURE__ */ new Map());
3325
- elementListeners.get(el).set(listenerKey, {
3326
- handler: trackedHandler,
3327
- original: handler,
3328
- eventName,
3329
- options,
3330
- isDelegated: false,
3331
- updateHandler: mutableHandler?.updateHandler
3332
- });
3333
- }
3334
- function removeTrackedListener(el, eventName, capture) {
3335
- if (!capture && getDelegatedHandlerForElement(el, eventName)) {
3336
- removeDelegatedListener(el, eventName);
3367
+ if (!Array.isArray(children) && (typeof children === "string" || typeof children === "number")) {
3368
+ if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
3369
+ const s = String(children);
3370
+ const t = el.firstChild;
3371
+ if (t.data !== s) t.data = s;
3372
+ } else {
3373
+ for (let n = el.firstChild; n;) {
3374
+ const next = n.nextSibling;
3375
+ teardownNodeSubtree(n);
3376
+ n = next;
3377
+ }
3378
+ el.textContent = String(children);
3379
+ }
3337
3380
  return;
3338
3381
  }
3339
- const listenerKey = getEventListenerKey(eventName, capture);
3340
- const entry = elementListeners.get(el)?.get(listenerKey);
3341
- if (!entry) return;
3342
- if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
3343
- else el.removeEventListener(entry.eventName, entry.handler);
3344
- const listeners = elementListeners.get(el);
3345
- listeners?.delete(listenerKey);
3346
- if (listeners?.size === 0) elementListeners.delete(el);
3347
- }
3348
- function markReactivePropsDirtySource(source) {
3349
- markFineGrainedEffectsDirtySource(source);
3350
- }
3351
- function setupReactiveProp(el, propName, propFn, tagName) {
3352
- const descriptor = {
3353
- el,
3354
- propName,
3355
- propFn,
3356
- tagName,
3357
- lastClassTokens: null
3358
- };
3359
- let effectHandle = null;
3360
- reactivePropRegistry.add(descriptor);
3361
- effectHandle = createFineGrainedEffect({
3362
- lane: "reactive",
3363
- compute: () => descriptor.propFn(),
3364
- commit: (value, previousValue) => {
3365
- applyScalarPropValue(el, propName, value, tagName, previousValue, descriptor);
3366
- },
3367
- equals: (previousValue, nextValue) => {
3368
- if (Object.is(previousValue, nextValue)) return true;
3369
- return false;
3370
- },
3371
- onError: (err) => {
3372
- if (getRuntimeEnv().NODE_ENV !== "production") logger.warn("[Askr] Reactive prop update failed:", err);
3382
+ if (Array.isArray(children)) {
3383
+ const normalizedChildren = normalizeComponentChildren(children);
3384
+ if (trySyncScalarChildSequenceInPlace(el, normalizedChildren, rendererReactiveChildDOMHost)) {
3385
+ keyedElements.delete(el);
3386
+ return;
3373
3387
  }
3374
- });
3375
- const cleanup = () => {
3376
- reactivePropRegistry.delete(descriptor);
3377
- effectHandle?.cleanup();
3378
- effectHandle = null;
3379
- };
3380
- const updateFn = (nextFn) => {
3381
- if (!effectHandle) return;
3382
- descriptor.propFn = nextFn;
3383
- try {
3384
- effectHandle.updateCompute(nextFn);
3385
- } catch (err) {
3386
- if (getRuntimeEnv().NODE_ENV !== "production") logger.warn("[Askr] Reactive prop update failed:", err);
3388
+ if (normalizedChildren.some(isControlBoundaryVNode)) {
3389
+ updateMixedControlChildren(el, normalizedChildren, forceUpdate);
3390
+ keyedElements.delete(el);
3391
+ return;
3387
3392
  }
3388
- };
3389
- return {
3390
- cleanup,
3391
- updateFn
3392
- };
3393
- }
3394
- function getOrCreateReactivePropsCleanupMap(el) {
3395
- return getElementReactivePropsCleanupMap(el, true);
3396
- }
3397
- /** @internal Create a standalone reactive prop entry for rollback restoration. */
3398
- function createReactivePropCleanupEntry(el, propName, propFn, tagName) {
3399
- const reactive = setupReactiveProp(el, propName, propFn, tagName);
3400
- return {
3401
- cleanup: reactive.cleanup,
3402
- updateFn: (nextValue) => {
3403
- reactive.updateFn(nextValue);
3404
- },
3405
- restoreFn: (nextValue) => createReactivePropCleanupEntry(el, propName, nextValue, tagName),
3406
- fnRef: propFn
3407
- };
3408
- }
3409
- function hasTrackedElementPropBindings(el) {
3410
- const existingListeners = elementListeners.get(el);
3411
- return existingListeners !== void 0 && existingListeners.size > 0 || getElementReactivePropCleanupSize(el) > 0;
3393
+ if (hasKeyedVNodeChildren(normalizedChildren)) {
3394
+ const newKeyMap = reconcileKeyedChildren(el, normalizedChildren, getOrBuildDomKeyMap(el));
3395
+ keyedElements.set(el, newKeyMap);
3396
+ return;
3397
+ }
3398
+ if (isBulkTextFastPathEligible(el, normalizedChildren)) {
3399
+ performBulkTextReplace(el, normalizedChildren);
3400
+ keyedElements.delete(el);
3401
+ return;
3402
+ }
3403
+ updateUnkeyedChildren(el, normalizedChildren, forceUpdate);
3404
+ return;
3405
+ }
3406
+ if (_isDOMElement(children)) {
3407
+ updateUnkeyedChildren(el, [children], forceUpdate);
3408
+ return;
3409
+ }
3410
+ for (let n = el.firstChild; n;) {
3411
+ const next = n.nextSibling;
3412
+ teardownNodeSubtree(n);
3413
+ n = next;
3414
+ }
3415
+ el.textContent = "";
3416
+ const dom = getRendererDOMHost().createDOMNode(children);
3417
+ if (dom) el.appendChild(dom);
3412
3418
  }
3413
- function hasAnyElementBindingState(el) {
3414
- return hasTrackedElementPropBindings(el) || getDelegatedHandlersForElement(el) !== void 0 || elementRefs.has(el);
3419
+ function hasKeyedVNodeChildren(children) {
3420
+ for (let i = 0; i < children.length; i++) if (extractKey(children[i]) !== void 0) return true;
3421
+ return false;
3415
3422
  }
3416
- function applyMatchingElementBindings(el, props) {
3417
- if (Object.prototype.hasOwnProperty.call(props, "ref")) updateElementRef(el, props.ref);
3418
- for (const key in props) {
3419
- const eventProp = parseEventProp(key);
3420
- if (!eventProp) continue;
3421
- const value = props[key];
3422
- if (typeof value === "function") addTrackedListener(el, eventProp.eventName, value, eventProp.capture);
3423
- }
3423
+ function isControlBoundaryVNode(child) {
3424
+ return _isDOMElement(child) && child.type === __CONTROL_BOUNDARY__;
3424
3425
  }
3425
- /** @internal Attach stateful bindings to a newly cloned blueprint element. */
3426
- function applyFreshElementBindings(el, props) {
3427
- if (Object.prototype.hasOwnProperty.call(props, "ref")) updateElementRef(el, props.ref);
3428
- for (const key in props) {
3429
- const eventProp = parseEventProp(key);
3430
- if (eventProp) {
3431
- const value = props[key];
3432
- if (typeof value === "function") addTrackedListener(el, eventProp.eventName, value, eventProp.capture, true);
3426
+ function removeRangeAtCursor(parent, cursor) {
3427
+ if (!isRangeStart(cursor)) return cursor;
3428
+ const end = findRangeEnd(cursor);
3429
+ if (!end) return cursor;
3430
+ const range = {
3431
+ start: cursor,
3432
+ end,
3433
+ single: false
3434
+ };
3435
+ const next = end.nextSibling;
3436
+ removeRange(range, (node) => {
3437
+ if (node === range.start || node === range.end) {
3438
+ node.parentNode?.removeChild(node);
3439
+ return;
3433
3440
  }
3441
+ teardownNodeSubtree(node);
3442
+ node.parentNode?.removeChild(node);
3443
+ });
3444
+ return next;
3445
+ }
3446
+ function consumeUnmatchedTailAtCursor(parent, cursor) {
3447
+ const instanceHost = cursor;
3448
+ if (isSSRPortalHydrationAnchor(cursor) && (isHydrationAdoptionScopeActive() || Boolean(instanceHost.__ASKR_INSTANCE) || Boolean(instanceHost.__ASKR_INSTANCES?.length))) return cursor.nextSibling;
3449
+ if (isRangeStart(cursor)) {
3450
+ const next = removeRangeAtCursor(parent, cursor);
3451
+ if (next !== cursor) return next;
3434
3452
  }
3453
+ const next = cursor.nextSibling;
3454
+ teardownNodeSubtree(cursor);
3455
+ if (cursor.parentNode === parent) parent.removeChild(cursor);
3456
+ return next;
3435
3457
  }
3436
- function applyPropsToElement$1(el, props, tagName, isHydrationSkipped) {
3437
- if (isHydrationSkipped(el)) return;
3438
- for (const key in props) {
3439
- const value = props[key];
3440
- if (key === "ref") {
3441
- updateElementRef(el, value);
3458
+ function updateMixedControlChildren(parent, children, forceUpdate) {
3459
+ clearControlBoundaryCommitOwner(parent);
3460
+ const parentNamespace = getParentNamespace(parent);
3461
+ const domHost = getRendererDOMHost();
3462
+ let cursor = parent.firstChild;
3463
+ for (const child of children) {
3464
+ if (isControlBoundaryVNode(child)) {
3465
+ const controlState = getControlBoundaryState(child);
3466
+ if (!controlState) throw new Error("[updateElementChildren] Control boundary missing internal state");
3467
+ const childVNodes = evaluateControlBoundaryState(controlState);
3468
+ const cursorAfterBoundary = cursor ? isRangeStart(cursor) ? findRangeEnd(cursor)?.nextSibling ?? cursor.nextSibling : cursor.nextSibling : null;
3469
+ const ranges = syncControlBoundaryInMixedParent(parent, controlState, childVNodes, cursor);
3470
+ if (controlState.kind === "for") {
3471
+ const last = ranges[ranges.length - 1];
3472
+ cursor = last ? last.end.nextSibling : cursorAfterBoundary?.parentNode === parent ? cursorAfterBoundary : cursor?.parentNode === parent ? cursor : null;
3473
+ continue;
3474
+ }
3475
+ for (const range of ranges) if (range.start.parentNode !== parent) moveRange(parent, range, cursor?.parentNode === parent ? cursor : null);
3476
+ else if (cursor?.parentNode === parent) moveRange(parent, range, cursor);
3477
+ const last = ranges[ranges.length - 1];
3478
+ if (last) {
3479
+ const cursorWasReplaced = cursor && !ranges.some((range) => range.start === cursor);
3480
+ if (!cursor?.parentNode) cursor = last.end.nextSibling;
3481
+ else if (cursorWasReplaced && isRangeStart(cursor)) cursor = findRangeEnd(cursor)?.nextSibling ?? last.end.nextSibling;
3482
+ else if (isRangeStart(cursor)) cursor = findRangeEnd(cursor)?.nextSibling ?? last.end.nextSibling;
3483
+ else cursor = last.end.nextSibling;
3484
+ } else {
3485
+ const inactiveBoundaryCursor = cursor?.parentNode === parent ? cursor : cursorAfterBoundary?.parentNode === parent ? cursorAfterBoundary : null;
3486
+ const inactiveBoundaryEnd = inactiveBoundaryCursor && isRangeStart(inactiveBoundaryCursor) ? findRangeEnd(inactiveBoundaryCursor) : null;
3487
+ cursor = inactiveBoundaryEnd?.previousSibling === inactiveBoundaryCursor ? inactiveBoundaryEnd.nextSibling : inactiveBoundaryCursor;
3488
+ }
3442
3489
  continue;
3443
3490
  }
3444
- if (isSkippedProp(key)) continue;
3445
- if (value === void 0 || value === null || value === false) continue;
3446
- const eventProp = parseEventProp(key);
3447
- if (eventProp) {
3448
- addTrackedListener(el, eventProp.eventName, value, eventProp.capture);
3491
+ if (cursor) cursor = removeRangeAtCursor(parent, cursor);
3492
+ if (cursor && (typeof child === "string" || typeof child === "number") && cursor.nodeType === 3) {
3493
+ cursor.data = String(child);
3494
+ cursor = cursor.nextSibling;
3449
3495
  continue;
3450
3496
  }
3451
- if (typeof value === "function" && key !== "ref") {
3452
- getOrCreateReactivePropsCleanupMap(el).set(key, createReactivePropCleanupEntry(el, key, value, tagName));
3497
+ if (cursor instanceof Element && _isDOMElement(child) && typeof child.type === "string" && tagsEqualIgnoreCase(cursor.tagName, child.type)) {
3498
+ domHost.updateElementFromVnode(cursor, child, true, forceUpdate);
3499
+ cursor = cursor.nextSibling;
3453
3500
  continue;
3454
3501
  }
3455
- applyScalarPropValue(el, key, value, tagName);
3502
+ if (cursor && _isDOMElement(child) && typeof child.type === "function") {
3503
+ const synced = domHost.syncComponentElement(cursor, child, child.type, (child.props ?? {}) || {}, parentNamespace, forceUpdate, void 0, void 0, true);
3504
+ if (synced) {
3505
+ cursor = synced.nextSibling;
3506
+ continue;
3507
+ }
3508
+ }
3509
+ const created = domHost.createDOMNode(child, parentNamespace);
3510
+ if (!created) {
3511
+ cursor = cursor?.nextSibling ?? null;
3512
+ continue;
3513
+ }
3514
+ const materialized = createDetachedRange(created);
3515
+ const old = cursor;
3516
+ moveRange(parent, materialized.range, old);
3517
+ if (old) {
3518
+ teardownNodeSubtree(old);
3519
+ old.parentNode?.removeChild(old);
3520
+ }
3521
+ cursor = materialized.range.end.nextSibling;
3456
3522
  }
3523
+ while (cursor) cursor = consumeUnmatchedTailAtCursor(parent, cursor);
3457
3524
  }
3458
- function syncElementPropBindings(el, domVNode, props, usesReactiveChildren) {
3459
- const existingListeners = elementListeners.get(el);
3460
- const existingReactiveProps = getElementReactivePropsCleanupMap(el);
3461
- let desiredListenerKeys = null;
3462
- let desiredDelegatedEventNames = null;
3463
- let desiredReactivePropNames = null;
3464
- if (usesReactiveChildren) (desiredReactivePropNames ??= /* @__PURE__ */ new Set()).add(REACTIVE_CHILDREN_KEY);
3465
- for (const key in props) {
3466
- const value = props[key];
3467
- if (key === "ref") continue;
3468
- if (isSkippedProp(key)) continue;
3469
- const eventProp = parseEventProp(key);
3470
- const eventName = eventProp?.eventName;
3471
- const eventCapture = eventProp?.capture ?? false;
3472
- const listenerKey = eventName === void 0 ? null : getEventListenerKey(eventName, eventCapture);
3473
- if (value === void 0 || value === null || value === false) {
3474
- if (listenerKey && existingListeners?.has(listenerKey)) {
3475
- const entry = existingListeners.get(listenerKey);
3476
- if (entry.isDelegated) removeDelegatedListener(el, entry.eventName);
3477
- else if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
3478
- else el.removeEventListener(entry.eventName, entry.handler);
3479
- existingListeners.delete(listenerKey);
3525
+ function isEmptyChild(child) {
3526
+ return child === null || child === void 0 || child === false;
3527
+ }
3528
+ function getOrBuildDomKeyMap(parent) {
3529
+ let keyMap = keyedElements.get(parent);
3530
+ if (!keyMap) {
3531
+ keyMap = /* @__PURE__ */ new Map();
3532
+ for (let child = parent.firstElementChild; child; child = child.nextElementSibling) {
3533
+ const key = getMaterializedKey(child);
3534
+ if (key !== void 0) keyMap.set(key, child);
3535
+ }
3536
+ if (keyMap.size > 0) keyedElements.set(parent, keyMap);
3537
+ }
3538
+ return keyMap.size > 0 ? keyMap : void 0;
3539
+ }
3540
+ function updateUnkeyedChildren(parent, newChildren, forceUpdate = false) {
3541
+ const parentNamespace = getParentNamespace(parent);
3542
+ const domHost = getRendererDOMHost();
3543
+ const trySyncComponentChild = (currentDom, next, hydrationRangeEnd) => {
3544
+ if (typeof next.type !== "function") return null;
3545
+ return domHost.syncComponentElement(currentDom, next, next.type, (next.props ?? {}) || {}, parentNamespace, forceUpdate, void 0, hydrationRangeEnd, true);
3546
+ };
3547
+ const hasText = newChildren.some((c) => typeof c === "string" || typeof c === "number");
3548
+ const hasElements = newChildren.some((c) => _isDOMElement(c));
3549
+ const hasEmptyChildren = newChildren.some(isEmptyChild);
3550
+ const hasComponentChildren = newChildren.some((c) => _isDOMElement(c) && typeof c.type === "function");
3551
+ const hasNonElementDomChildren = parent.childNodes.length !== parent.children.length;
3552
+ if (!hasEmptyChildren && !hasText && !hasComponentChildren && !hasNonElementDomChildren && hasElements && parent.children.length === newChildren.length) {
3553
+ const c = parent.children;
3554
+ for (let i = 0; i < newChildren.length; i++) {
3555
+ const next = newChildren[i];
3556
+ const current = c[i];
3557
+ if (!current || next === void 0) continue;
3558
+ if (_isDOMElement(next) && typeof next.type === "string") if (tagsEqualIgnoreCase(current.tagName, next.type)) domHost.updateElementFromVnode(current, next, true, forceUpdate);
3559
+ else {
3560
+ const dom = domHost.createDOMNode(next, parentNamespace);
3561
+ if (dom) {
3562
+ teardownNodeSubtree(current);
3563
+ parent.replaceChild(dom, current);
3564
+ }
3565
+ }
3566
+ else if (_isDOMElement(next)) {
3567
+ if (trySyncControlBoundaryChild(parent, current, next)) continue;
3568
+ if (!trySyncComponentChild(current, next)) {
3569
+ const dom = domHost.createDOMNode(next, parentNamespace);
3570
+ if (dom) {
3571
+ teardownNodeSubtree(current);
3572
+ if (current.parentNode === parent) parent.replaceChild(dom, current);
3573
+ else if (dom.parentNode !== parent) parent.insertBefore(dom, parent.children[i] ?? null);
3574
+ }
3575
+ }
3480
3576
  } else {
3481
- const entry = existingReactiveProps?.get(key);
3482
- if (entry) {
3483
- entry.cleanup();
3484
- existingReactiveProps?.delete(key);
3485
- } else applyScalarPropValue(el, key, value, domVNode.type);
3577
+ const dom = domHost.createDOMNode(next, parentNamespace);
3578
+ if (dom) {
3579
+ teardownNodeSubtree(current);
3580
+ parent.replaceChild(dom, current);
3581
+ }
3486
3582
  }
3487
- continue;
3488
3583
  }
3489
- if (typeof value === "function" && !eventProp && key !== "ref") {
3490
- const existingEntry = existingReactiveProps?.get(key);
3491
- if (existingReactiveProps && existingReactiveProps.size > 0) (desiredReactivePropNames ??= /* @__PURE__ */ new Set()).add(key);
3492
- if (existingEntry && existingEntry.fnRef === value) continue;
3493
- if (existingEntry?.updateFn) {
3494
- existingEntry.updateFn(value);
3495
- existingEntry.fnRef = value;
3584
+ return;
3585
+ }
3586
+ const existing = Array.from(parent.children);
3587
+ if (hasText || hasComponentChildren || hasEmptyChildren || hasNonElementDomChildren) {
3588
+ const allNodes = Array.from(parent.childNodes);
3589
+ for (let i = 0; i < Math.max(allNodes.length, newChildren.length); i += 1) {
3590
+ const currentNode = allNodes[i];
3591
+ const next = newChildren[i];
3592
+ const nextIsEmpty = isEmptyChild(next);
3593
+ if (nextIsEmpty && currentNode) {
3594
+ teardownNodeSubtree(currentNode);
3595
+ currentNode.parentNode?.removeChild(currentNode);
3496
3596
  continue;
3497
3597
  }
3498
- if (existingEntry) existingEntry.cleanup();
3499
- getOrCreateReactivePropsCleanupMap(el).set(key, createReactivePropCleanupEntry(el, key, value, domVNode.type));
3500
- continue;
3501
- }
3502
- const existingReactiveEntry = existingReactiveProps?.get(key);
3503
- if (existingReactiveEntry) {
3504
- existingReactiveEntry.cleanup();
3505
- existingReactiveProps?.delete(key);
3506
- }
3507
- if (eventProp && listenerKey) {
3508
- const eventName = eventProp.eventName;
3509
- const eventCapture = eventProp.capture;
3510
- const preserveHydrationDirect = existingListeners?.get(listenerKey)?.isDelegated === false && isHydrationDirectListener(el, listenerKey);
3511
- if (getCurrentHydrationListenerTransaction() && !existingListeners?.has(listenerKey) && !getDelegatedHandlerForElement(el, eventName) && !isHydrationDirectListener(el, listenerKey)) {
3512
- if (!hasStagedHydrationListener(el, eventName, eventCapture)) stageHydrationListener({
3513
- kind: "direct",
3514
- target: el,
3515
- eventName,
3516
- capture: eventCapture,
3517
- publish: () => addTrackedListener(el, eventName, value, eventCapture, false, true),
3518
- rollback: () => {
3519
- removeTrackedListener(el, eventName, eventCapture);
3520
- clearHydrationDirectListener(el, listenerKey);
3598
+ if (!currentNode && !nextIsEmpty) {
3599
+ const dom = domHost.createDOMNode(next, parentNamespace);
3600
+ if (dom) parent.appendChild(dom);
3601
+ continue;
3602
+ }
3603
+ if (!currentNode || nextIsEmpty) continue;
3604
+ if (typeof next === "string" || typeof next === "number") if (currentNode.nodeType === 3) currentNode.data = String(next);
3605
+ else {
3606
+ const textNode = document.createTextNode(String(next));
3607
+ teardownNodeSubtree(currentNode);
3608
+ parent.replaceChild(textNode, currentNode);
3609
+ }
3610
+ else if (_isDOMElement(next)) if (currentNode.nodeType === 1) {
3611
+ const currentEl = currentNode;
3612
+ if (typeof next.type === "string") if (tagsEqualIgnoreCase(currentEl.tagName, next.type)) domHost.updateElementFromVnode(currentEl, next, true, forceUpdate);
3613
+ else {
3614
+ const dom = domHost.createDOMNode(next, parentNamespace);
3615
+ if (dom) {
3616
+ teardownNodeSubtree(currentEl);
3617
+ if (currentNode.parentNode === parent) parent.replaceChild(dom, currentNode);
3618
+ else if (dom.parentNode !== parent) parent.insertBefore(dom, parent.childNodes[i] ?? null);
3619
+ }
3620
+ }
3621
+ else {
3622
+ if (trySyncControlBoundaryChild(parent, currentNode, next)) continue;
3623
+ const remainingExpected = newChildren.length - i - 1;
3624
+ const hydrationRangeEndIndex = allNodes.length - remainingExpected;
3625
+ const synced = trySyncComponentChild(currentEl, next, hydrationRangeEndIndex > i ? allNodes[hydrationRangeEndIndex] ?? null : void 0);
3626
+ if (synced && isRangeStart(synced)) {
3627
+ allNodes.splice(i, Math.max(1, hydrationRangeEndIndex - i), synced);
3628
+ continue;
3629
+ }
3630
+ if (synced && synced !== currentNode && synced.nextSibling === currentNode) {
3631
+ allNodes.splice(i, 0, synced);
3632
+ continue;
3633
+ }
3634
+ if (!synced) {
3635
+ const dom = domHost.createDOMNode(next, parentNamespace);
3636
+ if (dom) {
3637
+ teardownNodeSubtree(currentEl);
3638
+ parent.replaceChild(dom, currentNode);
3639
+ }
3640
+ }
3641
+ }
3642
+ } else {
3643
+ if (typeof next.type === "function") {
3644
+ const remainingExpected = newChildren.length - i - 1;
3645
+ const hydrationRangeEndIndex = allNodes.length - remainingExpected;
3646
+ const synced = trySyncComponentChild(currentNode, next, hydrationRangeEndIndex > i ? allNodes[hydrationRangeEndIndex] ?? null : void 0);
3647
+ if (synced && isRangeStart(synced)) {
3648
+ allNodes.splice(i, Math.max(1, hydrationRangeEndIndex - i), synced);
3649
+ continue;
3650
+ }
3651
+ if (synced && synced !== currentNode && synced.nextSibling === currentNode) {
3652
+ allNodes.splice(i, 0, synced);
3653
+ continue;
3521
3654
  }
3522
- });
3523
- continue;
3524
- }
3525
- if (hydrationDirectListenerDepth > 0 && !existingListeners?.has(listenerKey) && !getDelegatedHandlerForElement(el, eventName)) {
3526
- addTrackedListener(el, eventName, value, eventCapture, false, true);
3527
- continue;
3528
- }
3529
- const useDelegation = !preserveHydrationDirect && !eventCapture && isEventDelegationEnabled() && isDelegatedEvent(eventName);
3530
- if (useDelegation) (desiredDelegatedEventNames ??= /* @__PURE__ */ new Set()).add(eventName);
3531
- if (useDelegation) {
3532
- const existingDelegated = getDelegatedHandlerForElement(el, eventName);
3533
- if (existingDelegated?.original === value) continue;
3534
- if (existingDelegated && updateDelegatedListener(el, eventName, value, value, void 0)) continue;
3535
- addDelegatedListener(el, eventName, value, value, void 0);
3536
- continue;
3537
- }
3538
- (desiredListenerKeys ??= /* @__PURE__ */ new Set()).add(listenerKey);
3539
- const existing = existingListeners?.get(listenerKey);
3540
- if (existing && existing.original === value) continue;
3541
- if (existing) {
3542
- if (useDelegation && existing.isDelegated && updateDelegatedListener(el, eventName, value, value, void 0)) {
3543
- existing.handler = value;
3544
- existing.original = value;
3545
- existing.options = void 0;
3546
- continue;
3655
+ if (synced) continue;
3547
3656
  }
3548
- if (!useDelegation && !existing.isDelegated && existing.updateHandler) {
3549
- existing.updateHandler(value);
3550
- existing.original = value;
3551
- continue;
3657
+ const dom = domHost.createDOMNode(next, parentNamespace);
3658
+ if (dom) {
3659
+ teardownNodeSubtree(currentNode);
3660
+ parent.replaceChild(dom, currentNode);
3552
3661
  }
3553
- if (existing.isDelegated) removeDelegatedListener(el, existing.eventName);
3554
- else if (existing.options !== void 0) el.removeEventListener(existing.eventName, existing.handler, existing.options);
3555
- else el.removeEventListener(existing.eventName, existing.handler);
3556
- }
3557
- const options = getEventListenerOptions(eventName, eventCapture);
3558
- const mutableHandler = createMutableWrappedHandler(value, true);
3559
- const trackedHandler = mutableHandler.handler;
3560
- if (options !== void 0) el.addEventListener(eventName, trackedHandler, options);
3561
- else el.addEventListener(eventName, trackedHandler);
3562
- const listenerEntry = {
3563
- handler: trackedHandler,
3564
- original: value,
3565
- eventName,
3566
- options,
3567
- isDelegated: false,
3568
- updateHandler: mutableHandler?.updateHandler
3569
- };
3570
- if (!elementListeners.has(el)) elementListeners.set(el, /* @__PURE__ */ new Map());
3571
- elementListeners.get(el).set(listenerKey, listenerEntry);
3572
- } else applyScalarPropValue(el, key, value, domVNode.type);
3573
- }
3574
- removeStaleAttributes(el, domVNode, props);
3575
- if (existingListeners && existingListeners.size > 0) if (desiredListenerKeys === null) {
3576
- existingListeners.forEach((entry) => {
3577
- if (entry.isDelegated) removeDelegatedListener(el, entry.eventName);
3578
- else if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
3579
- else el.removeEventListener(entry.eventName, entry.handler);
3580
- });
3581
- elementListeners.delete(el);
3582
- } else {
3583
- existingListeners.forEach((entry, listenerKey) => {
3584
- if (!desiredListenerKeys.has(listenerKey)) {
3585
- if (entry.isDelegated) removeDelegatedListener(el, entry.eventName);
3586
- else if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
3587
- else el.removeEventListener(entry.eventName, entry.handler);
3588
- existingListeners.delete(listenerKey);
3589
- }
3590
- });
3591
- if (existingListeners.size === 0) elementListeners.delete(el);
3592
- }
3593
- const delegatedHandlers = getDelegatedHandlersForElement(el);
3594
- if (delegatedHandlers && delegatedHandlers.size > 0) {
3595
- if (desiredDelegatedEventNames === null) for (const eventName of delegatedHandlers.keys()) removeDelegatedListener(el, eventName);
3596
- else for (const eventName of delegatedHandlers.keys()) if (!desiredDelegatedEventNames.has(eventName)) removeDelegatedListener(el, eventName);
3597
- }
3598
- if (existingReactiveProps && existingReactiveProps.size > 0) if (desiredReactivePropNames === null) {
3599
- existingReactiveProps.forEach((entry) => {
3600
- entry.cleanup();
3601
- });
3602
- elementReactivePropsCleanup.delete(el);
3603
- } else {
3604
- existingReactiveProps.forEach((entry, key) => {
3605
- if (!desiredReactivePropNames.has(key)) {
3606
- entry.cleanup();
3607
- existingReactiveProps.delete(key);
3608
3662
  }
3609
- });
3610
- if (existingReactiveProps.size === 0) elementReactivePropsCleanup.delete(el);
3663
+ }
3664
+ return;
3611
3665
  }
3612
- }
3613
- //#endregion
3614
- //#region src/renderer/intrinsic-hydration-adoption.ts
3615
- let hydrationAdoptionDepth = 0;
3616
- function withIntrinsicHydrationAdoption(work) {
3617
- hydrationAdoptionDepth += 1;
3618
- try {
3619
- return work();
3620
- } finally {
3621
- hydrationAdoptionDepth -= 1;
3666
+ if (newChildren.length === 1 && existing.length === 0 && parent.childNodes.length === 1) {
3667
+ const firstNewChild = newChildren[0];
3668
+ const firstExisting = parent.firstChild;
3669
+ if ((typeof firstNewChild === "string" || typeof firstNewChild === "number") && firstExisting?.nodeType === 3) {
3670
+ firstExisting.data = String(firstNewChild);
3671
+ return;
3672
+ }
3622
3673
  }
3623
- }
3624
- function getDirectVNodeChildren(vnode) {
3625
- const children = vnode.props?.children ?? vnode.children;
3626
- if (children === null || children === void 0 || children === false) return [];
3627
- return Array.isArray(children) ? children : [children];
3628
- }
3629
- function canAdoptMatchingIntrinsicSubtree(element, vnode) {
3630
- if (typeof vnode.type !== "string" || !tagNamesEqualIgnoreCase$1(element.tagName, vnode.type) || hasAnyElementBindingState(element) || extractKey(vnode) !== void 0) return false;
3631
- const props = vnode.props || {};
3632
- if (!hasMatchingStaticPropsIgnoringEvents(element, props, vnode.type)) return false;
3633
- for (const key in props) {
3634
- const value = props[key];
3635
- const eventProp = parseEventProp(key);
3636
- if (eventProp && typeof value !== "function") return false;
3637
- if (!eventProp && key !== "ref" && typeof value === "function") return false;
3674
+ if (existing.length === 0 && parent.childNodes.length > 0) {
3675
+ for (let n = parent.firstChild; n;) {
3676
+ const next = n.nextSibling;
3677
+ teardownNodeSubtree(n);
3678
+ n = next;
3679
+ }
3680
+ parent.textContent = "";
3638
3681
  }
3639
- const children = getDirectVNodeChildren(vnode);
3640
- if (element.childNodes.length !== children.length) return false;
3641
- for (let index = 0; index < children.length; index += 1) {
3642
- const child = children[index];
3643
- const current = element.childNodes[index];
3644
- if (typeof child === "string" || typeof child === "number") {
3645
- if (current.nodeType !== 3 || current.data !== String(child)) return false;
3682
+ const max = Math.max(existing.length, newChildren.length);
3683
+ for (let i = 0; i < max; i++) {
3684
+ const current = existing[i];
3685
+ const next = newChildren[i];
3686
+ const nextIsEmpty = isEmptyChild(next);
3687
+ if (nextIsEmpty && current) {
3688
+ teardownNodeSubtree(current);
3689
+ current.remove();
3646
3690
  continue;
3647
3691
  }
3648
- if (!_isDOMElement(child) || typeof child.type !== "string" || !(current instanceof Element) || extractKey(child) !== void 0 || !canAdoptMatchingIntrinsicSubtree(current, child)) return false;
3649
- }
3650
- return true;
3651
- }
3652
- function adoptMatchingIntrinsicSubtree(element, vnode) {
3653
- const props = vnode.props || {};
3654
- let hasBindings = Object.prototype.hasOwnProperty.call(props, "ref");
3655
- for (const key in props) if (parseEventProp(key)) {
3656
- hasBindings = true;
3657
- break;
3658
- }
3659
- if (hasBindings) runRetainedElementUpdate(element, () => void 0, () => applyMatchingElementBindings(element, props), void 0, true);
3660
- const children = getDirectVNodeChildren(vnode);
3661
- for (let index = 0; index < children.length; index += 1) {
3662
- const child = children[index];
3663
- if (_isDOMElement(child)) adoptMatchingIntrinsicSubtree(element.childNodes[index], child);
3692
+ if (!current && !nextIsEmpty) {
3693
+ const dom = domHost.createDOMNode(next, parentNamespace);
3694
+ if (dom) parent.appendChild(dom);
3695
+ continue;
3696
+ }
3697
+ if (!current || nextIsEmpty) continue;
3698
+ if (typeof next === "string" || typeof next === "number") {
3699
+ const textNode = document.createTextNode(String(next));
3700
+ teardownNodeSubtree(current);
3701
+ parent.replaceChild(textNode, current);
3702
+ } else if (_isDOMElement(next)) {
3703
+ if (typeof next.type === "string") if (tagsEqualIgnoreCase(current.tagName, next.type)) domHost.updateElementFromVnode(current, next, true, forceUpdate);
3704
+ else {
3705
+ const dom = domHost.createDOMNode(next, parentNamespace);
3706
+ if (dom) {
3707
+ teardownNodeSubtree(current);
3708
+ parent.replaceChild(dom, current);
3709
+ }
3710
+ }
3711
+ else if (!trySyncComponentChild(current, next)) {
3712
+ const dom = domHost.createDOMNode(next, parentNamespace);
3713
+ if (dom) {
3714
+ teardownNodeSubtree(current);
3715
+ if (current.parentNode === parent) parent.replaceChild(dom, current);
3716
+ else if (dom.parentNode !== parent) parent.insertBefore(dom, parent.children[i] ?? null);
3717
+ }
3718
+ }
3719
+ } else {
3720
+ const dom = domHost.createDOMNode(next);
3721
+ if (dom) {
3722
+ teardownNodeSubtree(current);
3723
+ parent.replaceChild(dom, current);
3724
+ }
3725
+ }
3664
3726
  }
3665
3727
  }
3666
- function tryAdoptMatchingIntrinsicSubtree(element, vnode) {
3667
- if (hydrationAdoptionDepth === 0) return false;
3668
- if (!canAdoptMatchingIntrinsicSubtree(element, vnode)) return false;
3669
- adoptMatchingIntrinsicSubtree(element, vnode);
3670
- return true;
3671
- }
3672
3728
  //#endregion
3673
3729
  //#region src/renderer/evaluate-reconcile.ts
3674
3730
  function getRetainedHostOwnerChain(host, owner) {
@@ -4350,8 +4406,13 @@ function checkVNodeShapeChanged(dom, vnode) {
4350
4406
  if (typeof vnode.type !== "string") return true;
4351
4407
  return dom.tagName.toLowerCase() !== vnode.type.toLowerCase();
4352
4408
  }
4409
+ function canAdoptHydratedElement(dom, vnode) {
4410
+ if (!_isDOMElement(vnode)) return false;
4411
+ if (typeof vnode.type === "function") return true;
4412
+ return !checkVNodeShapeChanged(dom, vnode);
4413
+ }
4353
4414
  function adoptHydratedRange(parent, scope, before, vnode) {
4354
- if (!scope.hydrationPending || !before || !isRangeStart(before)) return null;
4415
+ if (!isHydrationAdoptionScopeActive() || !scope.hydrationPending || !before || !isRangeStart(before)) return null;
4355
4416
  const end = findRangeEnd(before);
4356
4417
  if (!end || end.parentNode !== parent) return null;
4357
4418
  const range = {
@@ -4518,7 +4579,7 @@ function syncControlBoundaryInMixedParent(parent, controlState, childrenVNodes,
4518
4579
  const item = controlState.orderedItems[index];
4519
4580
  const vnode = childrenVNodes[index];
4520
4581
  if (!item || vnode === void 0) continue;
4521
- if (item.scope.hydrationPending && !item.scope.range && syncBefore instanceof Element && getMaterializedKey(syncBefore) === controlState.orderedKeys[index] && !checkVNodeShapeChanged(syncBefore, vnode)) {
4582
+ if (item.scope.hydrationPending && !item.scope.range && syncBefore instanceof Element && getMaterializedKey(syncBefore) === controlState.orderedKeys[index] && canAdoptHydratedElement(syncBefore, vnode)) {
4522
4583
  assignScopeRange(item.scope, {
4523
4584
  start: syncBefore,
4524
4585
  end: syncBefore,
@@ -4548,6 +4609,14 @@ function syncControlBoundaryInMixedParent(parent, controlState, childrenVNodes,
4548
4609
  }
4549
4610
  const activeScope = controlState.activeScope;
4550
4611
  const activeVNode = childrenVNodes[0];
4612
+ if (activeScope?.hydrationPending && activeVNode !== void 0 && !activeScope.range && controlState.lastRemovedRanges.length === 0 && before instanceof Element && canAdoptHydratedElement(before, activeVNode)) {
4613
+ assignScopeRange(activeScope, {
4614
+ start: before,
4615
+ end: before,
4616
+ single: true
4617
+ });
4618
+ activeScope.hydrationPending = false;
4619
+ }
4551
4620
  const nextRange = activeScope && activeVNode !== void 0 ? syncControlBoundaryScopeDom(parent, activeScope, activeVNode, before, controlState.lastRemovedRanges.length === 0) : null;
4552
4621
  for (const range of controlState.lastRemovedRanges) if (range !== nextRange) removeRange(range, teardownBoundaryRangeNode);
4553
4622
  for (const node of controlState.lastRemovedNodes) if (node.parentNode === parent) teardownBoundaryRangeNode(node);
@@ -4800,12 +4869,60 @@ function unwrapStagingHost(staging, parent, restoreFocus) {
4800
4869
  function createAttributeFreeStagingHost(parent) {
4801
4870
  return parent.ownerDocument.createElementNS(parent.namespaceURI, parent.localName);
4802
4871
  }
4872
+ function isAutomaticPortalHost(node) {
4873
+ const host = node;
4874
+ const instances = new Set(host.__ASKR_INSTANCES ?? []);
4875
+ if (host.__ASKR_INSTANCE) instances.add(host.__ASKR_INSTANCE);
4876
+ return instances.size > 0 && Array.from(instances).some((candidate) => candidate.props.__askrAutoDefaultPortal === true);
4877
+ }
4878
+ function adoptHydratedComponentRange(existingHost, instance, result, endExclusive, forceChildrenUpdate, retainedInstances) {
4879
+ const parent = existingHost.parentNode;
4880
+ if (!(parent instanceof Element) || endExclusive !== null && endExclusive.parentNode !== parent) return null;
4881
+ if (endExclusive !== null) {
4882
+ let current = existingHost;
4883
+ while (current && current !== endExclusive) current = current.nextSibling;
4884
+ if (current !== endExclusive) return null;
4885
+ }
4886
+ const start = existingHost.ownerDocument.createComment(RANGE_START_MARKER);
4887
+ const end = existingHost.ownerDocument.createComment(RANGE_END_MARKER);
4888
+ parent.insertBefore(start, existingHost);
4889
+ parent.insertBefore(end, endExclusive);
4890
+ registerRange({
4891
+ start,
4892
+ end,
4893
+ single: false
4894
+ }, instance);
4895
+ const host = start;
4896
+ const owners = new Set(retainedInstances);
4897
+ owners.add(instance);
4898
+ host.__ASKR_INSTANCE = instance;
4899
+ host.__ASKR_INSTANCES = Array.from(owners);
4900
+ instance.target = null;
4901
+ instance._placeholder = start;
4902
+ mountInstanceInline(instance, null);
4903
+ const registered = registerLifecycleTransaction({}, () => {}, () => {
4904
+ start.parentNode?.removeChild(start);
4905
+ end.parentNode?.removeChild(end);
4906
+ });
4907
+ try {
4908
+ if (!syncComponentFragmentRange(host, instance, result, forceChildrenUpdate)) throw new Error("[askr] Failed to adopt hydrated component range.");
4909
+ } catch (error) {
4910
+ if (!registered) {
4911
+ start.remove();
4912
+ end.remove();
4913
+ }
4914
+ throw error;
4915
+ }
4916
+ return start;
4917
+ }
4803
4918
  function syncComponentFragmentRange(host, instance, result, forceUpdate) {
4804
- if (!isFragmentVNode(result)) return false;
4919
+ if (!isTransparentComponentRangeResult(result)) return false;
4805
4920
  const range = getOwnedRange(instance);
4806
4921
  const parent = range?.start.parentNode;
4807
4922
  if (!range || range.single || range.start !== host || !(parent instanceof Element)) return false;
4808
4923
  const restoreFocus = captureRangeFocus(range, parent);
4924
+ const normalizedChildren = normalizeComponentChildren(result);
4925
+ const preserveForeignHosts = Array.isArray(result);
4809
4926
  const staging = createAttributeFreeStagingHost(parent);
4810
4927
  try {
4811
4928
  parent.insertBefore(staging, range.end);
@@ -4818,10 +4935,15 @@ function syncComponentFragmentRange(host, instance, result, forceUpdate) {
4818
4935
  restoreFocus();
4819
4936
  return false;
4820
4937
  }
4821
- while (range.start.nextSibling && range.start.nextSibling !== staging) staging.appendChild(range.start.nextSibling);
4938
+ let current = range.start.nextSibling;
4939
+ while (current && current !== staging) {
4940
+ const next = current.nextSibling;
4941
+ if (!preserveForeignHosts || !isAutomaticPortalHost(current)) staging.appendChild(current);
4942
+ current = next;
4943
+ }
4822
4944
  const rollbackStaging = registerLifecycleTransaction({}, () => {}, () => unwrapStagingHost(staging, parent, restoreFocus));
4823
4945
  try {
4824
- getRendererDOMHost().updateElementChildren(staging, normalizeComponentChildren(result), forceUpdate);
4946
+ getRendererDOMHost().updateElementChildren(staging, normalizedChildren, forceUpdate);
4825
4947
  } catch (error) {
4826
4948
  if (!rollbackStaging) unwrapStagingHost(staging, parent, restoreFocus);
4827
4949
  throw error;
@@ -5029,11 +5151,19 @@ function inheritComponentKey(target, source) {
5029
5151
  }
5030
5152
  return target;
5031
5153
  }
5154
+ function extractComponentIdentityKey(node) {
5155
+ const publicKey = extractKey(node);
5156
+ if (publicKey !== void 0) return publicKey;
5157
+ if (typeof node !== "object" || node === null) return;
5158
+ const internalKey = node.props?.["__askrIdentityKey"];
5159
+ if (internalKey === void 0 || internalKey === null) return;
5160
+ return typeof internalKey === "symbol" ? String(internalKey) : internalKey;
5161
+ }
5032
5162
  function setComponentOwnershipIdentity(instance, node, parent, wrapperDepth = 0, position) {
5033
5163
  instance._vnodeOwner = typeof node === "object" && node !== null ? node : void 0;
5034
5164
  instance._vnodeParent = parent;
5035
5165
  instance._vnodeParentGeneration = parent?._ownershipGeneration;
5036
- const key = extractKey(node);
5166
+ const key = extractComponentIdentityKey(node);
5037
5167
  if (key === void 0) delete instance._vnodeKey;
5038
5168
  else instance._vnodeKey = key;
5039
5169
  instance._vnodePosition = position;
@@ -5044,7 +5174,7 @@ function findHostInstanceByType(host, type, node, parent, wrapperDepth) {
5044
5174
  if (instances && instances.length > 0) {
5045
5175
  const vnodeOwner = getVNodeComponentInstance(node);
5046
5176
  if (vnodeOwner && vnodeOwner.fn === type && instances.includes(vnodeOwner)) return vnodeOwner;
5047
- const key = extractKey(node);
5177
+ const key = extractComponentIdentityKey(node);
5048
5178
  const identityMatches = instances.filter((instance) => {
5049
5179
  if (instance.fn !== type) return false;
5050
5180
  if (parent !== void 0 && instance._vnodeParent !== parent) return false;
@@ -5060,7 +5190,7 @@ function findHostInstanceByType(host, type, node, parent, wrapperDepth) {
5060
5190
  }
5061
5191
  if (host.__ASKR_INSTANCE?.fn === type) {
5062
5192
  const instance = host.__ASKR_INSTANCE;
5063
- const key = extractKey(node);
5193
+ const key = extractComponentIdentityKey(node);
5064
5194
  if ((parent === void 0 || instance._vnodeParent === parent) && (key === void 0 || instance._vnodeKey === key) && (wrapperDepth === void 0 || instance._wrapperDepth === wrapperDepth)) return instance;
5065
5195
  if (node === void 0 && parent === void 0 && wrapperDepth === void 0) return instance;
5066
5196
  }
@@ -5068,8 +5198,9 @@ function findHostInstanceByType(host, type, node, parent, wrapperDepth) {
5068
5198
  }
5069
5199
  /**
5070
5200
  * Resolve an instance on a comment host only when the vnode carries durable
5071
- * identity. Fresh unkeyed null components intentionally supersede the old
5072
- * generation instead of falling back to type-only reuse.
5201
+ * identity. An unkeyed multi-node range can also move through transparent
5202
+ * wrapper owners while its recorded owner generation remains live. Fresh
5203
+ * unkeyed null components still supersede the old generation.
5073
5204
  */
5074
5205
  function findStableHostInstanceByType(host, type, node, parent, wrapperDepth) {
5075
5206
  const instances = new Set(host.__ASKR_INSTANCES ?? []);
@@ -5077,10 +5208,21 @@ function findStableHostInstanceByType(host, type, node, parent, wrapperDepth) {
5077
5208
  const parentGeneration = parent?._ownershipGeneration;
5078
5209
  const vnodeOwner = getVNodeComponentInstance(node);
5079
5210
  if (vnodeOwner?.fn === type && instances.has(vnodeOwner) && vnodeOwner._vnodeParent === parent && vnodeOwner._vnodeParentGeneration === parentGeneration) return vnodeOwner;
5080
- const key = extractKey(node);
5081
- if (key === void 0) return null;
5211
+ const key = extractComponentIdentityKey(node);
5212
+ if (key === void 0) {
5213
+ const rangeMatches = Array.from(instances).filter((instance) => {
5214
+ const range = getOwnedRange(instance);
5215
+ return instance.fn === type && instance._vnodeParent !== parent && instance._vnodeParent != null && instance._vnodeParentGeneration === instance._vnodeParent._ownershipGeneration && instance._vnodeKey === void 0 && instance._wrapperDepth === wrapperDepth && (range?.single === false && range.start === host || host instanceof Comment && host.data === "askr-range-start" && instance._placeholder === host);
5216
+ });
5217
+ return rangeMatches.length === 1 ? rangeMatches[0] : null;
5218
+ }
5082
5219
  const matches = Array.from(instances).filter((instance) => instance.fn === type && instance._vnodeParent === parent && instance._vnodeParentGeneration === parentGeneration && instance._vnodeKey === key && instance._wrapperDepth === wrapperDepth);
5083
- return matches.length > 0 ? matches[matches.length - 1] : null;
5220
+ if (matches.length > 0) return matches[matches.length - 1];
5221
+ const rangeMatches = Array.from(instances).filter((instance) => {
5222
+ const range = getOwnedRange(instance);
5223
+ return instance.fn === type && instance._vnodeParent !== parent && instance._vnodeParent != null && instance._vnodeParentGeneration === instance._vnodeParent._ownershipGeneration && instance._vnodeKey === key && instance._wrapperDepth === wrapperDepth && (range?.single === false && range.start === host || host instanceof Comment && host.data === "askr-range-start" && instance._placeholder === host);
5224
+ });
5225
+ return rangeMatches.length === 1 ? rangeMatches[0] : null;
5084
5226
  }
5085
5227
  //#endregion
5086
5228
  //#region src/renderer/component-host-replacement.ts
@@ -5223,6 +5365,40 @@ function registerVNodeComponentInstanceRollback(node, previousInstance, provisio
5223
5365
  }
5224
5366
  //#endregion
5225
5367
  //#region src/renderer/component-host-results.ts
5368
+ function retainReplacementOwnerChain(host, owner, retainedInstances) {
5369
+ const componentHost = host;
5370
+ const next = [...componentHost.__ASKR_INSTANCES ?? []];
5371
+ for (const instance of retainedInstances) if (!next.includes(instance)) next.push(instance);
5372
+ componentHost.__ASKR_INSTANCES = next;
5373
+ componentHost.__ASKR_INSTANCE = owner;
5374
+ }
5375
+ function adoptEmptySSRPortalHydrationHost(host, instance, retainedInstances, result) {
5376
+ if (!isSSRPortalHydrationAnchor(host) || result !== null && result !== void 0 && result !== false) return false;
5377
+ const instanceHost = host;
5378
+ instanceHost.__ASKR_INSTANCE = instance;
5379
+ instanceHost.__ASKR_INSTANCES = Array.from(/* @__PURE__ */ new Set([instance, ...retainedInstances]));
5380
+ instance._placeholder = instanceHost;
5381
+ mountInstanceInline(instance, null);
5382
+ retainReplacementOwnerChain(instanceHost, instance, retainedInstances);
5383
+ markHydrationHostAdopted(instanceHost);
5384
+ return true;
5385
+ }
5386
+ function materializeEmptyHydrationPlaceholder(existingHost, instance, retainedInstances, result, preserveHydrationCursor) {
5387
+ if (!preserveHydrationCursor && !isSSRPortalHydrationAnchor(existingHost) || result !== null && result !== void 0 && result !== false) return null;
5388
+ const placeholder = isSSRPortalHydrationAnchor(existingHost) ? existingHost : (existingHost.ownerDocument ?? document).createComment("");
5389
+ if (placeholder !== existingHost) existingHost.parentNode?.insertBefore(placeholder, existingHost);
5390
+ const host = placeholder;
5391
+ host.__ASKR_INSTANCE = instance;
5392
+ host.__ASKR_INSTANCES = [instance];
5393
+ instance._placeholder = placeholder;
5394
+ mountInstanceInline(instance, null);
5395
+ retainReplacementOwnerChain(placeholder, instance, retainedInstances);
5396
+ return placeholder;
5397
+ }
5398
+ function itemInstanceHydrationComplete(host) {
5399
+ const scope = host.__ASKR_INSTANCE?.scope;
5400
+ if (scope) scope.hydrationPending = false;
5401
+ }
5226
5402
  function materializeComponentResultNode(childInstance, result, parentNamespace) {
5227
5403
  const dom = getRendererDOMHost().createComponentResultNode(childInstance.fn, result, parentNamespace);
5228
5404
  if (dom instanceof Element) {
@@ -5240,7 +5416,17 @@ function materializeComponentResultNode(childInstance, result, parentNamespace)
5240
5416
  mountInstanceInline(childInstance, null);
5241
5417
  return placeholder;
5242
5418
  }
5243
- if (!_isDOMElement(result) || result.type !== __CONTROL_BOUNDARY__ && !isFragmentVNode(result)) {
5419
+ if (dom instanceof Comment) {
5420
+ const host = dom;
5421
+ const instances = host.__ASKR_INSTANCES ?? [];
5422
+ if (!instances.includes(childInstance)) instances.push(childInstance);
5423
+ host.__ASKR_INSTANCES = instances;
5424
+ host.__ASKR_INSTANCE = instances[0] ?? childInstance;
5425
+ childInstance._placeholder = dom;
5426
+ mountInstanceInline(childInstance, null);
5427
+ return dom;
5428
+ }
5429
+ if (!isTransparentComponentResult(result)) {
5244
5430
  const host = document.createElement("div");
5245
5431
  host.appendChild(dom);
5246
5432
  host.__ASKR_WRAPPER_HOST = true;
@@ -5364,15 +5550,16 @@ function resolveWrapperHostResult(host, result, snapshot, retainedInstances) {
5364
5550
  }
5365
5551
  //#endregion
5366
5552
  //#region src/renderer/component-host.ts
5367
- function syncComponentElement(currentDom, node, type, props, parentNamespace, forceChildrenUpdate = false, retainedHostInstances) {
5553
+ function syncComponentElement(currentDom, node, type, props, parentNamespace, forceChildrenUpdate = false, retainedHostInstances, hydrationRangeEnd, preserveHydrationCursorOnEmpty = false) {
5368
5554
  const existingHost = currentDom instanceof Element || currentDom instanceof Comment ? currentDom : null;
5369
5555
  const existingInstance = existingHost ? existingHost instanceof Comment ? findStableHostInstanceByType(existingHost, type, node, getCurrentInstance(), 0) : findHostInstanceByType(existingHost, type, node, getCurrentInstance(), 0) : null;
5370
5556
  if (!existingHost) return null;
5557
+ if (!canReconcileComponentHost(existingHost, Boolean(existingInstance))) return null;
5371
5558
  const domHost = getRendererDOMHost();
5372
5559
  if (!existingInstance || existingInstance.fn !== type) {
5373
- if (!(existingHost instanceof Element)) return null;
5560
+ if (!(existingHost instanceof Element) && !isSSRPortalHydrationAnchor(existingHost)) return null;
5374
5561
  const snapshot = getVNodeContextFrame(node) || getCurrentContextFrame() || null;
5375
- const hydrationInstance = createComponentInstance(nextComponentInstanceId(), type, props || {}, existingHost);
5562
+ const hydrationInstance = createComponentInstance(nextComponentInstanceId(), type, props || {}, existingHost instanceof Element ? existingHost : null);
5376
5563
  setComponentOwnershipIdentity(hydrationInstance, node, getCurrentInstance(), 0);
5377
5564
  hydrationInstance.isRoot = isRouteRootComponentVNode(node);
5378
5565
  hydrationInstance.portalScope = getCurrentInstance()?.portalScope ?? hydrationInstance.portalScope;
@@ -5388,7 +5575,13 @@ function syncComponentElement(currentDom, node, type, props, parentNamespace, fo
5388
5575
  const result = withContext(snapshot, () => renderComponentInline(hydrationInstance));
5389
5576
  if (isPromiseLike(result)) throw new Error("Async components are not supported. Components must return synchronously.");
5390
5577
  const scopedResult = markVNodeTreeWithContextFrame(result, snapshot ?? null);
5391
- if (scopedResult && typeof scopedResult === "object" && "type" in scopedResult && typeof scopedResult.type === "string" && tagNamesEqualIgnoreCase$1(existingHost.tagName, scopedResult.type)) {
5578
+ const emptyPlaceholder = materializeEmptyHydrationPlaceholder(existingHost, hydrationInstance, liveRetainedInstances, scopedResult, preserveHydrationCursorOnEmpty);
5579
+ if (emptyPlaceholder) return emptyPlaceholder;
5580
+ if (hydrationRangeEnd !== void 0 && isTransparentComponentRangeResult(scopedResult)) {
5581
+ const adoptedHost = adoptHydratedComponentRange(existingHost, hydrationInstance, scopedResult, hydrationRangeEnd, forceChildrenUpdate || hydrationInstance.mounted === false, liveRetainedInstances);
5582
+ if (adoptedHost) return adoptedHost;
5583
+ }
5584
+ if (existingHost instanceof Element && scopedResult && typeof scopedResult === "object" && "type" in scopedResult && typeof scopedResult.type === "string" && tagNamesEqualIgnoreCase$1(existingHost.tagName, scopedResult.type)) {
5392
5585
  withContext(snapshot, () => {
5393
5586
  domHost.updateElementFromVnode(existingHost, inheritComponentKey(scopedResult, node), true, forceChildrenUpdate || hydrationInstance.mounted === false);
5394
5587
  materializeKey(existingHost, node, props);
@@ -5398,7 +5591,12 @@ function syncComponentElement(currentDom, node, type, props, parentNamespace, fo
5398
5591
  return existingHost;
5399
5592
  }
5400
5593
  const resolvedResult = resolveHostNestedComponentResult(existingHost, hydrationInstance, scopedResult, snapshot ?? null, liveRetainedInstances);
5401
- if (_isDOMElement(resolvedResult) && typeof resolvedResult.type === "string" && tagNamesEqualIgnoreCase$1(existingHost.tagName, resolvedResult.type)) {
5594
+ if (adoptEmptySSRPortalHydrationHost(existingHost, hydrationInstance, liveRetainedInstances, resolvedResult)) return existingHost;
5595
+ if (hydrationRangeEnd !== void 0 && isTransparentComponentRangeResult(resolvedResult)) {
5596
+ const adoptedHost = adoptHydratedComponentRange(existingHost, hydrationInstance, resolvedResult, hydrationRangeEnd, forceChildrenUpdate || hydrationInstance.mounted === false, liveRetainedInstances);
5597
+ if (adoptedHost) return adoptedHost;
5598
+ }
5599
+ if (existingHost instanceof Element && _isDOMElement(resolvedResult) && typeof resolvedResult.type === "string" && tagNamesEqualIgnoreCase$1(existingHost.tagName, resolvedResult.type)) {
5402
5600
  withContext(snapshot, () => {
5403
5601
  domHost.updateElementFromVnode(existingHost, inheritComponentKey(resolvedResult, node), true, forceChildrenUpdate || hydrationInstance.mounted === false);
5404
5602
  materializeKey(existingHost, node, props);
@@ -5445,6 +5643,7 @@ function syncComponentElement(currentDom, node, type, props, parentNamespace, fo
5445
5643
  return existingHost;
5446
5644
  }
5447
5645
  const resolvedResult = resolveHostNestedComponentResult(existingHost, existingInstance, scopedResult, snapshot ?? null, liveRetainedInstances);
5646
+ if (existingHost instanceof Comment && syncComponentFragmentRange(existingHost, existingInstance, resolvedResult, forceChildrenUpdate || existingInstance.mounted === false)) return existingHost;
5448
5647
  if (existingHost instanceof Comment && (resolvedResult === null || resolvedResult === void 0 || resolvedResult === false)) {
5449
5648
  retainReplacementOwnerChain(existingHost, existingInstance, liveRetainedInstances);
5450
5649
  for (const instance of liveRetainedInstances) if (instance.target === null || instance._placeholder === existingHost) {
@@ -5465,20 +5664,6 @@ function syncComponentElement(currentDom, node, type, props, parentNamespace, fo
5465
5664
  retainReplacementOwnerChain(replacement, existingInstance, liveRetainedInstances);
5466
5665
  });
5467
5666
  }
5468
- function retainReplacementOwnerChain(host, owner, retainedInstances) {
5469
- const componentHost = host;
5470
- const next = [...componentHost.__ASKR_INSTANCES ?? []];
5471
- for (const instance of retainedInstances) if (!next.includes(instance)) next.push(instance);
5472
- componentHost.__ASKR_INSTANCES = next;
5473
- componentHost.__ASKR_INSTANCE = owner;
5474
- }
5475
- function itemInstanceHydrationComplete(host) {
5476
- const instance = host.__ASKR_INSTANCE;
5477
- if (instance) {
5478
- const scope = instance.scope;
5479
- if (scope) scope.hydrationPending = false;
5480
- }
5481
- }
5482
5667
  function createComponentElement(node, type, props, parentNamespace) {
5483
5668
  const snapshot = getVNodeContextFrame(node) || getCurrentContextFrame();
5484
5669
  const componentFn = type;
@@ -6529,6 +6714,10 @@ function replaceComponentRange(instance, result, placeholder) {
6529
6714
  const parent = placeholder.parentNode;
6530
6715
  if (!previousRange || previousRange.single || previousRange.start !== placeholder || !(parent instanceof Element)) return null;
6531
6716
  if (syncComponentFragmentRange(placeholder, instance, result, false)) return placeholder;
6717
+ if (_isDOMElement(result) && hasTransparentComponentResult(result.type)) {
6718
+ const retainedInstances = createRetainedHostInstanceSet(instance);
6719
+ if (syncComponentFragmentRange(placeholder, instance, resolveHostNestedComponentResult(placeholder, instance, result, getVNodeContextFrame(result) ?? instance.ownerFrame ?? null, retainedInstances), false)) return placeholder;
6720
+ }
6532
6721
  const next = createDetachedRange(materializeComponentResultNode(instance, result, getParentNamespace(parent)), instance);
6533
6722
  const nextHost = next.range.start;
6534
6723
  nextHost.__ASKR_INSTANCE = instance;