@askrjs/askr 0.0.78 → 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.
@@ -2576,384 +2576,410 @@ function canReuseStaticSubtree(el, vnode) {
2576
2576
  return true;
2577
2577
  }
2578
2578
  //#endregion
2579
- //#region src/renderer/element-children.ts
2580
- const rendererReactiveChildDOMHost = {
2581
- createDOMNode: (node, parentNamespace) => getRendererDOMHost().createDOMNode(node, parentNamespace),
2582
- updateElementChildren: (el, children, forceUpdate) => updateElementChildren$1(el, children, forceUpdate)
2583
- };
2584
- function updateElementChildren$1(el, children, forceUpdate = false) {
2585
- const directControlBoundary = getDirectControlBoundaryVNode(children);
2586
- if (directControlBoundary) {
2587
- const controlState = getControlBoundaryState(directControlBoundary);
2588
- if (!controlState) throw new Error("[updateElementChildren] Control boundary missing internal state");
2589
- registerControlBoundaryCommitOwner(el, controlState);
2590
- commitForBoundaryChildren(el, controlState, evaluateControlBoundaryState(controlState));
2591
- return;
2592
- }
2593
- clearControlBoundaryCommitOwner(el);
2594
- if (children === null || children === void 0) {
2595
- for (let n = el.firstChild; n;) {
2596
- const next = n.nextSibling;
2597
- teardownNodeSubtree(n);
2598
- 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);
2599
2615
  }
2600
- el.textContent = "";
2601
- return;
2602
- }
2603
- if (!Array.isArray(children) && isFragmentVNode(children)) {
2604
- updateUnkeyedChildren(el, normalizeComponentChildren(children), forceUpdate);
2605
- 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;
2606
2621
  }
2607
- if (!Array.isArray(children) && (typeof children === "string" || typeof children === "number")) {
2608
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2609
- const s = String(children);
2610
- const t = el.firstChild;
2611
- if (t.data !== s) t.data = s;
2612
- } else {
2613
- for (let n = el.firstChild; n;) {
2614
- const next = n.nextSibling;
2615
- teardownNodeSubtree(n);
2616
- n = next;
2617
- }
2618
- el.textContent = String(children);
2619
- }
2620
- 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);
2621
2648
  }
2622
- if (Array.isArray(children)) {
2623
- const normalizedChildren = normalizeComponentChildren(children);
2624
- if (trySyncScalarChildSequenceInPlace(el, normalizedChildren, rendererReactiveChildDOMHost)) {
2625
- keyedElements.delete(el);
2626
- return;
2627
- }
2628
- if (normalizedChildren.some(isControlBoundaryVNode)) {
2629
- updateMixedControlChildren(el, normalizedChildren, forceUpdate);
2630
- keyedElements.delete(el);
2631
- return;
2632
- }
2633
- if (hasKeyedVNodeChildren(normalizedChildren)) {
2634
- const newKeyMap = reconcileKeyedChildren(el, normalizedChildren, getOrBuildDomKeyMap(el));
2635
- keyedElements.set(el, newKeyMap);
2636
- return;
2637
- }
2638
- if (isBulkTextFastPathEligible(el, normalizedChildren)) {
2639
- performBulkTextReplace(el, normalizedChildren);
2640
- 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;
2641
2668
  return;
2642
2669
  }
2643
- updateUnkeyedChildren(el, normalizedChildren, forceUpdate);
2644
- return;
2645
2670
  }
2646
- if (_isDOMElement(children)) {
2647
- 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);
2648
2685
  return;
2649
2686
  }
2650
- for (let n = el.firstChild; n;) {
2651
- const next = n.nextSibling;
2652
- teardownNodeSubtree(n);
2653
- 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);
2654
2695
  }
2655
- el.textContent = "";
2656
- const dom = getRendererDOMHost().createDOMNode(children);
2657
- 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
+ });
2658
2705
  }
2659
- function hasKeyedVNodeChildren(children) {
2660
- for (let i = 0; i < children.length; i++) if (extractKey(children[i]) !== void 0) return true;
2661
- 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);
2662
2719
  }
2663
- function isControlBoundaryVNode(child) {
2664
- return _isDOMElement(child) && child.type === __CONTROL_BOUNDARY__;
2720
+ function markReactivePropsDirtySource(source) {
2721
+ markFineGrainedEffectsDirtySource(source);
2665
2722
  }
2666
- function removeRangeAtCursor(parent, cursor) {
2667
- if (!isRangeStart(cursor)) return cursor;
2668
- const end = findRangeEnd(cursor);
2669
- if (!end) return cursor;
2670
- const range = {
2671
- start: cursor,
2672
- end,
2673
- single: false
2723
+ function setupReactiveProp(el, propName, propFn, tagName) {
2724
+ const descriptor = {
2725
+ el,
2726
+ propName,
2727
+ propFn,
2728
+ tagName,
2729
+ lastClassTokens: null
2674
2730
  };
2675
- const next = end.nextSibling;
2676
- removeRange(range, (node) => {
2677
- if (node === range.start || node === range.end) {
2678
- node.parentNode?.removeChild(node);
2679
- 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);
2680
2745
  }
2681
- teardownNodeSubtree(node);
2682
- node.parentNode?.removeChild(node);
2683
2746
  });
2684
- return next;
2685
- }
2686
- function updateMixedControlChildren(parent, children, forceUpdate) {
2687
- clearControlBoundaryCommitOwner(parent);
2688
- const parentNamespace = getParentNamespace(parent);
2689
- const domHost = getRendererDOMHost();
2690
- let cursor = parent.firstChild;
2691
- for (const child of children) {
2692
- if (isControlBoundaryVNode(child)) {
2693
- const controlState = getControlBoundaryState(child);
2694
- if (!controlState) throw new Error("[updateElementChildren] Control boundary missing internal state");
2695
- const childVNodes = evaluateControlBoundaryState(controlState);
2696
- const cursorAfterBoundary = cursor ? isRangeStart(cursor) ? findRangeEnd(cursor)?.nextSibling ?? cursor.nextSibling : cursor.nextSibling : null;
2697
- const ranges = syncControlBoundaryInMixedParent(parent, controlState, childVNodes, cursor);
2698
- if (controlState.kind === "for") {
2699
- const last = ranges[ranges.length - 1];
2700
- cursor = last ? last.end.nextSibling : cursorAfterBoundary?.parentNode === parent ? cursorAfterBoundary : cursor?.parentNode === parent ? cursor : null;
2701
- continue;
2702
- }
2703
- for (const range of ranges) if (range.start.parentNode !== parent) moveRange(parent, range, cursor?.parentNode === parent ? cursor : null);
2704
- else if (cursor?.parentNode === parent) moveRange(parent, range, cursor);
2705
- const last = ranges[ranges.length - 1];
2706
- if (last) {
2707
- const cursorWasReplaced = cursor && !ranges.some((range) => range.start === cursor);
2708
- if (!cursor?.parentNode) cursor = last.end.nextSibling;
2709
- else if (cursorWasReplaced && isRangeStart(cursor)) cursor = findRangeEnd(cursor)?.nextSibling ?? last.end.nextSibling;
2710
- else if (isRangeStart(cursor)) cursor = findRangeEnd(cursor)?.nextSibling ?? last.end.nextSibling;
2711
- else cursor = last.end.nextSibling;
2712
- } else {
2713
- const inactiveBoundaryCursor = cursor?.parentNode === parent ? cursor : cursorAfterBoundary?.parentNode === parent ? cursorAfterBoundary : null;
2714
- const inactiveBoundaryEnd = inactiveBoundaryCursor && isRangeStart(inactiveBoundaryCursor) ? findRangeEnd(inactiveBoundaryCursor) : null;
2715
- cursor = inactiveBoundaryEnd?.previousSibling === inactiveBoundaryCursor ? inactiveBoundaryEnd.nextSibling : inactiveBoundaryCursor;
2716
- }
2717
- 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);
2718
2759
  }
2719
- if (cursor) cursor = removeRangeAtCursor(parent, cursor);
2720
- if (cursor && (typeof child === "string" || typeof child === "number") && cursor.nodeType === 3) {
2721
- cursor.data = String(child);
2722
- cursor = cursor.nextSibling;
2723
- 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);
2724
2805
  }
2725
- if (cursor instanceof Element && _isDOMElement(child) && typeof child.type === "string" && tagsEqualIgnoreCase(cursor.tagName, child.type)) {
2726
- domHost.updateElementFromVnode(cursor, child, true, forceUpdate);
2727
- 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);
2728
2814
  continue;
2729
2815
  }
2730
- if (cursor && _isDOMElement(child) && typeof child.type === "function") {
2731
- const synced = domHost.syncComponentElement(cursor, child, child.type, (child.props ?? {}) || {}, parentNamespace, forceUpdate, void 0, void 0, true);
2732
- if (synced) {
2733
- cursor = synced.nextSibling;
2734
- continue;
2735
- }
2736
- }
2737
- const created = domHost.createDOMNode(child, parentNamespace);
2738
- if (!created) {
2739
- 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);
2740
2821
  continue;
2741
2822
  }
2742
- const materialized = createDetachedRange(created);
2743
- const old = cursor;
2744
- moveRange(parent, materialized.range, old);
2745
- if (old) {
2746
- teardownNodeSubtree(old);
2747
- old.parentNode?.removeChild(old);
2748
- }
2749
- cursor = materialized.range.end.nextSibling;
2750
- }
2751
- while (cursor) {
2752
- const next = removeRangeAtCursor(parent, cursor);
2753
- cursor = next === cursor ? cursor.nextSibling : next;
2754
- }
2755
- }
2756
- function isEmptyChild(child) {
2757
- return child === null || child === void 0 || child === false;
2758
- }
2759
- function getOrBuildDomKeyMap(parent) {
2760
- let keyMap = keyedElements.get(parent);
2761
- if (!keyMap) {
2762
- keyMap = /* @__PURE__ */ new Map();
2763
- for (let child = parent.firstElementChild; child; child = child.nextElementSibling) {
2764
- const key = getMaterializedKey(child);
2765
- 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;
2766
2826
  }
2767
- if (keyMap.size > 0) keyedElements.set(parent, keyMap);
2827
+ applyScalarPropValue(el, key, value, tagName);
2768
2828
  }
2769
- return keyMap.size > 0 ? keyMap : void 0;
2770
2829
  }
2771
- function updateUnkeyedChildren(parent, newChildren, forceUpdate = false) {
2772
- const parentNamespace = getParentNamespace(parent);
2773
- const domHost = getRendererDOMHost();
2774
- const trySyncComponentChild = (currentDom, next, hydrationRangeEnd) => {
2775
- if (typeof next.type !== "function") return null;
2776
- return domHost.syncComponentElement(currentDom, next, next.type, (next.props ?? {}) || {}, parentNamespace, forceUpdate, void 0, hydrationRangeEnd, true);
2777
- };
2778
- const hasText = newChildren.some((c) => typeof c === "string" || typeof c === "number");
2779
- const hasElements = newChildren.some((c) => _isDOMElement(c));
2780
- const hasEmptyChildren = newChildren.some(isEmptyChild);
2781
- const hasComponentChildren = newChildren.some((c) => _isDOMElement(c) && typeof c.type === "function");
2782
- const hasNonElementDomChildren = parent.childNodes.length !== parent.children.length;
2783
- if (!hasEmptyChildren && !hasText && !hasComponentChildren && !hasNonElementDomChildren && hasElements && parent.children.length === newChildren.length) {
2784
- const c = parent.children;
2785
- for (let i = 0; i < newChildren.length; i++) {
2786
- const next = newChildren[i];
2787
- const current = c[i];
2788
- if (!current || next === void 0) continue;
2789
- if (_isDOMElement(next) && typeof next.type === "string") if (tagsEqualIgnoreCase(current.tagName, next.type)) domHost.updateElementFromVnode(current, next, true, forceUpdate);
2790
- else {
2791
- const dom = domHost.createDOMNode(next, parentNamespace);
2792
- if (dom) {
2793
- teardownNodeSubtree(current);
2794
- parent.replaceChild(dom, current);
2795
- }
2796
- }
2797
- else if (_isDOMElement(next)) {
2798
- if (trySyncControlBoundaryChild(parent, current, next)) continue;
2799
- if (!trySyncComponentChild(current, next)) {
2800
- const dom = domHost.createDOMNode(next, parentNamespace);
2801
- if (dom) {
2802
- teardownNodeSubtree(current);
2803
- if (current.parentNode === parent) parent.replaceChild(dom, current);
2804
- else if (dom.parentNode !== parent) parent.insertBefore(dom, parent.children[i] ?? null);
2805
- }
2806
- }
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);
2807
2852
  } else {
2808
- const dom = domHost.createDOMNode(next, parentNamespace);
2809
- if (dom) {
2810
- teardownNodeSubtree(current);
2811
- parent.replaceChild(dom, current);
2812
- }
2853
+ const entry = existingReactiveProps?.get(key);
2854
+ if (entry) {
2855
+ entry.cleanup();
2856
+ existingReactiveProps?.delete(key);
2857
+ } else applyScalarPropValue(el, key, value, domVNode.type);
2813
2858
  }
2859
+ continue;
2814
2860
  }
2815
- return;
2816
- }
2817
- const existing = Array.from(parent.children);
2818
- if (hasText || hasComponentChildren || hasEmptyChildren || hasNonElementDomChildren) {
2819
- const allNodes = Array.from(parent.childNodes);
2820
- for (let i = 0; i < Math.max(allNodes.length, newChildren.length); i += 1) {
2821
- const currentNode = allNodes[i];
2822
- const next = newChildren[i];
2823
- const nextIsEmpty = isEmptyChild(next);
2824
- if (nextIsEmpty && currentNode) {
2825
- teardownNodeSubtree(currentNode);
2826
- currentNode.parentNode?.removeChild(currentNode);
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;
2827
2868
  continue;
2828
2869
  }
2829
- if (!currentNode && !nextIsEmpty) {
2830
- const dom = domHost.createDOMNode(next, parentNamespace);
2831
- 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
+ });
2832
2895
  continue;
2833
2896
  }
2834
- if (!currentNode || nextIsEmpty) continue;
2835
- if (typeof next === "string" || typeof next === "number") if (currentNode.nodeType === 3) currentNode.data = String(next);
2836
- else {
2837
- const textNode = document.createTextNode(String(next));
2838
- teardownNodeSubtree(currentNode);
2839
- parent.replaceChild(textNode, currentNode);
2897
+ if (hydrationDirectListenerDepth > 0 && !existingListeners?.has(listenerKey) && !getDelegatedHandlerForElement(el, eventName)) {
2898
+ addTrackedListener(el, eventName, value, eventCapture, false, true);
2899
+ continue;
2840
2900
  }
2841
- else if (_isDOMElement(next)) if (currentNode.nodeType === 1) {
2842
- const currentEl = currentNode;
2843
- if (typeof next.type === "string") if (tagsEqualIgnoreCase(currentEl.tagName, next.type)) domHost.updateElementFromVnode(currentEl, next, true, forceUpdate);
2844
- else {
2845
- const dom = domHost.createDOMNode(next, parentNamespace);
2846
- if (dom) {
2847
- teardownNodeSubtree(currentEl);
2848
- if (currentNode.parentNode === parent) parent.replaceChild(dom, currentNode);
2849
- else if (dom.parentNode !== parent) parent.insertBefore(dom, parent.childNodes[i] ?? null);
2850
- }
2851
- }
2852
- else {
2853
- if (trySyncControlBoundaryChild(parent, currentNode, next)) continue;
2854
- const remainingExpected = newChildren.length - i - 1;
2855
- const hydrationRangeEndIndex = allNodes.length - remainingExpected;
2856
- const synced = trySyncComponentChild(currentEl, next, hydrationRangeEndIndex > i ? allNodes[hydrationRangeEndIndex] ?? null : void 0);
2857
- if (synced && isRangeStart(synced)) {
2858
- allNodes.splice(i, Math.max(1, hydrationRangeEndIndex - i), synced);
2859
- continue;
2860
- }
2861
- if (synced && synced !== currentNode && synced.nextSibling === currentNode) {
2862
- allNodes.splice(i, 0, synced);
2863
- continue;
2864
- }
2865
- if (!synced) {
2866
- const dom = domHost.createDOMNode(next, parentNamespace);
2867
- if (dom) {
2868
- teardownNodeSubtree(currentEl);
2869
- parent.replaceChild(dom, currentNode);
2870
- }
2871
- }
2872
- }
2873
- } else {
2874
- if (typeof next.type === "function") {
2875
- const remainingExpected = newChildren.length - i - 1;
2876
- const hydrationRangeEndIndex = allNodes.length - remainingExpected;
2877
- const synced = trySyncComponentChild(currentNode, next, hydrationRangeEndIndex > i ? allNodes[hydrationRangeEndIndex] ?? null : void 0);
2878
- if (synced && isRangeStart(synced)) {
2879
- allNodes.splice(i, Math.max(1, hydrationRangeEndIndex - i), synced);
2880
- continue;
2881
- }
2882
- if (synced && synced !== currentNode && synced.nextSibling === currentNode) {
2883
- allNodes.splice(i, 0, synced);
2884
- continue;
2885
- }
2886
- if (synced) continue;
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;
2887
2919
  }
2888
- const dom = domHost.createDOMNode(next, parentNamespace);
2889
- if (dom) {
2890
- teardownNodeSubtree(currentNode);
2891
- parent.replaceChild(dom, currentNode);
2920
+ if (!useDelegation && !existing.isDelegated && existing.updateHandler) {
2921
+ existing.updateHandler(value);
2922
+ existing.original = value;
2923
+ continue;
2892
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);
2893
2928
  }
2894
- }
2895
- 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);
2896
2945
  }
2897
- if (newChildren.length === 1 && existing.length === 0 && parent.childNodes.length === 1) {
2898
- const firstNewChild = newChildren[0];
2899
- const firstExisting = parent.firstChild;
2900
- if ((typeof firstNewChild === "string" || typeof firstNewChild === "number") && firstExisting?.nodeType === 3) {
2901
- firstExisting.data = String(firstNewChild);
2902
- return;
2903
- }
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);
2904
2964
  }
2905
- if (existing.length === 0 && parent.childNodes.length > 0) {
2906
- for (let n = parent.firstChild; n;) {
2907
- const next = n.nextSibling;
2908
- teardownNodeSubtree(n);
2909
- n = next;
2910
- }
2911
- 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);
2912
2969
  }
2913
- const max = Math.max(existing.length, newChildren.length);
2914
- for (let i = 0; i < max; i++) {
2915
- const current = existing[i];
2916
- const next = newChildren[i];
2917
- const nextIsEmpty = isEmptyChild(next);
2918
- if (nextIsEmpty && current) {
2919
- teardownNodeSubtree(current);
2920
- current.remove();
2921
- continue;
2922
- }
2923
- if (!current && !nextIsEmpty) {
2924
- const dom = domHost.createDOMNode(next, parentNamespace);
2925
- if (dom) parent.appendChild(dom);
2926
- continue;
2927
- }
2928
- if (!current || nextIsEmpty) continue;
2929
- if (typeof next === "string" || typeof next === "number") {
2930
- const textNode = document.createTextNode(String(next));
2931
- teardownNodeSubtree(current);
2932
- parent.replaceChild(textNode, current);
2933
- } else if (_isDOMElement(next)) {
2934
- if (typeof next.type === "string") if (tagsEqualIgnoreCase(current.tagName, next.type)) domHost.updateElementFromVnode(current, next, true, forceUpdate);
2935
- else {
2936
- const dom = domHost.createDOMNode(next, parentNamespace);
2937
- if (dom) {
2938
- teardownNodeSubtree(current);
2939
- parent.replaceChild(dom, current);
2940
- }
2941
- }
2942
- else if (!trySyncComponentChild(current, next)) {
2943
- const dom = domHost.createDOMNode(next, parentNamespace);
2944
- if (dom) {
2945
- teardownNodeSubtree(current);
2946
- if (current.parentNode === parent) parent.replaceChild(dom, current);
2947
- else if (dom.parentNode !== parent) parent.insertBefore(dom, parent.children[i] ?? null);
2948
- }
2949
- }
2950
- } else {
2951
- const dom = domHost.createDOMNode(next);
2952
- if (dom) {
2953
- teardownNodeSubtree(current);
2954
- 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);
2955
2980
  }
2956
- }
2981
+ });
2982
+ if (existingReactiveProps.size === 0) elementReactivePropsCleanup.delete(el);
2957
2983
  }
2958
2984
  }
2959
2985
  //#endregion
@@ -3238,471 +3264,467 @@ function runRetainedElementUpdate(element, cleanupRangeNode, update, onError, bi
3238
3264
  }
3239
3265
  }
3240
3266
  //#endregion
3241
- //#region src/renderer/hydration-listener-transaction.ts
3242
- let currentTransaction = null;
3243
- function beginHydrationListenerTransaction() {
3244
- const transaction = {
3245
- parent: currentTransaction,
3246
- stages: [],
3247
- active: true
3248
- };
3249
- currentTransaction = transaction;
3250
- 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;
3251
3272
  }
3252
- function stageHydrationListener(stage) {
3253
- if (!currentTransaction?.active) return false;
3254
- currentTransaction.stages.push(stage);
3255
- return true;
3273
+ function markHydrationHostAdopted(host) {
3274
+ adoptedHydrationHosts.add(host);
3256
3275
  }
3257
- function hasStagedHydrationListener(target, eventName, capture) {
3258
- return !!currentTransaction?.stages.some((stage) => stage.target === target && stage.eventName === eventName && stage.capture === capture);
3276
+ function isAdoptedHydrationHost(host) {
3277
+ return adoptedHydrationHosts.has(host);
3259
3278
  }
3260
- function closeTransaction(transaction) {
3261
- if (!transaction.active) return false;
3262
- transaction.active = false;
3263
- currentTransaction = transaction.parent;
3264
- return true;
3279
+ function canReconcileComponentHost(host, hasMatchingInstance) {
3280
+ return hasMatchingInstance || Boolean(host.__ASKR_INSTANCE) || Boolean(host.__ASKR_INSTANCES?.length) || isAdoptedHydrationHost(host) || isHydrationAdoptionScopeActive();
3265
3281
  }
3266
- function commitHydrationListenerTransaction(transaction) {
3267
- if (!closeTransaction(transaction)) return;
3268
- const stages = [...transaction.stages].sort((left, right) => {
3269
- if (left.kind !== right.kind) return left.kind === "delegated" ? -1 : 1;
3270
- return left.eventName.localeCompare(right.eventName);
3271
- });
3272
- const published = [];
3282
+ function withIntrinsicHydrationAdoption(work) {
3283
+ hydrationAdoptionDepth += 1;
3273
3284
  try {
3274
- for (const stage of stages) {
3275
- stage.publish();
3276
- published.push(stage);
3277
- }
3278
- } catch (error) {
3279
- for (let index = published.length - 1; index >= 0; index -= 1) try {
3280
- published[index].rollback();
3281
- } catch {}
3282
- throw error;
3285
+ return work();
3286
+ } finally {
3287
+ hydrationAdoptionDepth -= 1;
3283
3288
  }
3284
3289
  }
3285
- function discardHydrationListenerTransaction(transaction) {
3286
- if (!closeTransaction(transaction)) return;
3287
- for (let index = transaction.stages.length - 1; index >= 0; index -= 1) try {
3288
- transaction.stages[index].rollback();
3289
- } catch {}
3290
- }
3291
- function getCurrentHydrationListenerTransaction() {
3292
- return currentTransaction?.active ? currentTransaction : null;
3293
- }
3294
- //#endregion
3295
- //#region src/renderer/prop-bindings.ts
3296
- const reactivePropRegistry = /* @__PURE__ */ new Set();
3297
- const hydrationDirectListeners = /* @__PURE__ */ new WeakMap();
3298
- let hydrationDirectListenerDepth = 0;
3299
- function beginHydrationDirectListenerMode() {
3300
- 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];
3301
3294
  }
3302
- function endHydrationDirectListenerMode() {
3303
- hydrationDirectListenerDepth = Math.max(0, hydrationDirectListenerDepth - 1);
3304
- }
3305
- function markHydrationDirectListener(element, listenerKey) {
3306
- let listeners = hydrationDirectListeners.get(element);
3307
- if (!listeners) {
3308
- listeners = /* @__PURE__ */ new Set();
3309
- 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;
3310
3304
  }
3311
- 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;
3312
3317
  }
3313
- function isHydrationDirectListener(element, listenerKey) {
3314
- 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
+ }
3315
3331
  }
3316
- function clearHydrationDirectListener(element, listenerKey) {
3317
- const listeners = hydrationDirectListeners.get(element);
3318
- listeners?.delete(listenerKey);
3319
- 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;
3320
3337
  }
3321
- function addTrackedListener(el, eventName, handler, capture = false, fresh = false, forceDirect = false) {
3322
- const effectiveForceDirect = forceDirect || hydrationDirectListenerDepth > 0;
3323
- const useDelegation = !effectiveForceDirect && !capture && isEventDelegationEnabled() && isDelegatedEvent(eventName);
3324
- const listenerKey = getEventListenerKey(eventName, capture);
3325
- if (effectiveForceDirect && isHydrationDirectListener(el, listenerKey)) {
3326
- const existing = elementListeners.get(el)?.get(listenerKey);
3327
- if (existing) {
3328
- existing.updateHandler?.(handler);
3329
- existing.original = handler;
3330
- return;
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;
3352
+ }
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;
3331
3359
  }
3360
+ el.textContent = "";
3361
+ return;
3332
3362
  }
3333
- if (!effectiveForceDirect && !hasStagedHydrationListener(el, eventName, capture) && stageHydrationListener({
3334
- kind: "direct",
3335
- target: el,
3336
- eventName,
3337
- capture,
3338
- publish: () => addTrackedListener(el, eventName, handler, capture, fresh, true),
3339
- rollback: () => {
3340
- removeTrackedListener(el, eventName, capture);
3341
- clearHydrationDirectListener(el, listenerKey);
3363
+ if (!Array.isArray(children) && isFragmentVNode(children)) {
3364
+ updateUnkeyedChildren(el, normalizeComponentChildren(children), forceUpdate);
3365
+ return;
3366
+ }
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);
3342
3379
  }
3343
- })) return;
3344
- if (useDelegation) {
3345
- if (fresh) addFreshDelegatedListener(el, eventName, handler, handler, void 0);
3346
- else addDelegatedListener(el, eventName, handler, handler, void 0);
3347
3380
  return;
3348
3381
  }
3349
- const options = getEventListenerOptions(eventName, capture);
3350
- const mutableHandler = createMutableWrappedHandler(handler, true);
3351
- const trackedHandler = mutableHandler.handler;
3352
- if (options !== void 0) el.addEventListener(eventName, trackedHandler, options);
3353
- else el.addEventListener(eventName, trackedHandler);
3354
- if (effectiveForceDirect) {
3355
- if (!capture && getDelegatedHandlerForElement(el, eventName)) removeDelegatedListener(el, eventName);
3356
- markHydrationDirectListener(el, listenerKey);
3382
+ if (Array.isArray(children)) {
3383
+ const normalizedChildren = normalizeComponentChildren(children);
3384
+ if (trySyncScalarChildSequenceInPlace(el, normalizedChildren, rendererReactiveChildDOMHost)) {
3385
+ keyedElements.delete(el);
3386
+ return;
3387
+ }
3388
+ if (normalizedChildren.some(isControlBoundaryVNode)) {
3389
+ updateMixedControlChildren(el, normalizedChildren, forceUpdate);
3390
+ keyedElements.delete(el);
3391
+ return;
3392
+ }
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;
3357
3405
  }
3358
- if (!elementListeners.has(el)) elementListeners.set(el, /* @__PURE__ */ new Map());
3359
- elementListeners.get(el).set(listenerKey, {
3360
- handler: trackedHandler,
3361
- original: handler,
3362
- eventName,
3363
- options,
3364
- isDelegated: false,
3365
- updateHandler: mutableHandler?.updateHandler
3366
- });
3367
- }
3368
- function removeTrackedListener(el, eventName, capture) {
3369
- if (!capture && getDelegatedHandlerForElement(el, eventName)) {
3370
- removeDelegatedListener(el, eventName);
3406
+ if (_isDOMElement(children)) {
3407
+ updateUnkeyedChildren(el, [children], forceUpdate);
3371
3408
  return;
3372
3409
  }
3373
- const listenerKey = getEventListenerKey(eventName, capture);
3374
- const entry = elementListeners.get(el)?.get(listenerKey);
3375
- if (!entry) return;
3376
- if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
3377
- else el.removeEventListener(entry.eventName, entry.handler);
3378
- const listeners = elementListeners.get(el);
3379
- listeners?.delete(listenerKey);
3380
- if (listeners?.size === 0) elementListeners.delete(el);
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);
3381
3418
  }
3382
- function markReactivePropsDirtySource(source) {
3383
- markFineGrainedEffectsDirtySource(source);
3419
+ function hasKeyedVNodeChildren(children) {
3420
+ for (let i = 0; i < children.length; i++) if (extractKey(children[i]) !== void 0) return true;
3421
+ return false;
3384
3422
  }
3385
- function setupReactiveProp(el, propName, propFn, tagName) {
3386
- const descriptor = {
3387
- el,
3388
- propName,
3389
- propFn,
3390
- tagName,
3391
- lastClassTokens: null
3423
+ function isControlBoundaryVNode(child) {
3424
+ return _isDOMElement(child) && child.type === __CONTROL_BOUNDARY__;
3425
+ }
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
3392
3434
  };
3393
- let effectHandle = null;
3394
- reactivePropRegistry.add(descriptor);
3395
- effectHandle = createFineGrainedEffect({
3396
- lane: "reactive",
3397
- compute: () => descriptor.propFn(),
3398
- commit: (value, previousValue) => {
3399
- applyScalarPropValue(el, propName, value, tagName, previousValue, descriptor);
3400
- },
3401
- equals: (previousValue, nextValue) => {
3402
- if (Object.is(previousValue, nextValue)) return true;
3403
- return false;
3404
- },
3405
- onError: (err) => {
3406
- if (getRuntimeEnv().NODE_ENV !== "production") logger.warn("[Askr] Reactive prop update failed:", err);
3435
+ const next = end.nextSibling;
3436
+ removeRange(range, (node) => {
3437
+ if (node === range.start || node === range.end) {
3438
+ node.parentNode?.removeChild(node);
3439
+ return;
3407
3440
  }
3441
+ teardownNodeSubtree(node);
3442
+ node.parentNode?.removeChild(node);
3408
3443
  });
3409
- const cleanup = () => {
3410
- reactivePropRegistry.delete(descriptor);
3411
- effectHandle?.cleanup();
3412
- effectHandle = null;
3413
- };
3414
- const updateFn = (nextFn) => {
3415
- if (!effectHandle) return;
3416
- descriptor.propFn = nextFn;
3417
- try {
3418
- effectHandle.updateCompute(nextFn);
3419
- } catch (err) {
3420
- if (getRuntimeEnv().NODE_ENV !== "production") logger.warn("[Askr] Reactive prop update failed:", err);
3421
- }
3422
- };
3423
- return {
3424
- cleanup,
3425
- updateFn
3426
- };
3427
- }
3428
- function getOrCreateReactivePropsCleanupMap(el) {
3429
- return getElementReactivePropsCleanupMap(el, true);
3444
+ return next;
3430
3445
  }
3431
- /** @internal Create a standalone reactive prop entry for rollback restoration. */
3432
- function createReactivePropCleanupEntry(el, propName, propFn, tagName) {
3433
- const reactive = setupReactiveProp(el, propName, propFn, tagName);
3434
- return {
3435
- cleanup: reactive.cleanup,
3436
- updateFn: (nextValue) => {
3437
- reactive.updateFn(nextValue);
3438
- },
3439
- restoreFn: (nextValue) => createReactivePropCleanupEntry(el, propName, nextValue, tagName),
3440
- fnRef: propFn
3441
- };
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;
3452
+ }
3453
+ const next = cursor.nextSibling;
3454
+ teardownNodeSubtree(cursor);
3455
+ if (cursor.parentNode === parent) parent.removeChild(cursor);
3456
+ return next;
3442
3457
  }
3443
- function hasTrackedElementPropBindings(el) {
3444
- const existingListeners = elementListeners.get(el);
3445
- return existingListeners !== void 0 && existingListeners.size > 0 || getElementReactivePropCleanupSize(el) > 0;
3446
- }
3447
- function hasAnyElementBindingState(el) {
3448
- return hasTrackedElementPropBindings(el) || getDelegatedHandlersForElement(el) !== void 0 || elementRefs.has(el);
3449
- }
3450
- function applyMatchingElementBindings(el, props) {
3451
- if (Object.prototype.hasOwnProperty.call(props, "ref")) updateElementRef(el, props.ref);
3452
- for (const key in props) {
3453
- const eventProp = parseEventProp(key);
3454
- if (!eventProp) continue;
3455
- const value = props[key];
3456
- if (typeof value === "function") addTrackedListener(el, eventProp.eventName, value, eventProp.capture);
3457
- }
3458
- }
3459
- /** @internal Attach stateful bindings to a newly cloned blueprint element. */
3460
- function applyFreshElementBindings(el, props) {
3461
- if (Object.prototype.hasOwnProperty.call(props, "ref")) updateElementRef(el, props.ref);
3462
- for (const key in props) {
3463
- const eventProp = parseEventProp(key);
3464
- if (eventProp) {
3465
- const value = props[key];
3466
- if (typeof value === "function") addTrackedListener(el, eventProp.eventName, value, eventProp.capture, true);
3467
- }
3468
- }
3469
- }
3470
- function applyPropsToElement$1(el, props, tagName, isHydrationSkipped) {
3471
- if (isHydrationSkipped(el)) return;
3472
- for (const key in props) {
3473
- const value = props[key];
3474
- if (key === "ref") {
3475
- updateElementRef(el, value);
3476
- continue;
3477
- }
3478
- if (isSkippedProp(key)) continue;
3479
- if (value === void 0 || value === null || value === false) continue;
3480
- const eventProp = parseEventProp(key);
3481
- if (eventProp) {
3482
- addTrackedListener(el, eventProp.eventName, value, eventProp.capture);
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
+ }
3483
3489
  continue;
3484
3490
  }
3485
- if (typeof value === "function" && key !== "ref") {
3486
- getOrCreateReactivePropsCleanupMap(el).set(key, createReactivePropCleanupEntry(el, key, value, tagName));
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;
3487
3495
  continue;
3488
3496
  }
3489
- applyScalarPropValue(el, key, value, tagName);
3490
- }
3491
- }
3492
- function syncElementPropBindings(el, domVNode, props, usesReactiveChildren) {
3493
- const existingListeners = elementListeners.get(el);
3494
- const existingReactiveProps = getElementReactivePropsCleanupMap(el);
3495
- let desiredListenerKeys = null;
3496
- let desiredDelegatedEventNames = null;
3497
- let desiredReactivePropNames = null;
3498
- if (usesReactiveChildren) (desiredReactivePropNames ??= /* @__PURE__ */ new Set()).add(REACTIVE_CHILDREN_KEY);
3499
- for (const key in props) {
3500
- const value = props[key];
3501
- if (key === "ref") continue;
3502
- if (isSkippedProp(key)) continue;
3503
- const eventProp = parseEventProp(key);
3504
- const eventName = eventProp?.eventName;
3505
- const eventCapture = eventProp?.capture ?? false;
3506
- const listenerKey = eventName === void 0 ? null : getEventListenerKey(eventName, eventCapture);
3507
- if (value === void 0 || value === null || value === false) {
3508
- if (listenerKey && existingListeners?.has(listenerKey)) {
3509
- const entry = existingListeners.get(listenerKey);
3510
- if (entry.isDelegated) removeDelegatedListener(el, entry.eventName);
3511
- else if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
3512
- else el.removeEventListener(entry.eventName, entry.handler);
3513
- existingListeners.delete(listenerKey);
3514
- } else {
3515
- const entry = existingReactiveProps?.get(key);
3516
- if (entry) {
3517
- entry.cleanup();
3518
- existingReactiveProps?.delete(key);
3519
- } else applyScalarPropValue(el, key, value, domVNode.type);
3520
- }
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;
3521
3500
  continue;
3522
3501
  }
3523
- if (typeof value === "function" && !eventProp && key !== "ref") {
3524
- const existingEntry = existingReactiveProps?.get(key);
3525
- if (existingReactiveProps && existingReactiveProps.size > 0) (desiredReactivePropNames ??= /* @__PURE__ */ new Set()).add(key);
3526
- if (existingEntry && existingEntry.fnRef === value) continue;
3527
- if (existingEntry?.updateFn) {
3528
- existingEntry.updateFn(value);
3529
- existingEntry.fnRef = value;
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;
3530
3506
  continue;
3531
3507
  }
3532
- if (existingEntry) existingEntry.cleanup();
3533
- getOrCreateReactivePropsCleanupMap(el).set(key, createReactivePropCleanupEntry(el, key, value, domVNode.type));
3508
+ }
3509
+ const created = domHost.createDOMNode(child, parentNamespace);
3510
+ if (!created) {
3511
+ cursor = cursor?.nextSibling ?? null;
3534
3512
  continue;
3535
3513
  }
3536
- const existingReactiveEntry = existingReactiveProps?.get(key);
3537
- if (existingReactiveEntry) {
3538
- existingReactiveEntry.cleanup();
3539
- existingReactiveProps?.delete(key);
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);
3540
3520
  }
3541
- if (eventProp && listenerKey) {
3542
- const eventName = eventProp.eventName;
3543
- const eventCapture = eventProp.capture;
3544
- const preserveHydrationDirect = existingListeners?.get(listenerKey)?.isDelegated === false && isHydrationDirectListener(el, listenerKey);
3545
- if (getCurrentHydrationListenerTransaction() && !existingListeners?.has(listenerKey) && !getDelegatedHandlerForElement(el, eventName) && !isHydrationDirectListener(el, listenerKey)) {
3546
- if (!hasStagedHydrationListener(el, eventName, eventCapture)) stageHydrationListener({
3547
- kind: "direct",
3548
- target: el,
3549
- eventName,
3550
- capture: eventCapture,
3551
- publish: () => addTrackedListener(el, eventName, value, eventCapture, false, true),
3552
- rollback: () => {
3553
- removeTrackedListener(el, eventName, eventCapture);
3554
- clearHydrationDirectListener(el, listenerKey);
3521
+ cursor = materialized.range.end.nextSibling;
3522
+ }
3523
+ while (cursor) cursor = consumeUnmatchedTailAtCursor(parent, cursor);
3524
+ }
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);
3555
3574
  }
3556
- });
3557
- continue;
3575
+ }
3576
+ } else {
3577
+ const dom = domHost.createDOMNode(next, parentNamespace);
3578
+ if (dom) {
3579
+ teardownNodeSubtree(current);
3580
+ parent.replaceChild(dom, current);
3581
+ }
3558
3582
  }
3559
- if (hydrationDirectListenerDepth > 0 && !existingListeners?.has(listenerKey) && !getDelegatedHandlerForElement(el, eventName)) {
3560
- addTrackedListener(el, eventName, value, eventCapture, false, true);
3583
+ }
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);
3561
3596
  continue;
3562
3597
  }
3563
- const useDelegation = !preserveHydrationDirect && !eventCapture && isEventDelegationEnabled() && isDelegatedEvent(eventName);
3564
- if (useDelegation) (desiredDelegatedEventNames ??= /* @__PURE__ */ new Set()).add(eventName);
3565
- if (useDelegation) {
3566
- const existingDelegated = getDelegatedHandlerForElement(el, eventName);
3567
- if (existingDelegated?.original === value) continue;
3568
- if (existingDelegated && updateDelegatedListener(el, eventName, value, value, void 0)) continue;
3569
- addDelegatedListener(el, eventName, value, value, void 0);
3598
+ if (!currentNode && !nextIsEmpty) {
3599
+ const dom = domHost.createDOMNode(next, parentNamespace);
3600
+ if (dom) parent.appendChild(dom);
3570
3601
  continue;
3571
3602
  }
3572
- (desiredListenerKeys ??= /* @__PURE__ */ new Set()).add(listenerKey);
3573
- const existing = existingListeners?.get(listenerKey);
3574
- if (existing && existing.original === value) continue;
3575
- if (existing) {
3576
- if (useDelegation && existing.isDelegated && updateDelegatedListener(el, eventName, value, value, void 0)) {
3577
- existing.handler = value;
3578
- existing.original = value;
3579
- existing.options = void 0;
3580
- continue;
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
+ }
3581
3620
  }
3582
- if (!useDelegation && !existing.isDelegated && existing.updateHandler) {
3583
- existing.updateHandler(value);
3584
- existing.original = value;
3585
- continue;
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;
3654
+ }
3655
+ if (synced) continue;
3656
+ }
3657
+ const dom = domHost.createDOMNode(next, parentNamespace);
3658
+ if (dom) {
3659
+ teardownNodeSubtree(currentNode);
3660
+ parent.replaceChild(dom, currentNode);
3586
3661
  }
3587
- if (existing.isDelegated) removeDelegatedListener(el, existing.eventName);
3588
- else if (existing.options !== void 0) el.removeEventListener(existing.eventName, existing.handler, existing.options);
3589
- else el.removeEventListener(existing.eventName, existing.handler);
3590
- }
3591
- const options = getEventListenerOptions(eventName, eventCapture);
3592
- const mutableHandler = createMutableWrappedHandler(value, true);
3593
- const trackedHandler = mutableHandler.handler;
3594
- if (options !== void 0) el.addEventListener(eventName, trackedHandler, options);
3595
- else el.addEventListener(eventName, trackedHandler);
3596
- const listenerEntry = {
3597
- handler: trackedHandler,
3598
- original: value,
3599
- eventName,
3600
- options,
3601
- isDelegated: false,
3602
- updateHandler: mutableHandler?.updateHandler
3603
- };
3604
- if (!elementListeners.has(el)) elementListeners.set(el, /* @__PURE__ */ new Map());
3605
- elementListeners.get(el).set(listenerKey, listenerEntry);
3606
- } else applyScalarPropValue(el, key, value, domVNode.type);
3607
- }
3608
- removeStaleAttributes(el, domVNode, props);
3609
- if (existingListeners && existingListeners.size > 0) if (desiredListenerKeys === null) {
3610
- existingListeners.forEach((entry) => {
3611
- if (entry.isDelegated) removeDelegatedListener(el, entry.eventName);
3612
- else if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
3613
- else el.removeEventListener(entry.eventName, entry.handler);
3614
- });
3615
- elementListeners.delete(el);
3616
- } else {
3617
- existingListeners.forEach((entry, listenerKey) => {
3618
- if (!desiredListenerKeys.has(listenerKey)) {
3619
- if (entry.isDelegated) removeDelegatedListener(el, entry.eventName);
3620
- else if (entry.options !== void 0) el.removeEventListener(entry.eventName, entry.handler, entry.options);
3621
- else el.removeEventListener(entry.eventName, entry.handler);
3622
- existingListeners.delete(listenerKey);
3623
- }
3624
- });
3625
- if (existingListeners.size === 0) elementListeners.delete(el);
3626
- }
3627
- const delegatedHandlers = getDelegatedHandlersForElement(el);
3628
- if (delegatedHandlers && delegatedHandlers.size > 0) {
3629
- if (desiredDelegatedEventNames === null) for (const eventName of delegatedHandlers.keys()) removeDelegatedListener(el, eventName);
3630
- else for (const eventName of delegatedHandlers.keys()) if (!desiredDelegatedEventNames.has(eventName)) removeDelegatedListener(el, eventName);
3631
- }
3632
- if (existingReactiveProps && existingReactiveProps.size > 0) if (desiredReactivePropNames === null) {
3633
- existingReactiveProps.forEach((entry) => {
3634
- entry.cleanup();
3635
- });
3636
- elementReactivePropsCleanup.delete(el);
3637
- } else {
3638
- existingReactiveProps.forEach((entry, key) => {
3639
- if (!desiredReactivePropNames.has(key)) {
3640
- entry.cleanup();
3641
- existingReactiveProps.delete(key);
3642
3662
  }
3643
- });
3644
- if (existingReactiveProps.size === 0) elementReactivePropsCleanup.delete(el);
3663
+ }
3664
+ return;
3645
3665
  }
3646
- }
3647
- //#endregion
3648
- //#region src/renderer/intrinsic-hydration-adoption.ts
3649
- let hydrationAdoptionDepth = 0;
3650
- function withIntrinsicHydrationAdoption(work) {
3651
- hydrationAdoptionDepth += 1;
3652
- try {
3653
- return work();
3654
- } finally {
3655
- 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
+ }
3656
3673
  }
3657
- }
3658
- function getDirectVNodeChildren(vnode) {
3659
- const children = vnode.props?.children ?? vnode.children;
3660
- if (children === null || children === void 0 || children === false) return [];
3661
- return Array.isArray(children) ? children : [children];
3662
- }
3663
- function canAdoptMatchingIntrinsicSubtree(element, vnode) {
3664
- if (typeof vnode.type !== "string" || !tagNamesEqualIgnoreCase$1(element.tagName, vnode.type) || hasAnyElementBindingState(element) || extractKey(vnode) !== void 0) return false;
3665
- const props = vnode.props || {};
3666
- if (!hasMatchingStaticPropsIgnoringEvents(element, props, vnode.type)) return false;
3667
- for (const key in props) {
3668
- const value = props[key];
3669
- const eventProp = parseEventProp(key);
3670
- if (eventProp && typeof value !== "function") return false;
3671
- 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 = "";
3672
3681
  }
3673
- const children = getDirectVNodeChildren(vnode);
3674
- if (element.childNodes.length !== children.length) return false;
3675
- for (let index = 0; index < children.length; index += 1) {
3676
- const child = children[index];
3677
- const current = element.childNodes[index];
3678
- if (typeof child === "string" || typeof child === "number") {
3679
- 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();
3680
3690
  continue;
3681
3691
  }
3682
- if (!_isDOMElement(child) || typeof child.type !== "string" || !(current instanceof Element) || extractKey(child) !== void 0 || !canAdoptMatchingIntrinsicSubtree(current, child)) return false;
3683
- }
3684
- return true;
3685
- }
3686
- function adoptMatchingIntrinsicSubtree(element, vnode) {
3687
- const props = vnode.props || {};
3688
- let hasBindings = Object.prototype.hasOwnProperty.call(props, "ref");
3689
- for (const key in props) if (parseEventProp(key)) {
3690
- hasBindings = true;
3691
- break;
3692
- }
3693
- if (hasBindings) runRetainedElementUpdate(element, () => void 0, () => applyMatchingElementBindings(element, props), void 0, true);
3694
- const children = getDirectVNodeChildren(vnode);
3695
- for (let index = 0; index < children.length; index += 1) {
3696
- const child = children[index];
3697
- 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
+ }
3698
3726
  }
3699
3727
  }
3700
- function tryAdoptMatchingIntrinsicSubtree(element, vnode) {
3701
- if (hydrationAdoptionDepth === 0) return false;
3702
- if (!canAdoptMatchingIntrinsicSubtree(element, vnode)) return false;
3703
- adoptMatchingIntrinsicSubtree(element, vnode);
3704
- return true;
3705
- }
3706
3728
  //#endregion
3707
3729
  //#region src/renderer/evaluate-reconcile.ts
3708
3730
  function getRetainedHostOwnerChain(host, owner) {
@@ -4390,7 +4412,7 @@ function canAdoptHydratedElement(dom, vnode) {
4390
4412
  return !checkVNodeShapeChanged(dom, vnode);
4391
4413
  }
4392
4414
  function adoptHydratedRange(parent, scope, before, vnode) {
4393
- if (!scope.hydrationPending || !before || !isRangeStart(before)) return null;
4415
+ if (!isHydrationAdoptionScopeActive() || !scope.hydrationPending || !before || !isRangeStart(before)) return null;
4394
4416
  const end = findRangeEnd(before);
4395
4417
  if (!end || end.parentNode !== parent) return null;
4396
4418
  const range = {
@@ -5358,6 +5380,7 @@ function adoptEmptySSRPortalHydrationHost(host, instance, retainedInstances, res
5358
5380
  instance._placeholder = instanceHost;
5359
5381
  mountInstanceInline(instance, null);
5360
5382
  retainReplacementOwnerChain(instanceHost, instance, retainedInstances);
5383
+ markHydrationHostAdopted(instanceHost);
5361
5384
  return true;
5362
5385
  }
5363
5386
  function materializeEmptyHydrationPlaceholder(existingHost, instance, retainedInstances, result, preserveHydrationCursor) {
@@ -5531,6 +5554,7 @@ function syncComponentElement(currentDom, node, type, props, parentNamespace, fo
5531
5554
  const existingHost = currentDom instanceof Element || currentDom instanceof Comment ? currentDom : null;
5532
5555
  const existingInstance = existingHost ? existingHost instanceof Comment ? findStableHostInstanceByType(existingHost, type, node, getCurrentInstance(), 0) : findHostInstanceByType(existingHost, type, node, getCurrentInstance(), 0) : null;
5533
5556
  if (!existingHost) return null;
5557
+ if (!canReconcileComponentHost(existingHost, Boolean(existingInstance))) return null;
5534
5558
  const domHost = getRendererDOMHost();
5535
5559
  if (!existingInstance || existingInstance.fn !== type) {
5536
5560
  if (!(existingHost instanceof Element) && !isSSRPortalHydrationAnchor(existingHost)) return null;