@qwik.dev/core 2.0.0-beta.29 → 2.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * @qwik.dev/core 2.0.0-beta.29-dev+bc61b71
3
+ * @qwik.dev/core 2.0.0-beta.30-dev+5421ed4
4
4
  * Copyright QwikDev. All Rights Reserved.
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://github.com/QwikDev/qwik/blob/main/LICENSE
@@ -13,7 +13,6 @@ import { p } from '@qwik.dev/core/preloader';
13
13
  const g = globalThis;
14
14
  const qDev = g.qDev !== false;
15
15
  const qInspector = g.qInspector === true;
16
- const qSerialize = g.qSerialize !== false;
17
16
  const qDynamicPlatform = g.qDynamicPlatform !== false;
18
17
  const qTest = g.qTest === true;
19
18
  const qRuntimeQrl = g.qRuntimeQrl === true;
@@ -1007,7 +1006,7 @@ const COMMA = ',';
1007
1006
  *
1008
1007
  * @public
1009
1008
  */
1010
- const version = "2.0.0-beta.29-dev+bc61b71";
1009
+ const version = "2.0.0-beta.30-dev+5421ed4";
1011
1010
 
1012
1011
  // keep this import from core/build so the cjs build works
1013
1012
  const createPlatform = () => {
@@ -1876,6 +1875,9 @@ function getEffects$1(effects, prop) {
1876
1875
  const directGetPropsProxyProp = (jsx, prop) => {
1877
1876
  return (jsx.constProps && prop in jsx.constProps ? jsx.constProps[prop] : jsx.varProps[prop]);
1878
1877
  };
1878
+ const _getProps = (props, prop) => {
1879
+ return _getVarProps(props)?.[prop] || _getConstProps(props)?.[prop] || null;
1880
+ };
1879
1881
  /** Used by the optimizer for spread props operations @internal */
1880
1882
  const _getVarProps = (props) => {
1881
1883
  if (!props) {
@@ -2030,15 +2032,22 @@ const cleanupFn = (target, handleError) => {
2030
2032
  cleanupFns = [];
2031
2033
  target.$destroy$ = () => {
2032
2034
  target.$destroy$ = null;
2033
- // TODO handle promises
2035
+ let cleanupPromises = null;
2034
2036
  for (const fn of cleanupFns) {
2035
2037
  try {
2036
- fn();
2038
+ const result = fn();
2039
+ if (isPromise(result)) {
2040
+ (cleanupPromises ||= []).push(result.catch(handleError));
2041
+ }
2037
2042
  }
2038
2043
  catch (err) {
2039
2044
  handleError(err);
2040
2045
  }
2041
2046
  }
2047
+ cleanupFns = null;
2048
+ if (cleanupPromises?.length) {
2049
+ return Promise.all(cleanupPromises).then(() => undefined);
2050
+ }
2042
2051
  };
2043
2052
  }
2044
2053
  cleanupFns.push(fn);
@@ -2574,11 +2583,7 @@ class SerializerSignalImpl extends ComputedSignalImpl {
2574
2583
  return;
2575
2584
  }
2576
2585
  throwIfQRLNotResolved(this.$computeQrl$);
2577
- this.$flags$ &= -2 /* SignalFlags.INVALID */;
2578
- let arg = this.$computeQrl$.resolved;
2579
- if (typeof arg === 'function') {
2580
- arg = arg();
2581
- }
2586
+ const arg = untrack(this.$computeQrl$.resolved);
2582
2587
  const { deserialize, initial } = arg;
2583
2588
  const update = arg.update;
2584
2589
  const currentValue = this.$untrackedValue$ === NEEDS_COMPUTATION ? initial : this.$untrackedValue$;
@@ -2586,6 +2591,8 @@ class SerializerSignalImpl extends ComputedSignalImpl {
2586
2591
  ? update?.(currentValue) || currentValue
2587
2592
  : deserialize(currentValue), this, "." /* EffectProperty.VNODE */, this.$container$);
2588
2593
  this.$didInitialize$ = true;
2594
+ // Needs to invalidate only after all possible Promise throws happened
2595
+ this.$flags$ &= -2 /* SignalFlags.INVALID */;
2589
2596
  // We allow forcing the update of the signal without changing the value, for example when the deserialized value is the same reference as the old value but its internals have changed. In that case we want to trigger effects that depend on this signal, even though the value is the same.
2590
2597
  const didChange = (this.$didInitialize$ && untrackedValue !== 'undefined') ||
2591
2598
  untrackedValue !== this.$untrackedValue$;
@@ -3709,6 +3716,37 @@ const cleanupDestroyable = (destroyable) => {
3709
3716
  destroyable.$destroy$ = null;
3710
3717
  }
3711
3718
  };
3719
+ const cleanupAsyncDestroyable = (destroyable, handleError) => {
3720
+ const pendingCleanup = destroyable.$destroyPromise$;
3721
+ if (pendingCleanup) {
3722
+ return pendingCleanup;
3723
+ }
3724
+ const cleanup = destroyable.$destroy$;
3725
+ if (!cleanup) {
3726
+ return;
3727
+ }
3728
+ destroyable.$destroy$ = null;
3729
+ try {
3730
+ const result = cleanup();
3731
+ if (isPromise(result)) {
3732
+ const cleanupPromise = Promise.resolve(result)
3733
+ .then(() => undefined, (err) => {
3734
+ handleError(err);
3735
+ })
3736
+ .finally(() => {
3737
+ if (destroyable.$destroyPromise$ === cleanupPromise) {
3738
+ destroyable.$destroyPromise$ = undefined;
3739
+ }
3740
+ });
3741
+ destroyable.$destroyPromise$ = cleanupPromise;
3742
+ return cleanupPromise;
3743
+ }
3744
+ }
3745
+ catch (err) {
3746
+ handleError(err);
3747
+ }
3748
+ return;
3749
+ };
3712
3750
 
3713
3751
  /**
3714
3752
  * This safely calls an event handler, handling errors and retrying on thrown Promises, and
@@ -3770,13 +3808,20 @@ function _run(event, element) {
3770
3808
  return runEventHandlerQRL(qrlToRun, event, element, ctx);
3771
3809
  }
3772
3810
 
3773
- /**
3774
- * Helper to get the next sibling of a VNode. Extracted to module scope to help V8 inline it
3775
- * reliably.
3776
- */
3777
3811
  function peekNextSibling(vCurrent) {
3778
3812
  return vCurrent ? vCurrent.nextSibling : null;
3779
3813
  }
3814
+ function getLevelBoundary(diffContext) {
3815
+ return diffContext.$vParent$ === diffContext.$vEndParent$ ? diffContext.$vEnd$ : null;
3816
+ }
3817
+ function getCurrentInsertBefore(diffContext) {
3818
+ return diffContext.$vCurrent$ || getLevelBoundary(diffContext);
3819
+ }
3820
+ function peekNextSiblingWithinBoundary(diffContext, vCurrent) {
3821
+ const nextSibling = peekNextSibling(vCurrent);
3822
+ const boundary = getLevelBoundary(diffContext);
3823
+ return nextSibling === boundary ? null : nextSibling;
3824
+ }
3780
3825
  const _hasOwnProperty = Object.prototype.hasOwnProperty;
3781
3826
  /** Helper to set an attribute on a vnode. Extracted to module scope to avoid closure allocation. */
3782
3827
  function setAttribute(journal, vnode, key, value, scopedStyleIdPrefix, originalValue) {
@@ -3798,6 +3843,8 @@ function createDiffContext(container, journal, cursor, scopedStyleIdPrefix) {
3798
3843
  $vParent$: null,
3799
3844
  $vCurrent$: null,
3800
3845
  $vNewNode$: null,
3846
+ $vEnd$: null,
3847
+ $vEndParent$: null,
3801
3848
  $vSiblings$: null,
3802
3849
  $vSiblingsArray$: null,
3803
3850
  $vSideBuffer$: null,
@@ -3819,10 +3866,34 @@ function createDiffContext(container, journal, cursor, scopedStyleIdPrefix) {
3819
3866
  },
3820
3867
  };
3821
3868
  }
3822
- const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyleIdPrefix) => {
3823
- const diffContext = createDiffContext(container, journal, cursor, scopedStyleIdPrefix);
3824
- ////////////////////////////////
3825
- diff(diffContext, jsxNode, vStartNode);
3869
+ function prepareDiffContext(diffContext, container, journal, cursor, scopedStyleIdPrefix) {
3870
+ diffContext.$container$ = container;
3871
+ diffContext.$journal$ = journal;
3872
+ diffContext.$cursor$ = cursor;
3873
+ diffContext.$scopedStyleIdPrefix$ = scopedStyleIdPrefix;
3874
+ diffContext.$subscriptionData$.$const$.data.$scopedStyleIdPrefix$ = scopedStyleIdPrefix;
3875
+ diffContext.$subscriptionData$.$var$.data.$scopedStyleIdPrefix$ = scopedStyleIdPrefix;
3876
+ diffContext.$asyncQueue$.length = 0;
3877
+ diffContext.$asyncAttributePromises$.length = 0;
3878
+ }
3879
+ function getPreparedDiffContext(diffContext, container, journal, cursor, scopedStyleIdPrefix) {
3880
+ const reusableDiffContext = diffContext ?? createDiffContext(container, journal, cursor, scopedStyleIdPrefix);
3881
+ prepareDiffContext(reusableDiffContext, container, journal, cursor, scopedStyleIdPrefix);
3882
+ return reusableDiffContext;
3883
+ }
3884
+ const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyleIdPrefix, diffContext) => {
3885
+ return runDiff(getPreparedDiffContext(diffContext, container, journal, cursor, scopedStyleIdPrefix), jsxNode, vStartNode);
3886
+ };
3887
+ const vnode_diff_range = (container, journal, jsxNode, vParent, vCurrent, vEnd, cursor, scopedStyleIdPrefix,
3888
+ // Allows callers diffing into an empty known-new range to skip keyed-sibling materialization.
3889
+ forceCreationMode = false, diffContext) => {
3890
+ return runDiff(getPreparedDiffContext(diffContext, container, journal, cursor, scopedStyleIdPrefix), jsxNode, vParent, vCurrent, vEnd, forceCreationMode);
3891
+ };
3892
+ //////////////////////////////////////////////
3893
+ //////////////////////////////////////////////
3894
+ //////////////////////////////////////////////
3895
+ function runDiff(diffContext, jsxNode, vStartNode, vCurrent = vnode_getFirstChild(vStartNode), vEnd = null, forceCreationMode = false) {
3896
+ diff(diffContext, jsxNode, vStartNode, vCurrent, vEnd, forceCreationMode);
3826
3897
  const result = drainAsyncQueue(diffContext);
3827
3898
  // Cleanup diffContext after completion
3828
3899
  if (isPromise(result)) {
@@ -3833,112 +3904,123 @@ const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyle
3833
3904
  else {
3834
3905
  cleanupDiffContext(diffContext);
3835
3906
  }
3836
- };
3837
- //////////////////////////////////////////////
3838
- //////////////////////////////////////////////
3839
- //////////////////////////////////////////////
3840
- function diff(diffContext, jsxNode, vStartNode) {
3907
+ }
3908
+ function diff(diffContext, jsxNode, vStartNode, vCurrent = vnode_getFirstChild(vStartNode), vEnd = null, forceCreationMode = false) {
3909
+ const previousCreationMode = diffContext.$isCreationMode$;
3841
3910
  isDev && assertFalse(vnode_isVNode(jsxNode), 'JSXNode should not be a VNode');
3842
3911
  isDev && assertTrue(vnode_isVNode(vStartNode), 'vStartNode should be a VNode');
3912
+ diffContext.$isCreationMode$ = forceCreationMode || previousCreationMode;
3843
3913
  diffContext.$vParent$ = vStartNode;
3844
3914
  diffContext.$vNewNode$ = null;
3845
- diffContext.$vCurrent$ = vnode_getFirstChild(vStartNode);
3915
+ diffContext.$vCurrent$ = vCurrent;
3916
+ diffContext.$vEnd$ = vEnd;
3917
+ diffContext.$vEndParent$ = vStartNode;
3846
3918
  stackPush(diffContext, jsxNode, true);
3847
- if (diffContext.$vParent$.flags & 32 /* VNodeFlags.Deleted */) {
3848
- // Ignore diff if the parent is deleted.
3849
- return;
3850
- }
3851
- while (diffContext.$stack$.length) {
3852
- while (diffContext.$jsxIdx$ < diffContext.$jsxCount$) {
3853
- isDev &&
3854
- assertFalse(diffContext.$vParent$ === diffContext.$vCurrent$, "Parent and current can't be the same");
3855
- if (typeof diffContext.$jsxValue$ === 'string') {
3856
- expectText(diffContext, diffContext.$jsxValue$);
3857
- }
3858
- else if (typeof diffContext.$jsxValue$ === 'number') {
3859
- expectText(diffContext, String(diffContext.$jsxValue$));
3860
- }
3861
- else if (diffContext.$jsxValue$ && typeof diffContext.$jsxValue$ === 'object') {
3862
- if (isJSXNode(diffContext.$jsxValue$)) {
3863
- const type = diffContext.$jsxValue$.type;
3864
- if (typeof type === 'string') {
3865
- expectNoTextNode(diffContext);
3866
- expectElement(diffContext, diffContext.$jsxValue$, type);
3867
- const hasDangerousInnerHTML = (diffContext.$jsxValue$.constProps &&
3868
- _hasOwnProperty.call(diffContext.$jsxValue$.constProps, dangerouslySetInnerHTML)) ||
3869
- _hasOwnProperty.call(diffContext.$jsxValue$.varProps, dangerouslySetInnerHTML);
3870
- if (hasDangerousInnerHTML) {
3871
- expectNoChildren(diffContext, false);
3872
- }
3873
- else {
3874
- descend(diffContext, diffContext.$jsxValue$.children, true);
3875
- }
3876
- }
3877
- else if (typeof type === 'function') {
3878
- if (type === Fragment) {
3879
- expectNoTextNode(diffContext);
3880
- expectVirtual(diffContext, "F" /* VirtualType.Fragment */, diffContext.$jsxValue$.key);
3881
- descend(diffContext, diffContext.$jsxValue$.children, true);
3882
- }
3883
- else if (type === Slot) {
3919
+ try {
3920
+ if (diffContext.$vParent$.flags & 32 /* VNodeFlags.Deleted */) {
3921
+ // Ignore diff if the parent is deleted.
3922
+ return;
3923
+ }
3924
+ while (diffContext.$stack$.length) {
3925
+ while (diffContext.$jsxIdx$ < diffContext.$jsxCount$) {
3926
+ isDev &&
3927
+ assertFalse(diffContext.$vParent$ === diffContext.$vCurrent$, "Parent and current can't be the same");
3928
+ if (typeof diffContext.$jsxValue$ === 'string') {
3929
+ expectText(diffContext, diffContext.$jsxValue$);
3930
+ }
3931
+ else if (typeof diffContext.$jsxValue$ === 'number') {
3932
+ expectText(diffContext, String(diffContext.$jsxValue$));
3933
+ }
3934
+ else if (diffContext.$jsxValue$ && typeof diffContext.$jsxValue$ === 'object') {
3935
+ if (isJSXNode(diffContext.$jsxValue$)) {
3936
+ const type = diffContext.$jsxValue$.type;
3937
+ if (typeof type === 'string') {
3884
3938
  expectNoTextNode(diffContext);
3885
- if (!expectSlot(diffContext)) {
3886
- // nothing to project, so try to render the Slot default content.
3939
+ expectElement(diffContext, diffContext.$jsxValue$, type);
3940
+ const hasDangerousInnerHTML = (diffContext.$jsxValue$.constProps &&
3941
+ _hasOwnProperty.call(diffContext.$jsxValue$.constProps, dangerouslySetInnerHTML)) ||
3942
+ _hasOwnProperty.call(diffContext.$jsxValue$.varProps, dangerouslySetInnerHTML);
3943
+ if (hasDangerousInnerHTML) {
3944
+ // Only clean up children for existing nodes; new nodes have no children yet
3945
+ if (!diffContext.$vNewNode$) {
3946
+ expectNoChildren(diffContext, false);
3947
+ }
3948
+ }
3949
+ else {
3887
3950
  descend(diffContext, diffContext.$jsxValue$.children, true);
3888
3951
  }
3889
3952
  }
3890
- else if (type === Projection) {
3891
- expectProjection(diffContext);
3892
- descend(diffContext, diffContext.$jsxValue$.children, true,
3893
- // special case for projection, we don't want to expect no children
3894
- // because the projection's children are not removed
3895
- false);
3896
- }
3897
- else if (type === SSRComment) {
3898
- expectNoMore(diffContext);
3953
+ else if (typeof type === 'function') {
3954
+ if (type === Fragment) {
3955
+ expectNoTextNode(diffContext);
3956
+ expectVirtual(diffContext, "F" /* VirtualType.Fragment */, diffContext.$jsxValue$.key);
3957
+ descend(diffContext, diffContext.$jsxValue$.children, true);
3958
+ }
3959
+ else if (type === Slot) {
3960
+ expectNoTextNode(diffContext);
3961
+ if (!expectSlot(diffContext)) {
3962
+ // nothing to project, so try to render the Slot default content.
3963
+ descend(diffContext, diffContext.$jsxValue$.children, true);
3964
+ }
3965
+ }
3966
+ else if (type === Projection) {
3967
+ expectProjection(diffContext);
3968
+ descend(diffContext, diffContext.$jsxValue$.children, true,
3969
+ // special case for projection, we don't want to expect no children
3970
+ // because the projection's children are not removed
3971
+ false);
3972
+ }
3973
+ else if (type === SSRComment) {
3974
+ expectNoMore(diffContext);
3975
+ }
3976
+ else if (type === SSRRaw) {
3977
+ expectNoMore(diffContext);
3978
+ }
3979
+ else {
3980
+ // Must be a component
3981
+ expectNoTextNode(diffContext);
3982
+ expectComponent(diffContext, type);
3983
+ }
3899
3984
  }
3900
- else if (type === SSRRaw) {
3901
- expectNoMore(diffContext);
3985
+ }
3986
+ else if (Array.isArray(diffContext.$jsxValue$)) {
3987
+ descend(diffContext, diffContext.$jsxValue$, false);
3988
+ }
3989
+ else if (isSignal(diffContext.$jsxValue$)) {
3990
+ expectVirtual(diffContext, "S" /* VirtualType.WrappedSignal */, null);
3991
+ const unwrappedSignal = diffContext.$jsxValue$ instanceof WrappedSignalImpl
3992
+ ? diffContext.$jsxValue$.$unwrapIfSignal$()
3993
+ : diffContext.$jsxValue$;
3994
+ const signals = diffContext.$vCurrent$?.[_EFFECT_BACK_REF]?.get("." /* EffectProperty.VNODE */)?.backRef;
3995
+ let hasUnwrappedSignal = signals?.has(unwrappedSignal);
3996
+ if (signals && unwrappedSignal instanceof WrappedSignalImpl) {
3997
+ hasUnwrappedSignal = containsWrappedSignal(signals, unwrappedSignal);
3902
3998
  }
3903
- else {
3904
- // Must be a component
3905
- expectNoTextNode(diffContext);
3906
- expectComponent(diffContext, type);
3999
+ if (!hasUnwrappedSignal) {
4000
+ const vHost = (diffContext.$vNewNode$ || diffContext.$vCurrent$);
4001
+ descend(diffContext, resolveSignalAndDescend(diffContext, () => trackSignalAndAssignHost(unwrappedSignal, vHost, "." /* EffectProperty.VNODE */, diffContext.$container$)), true);
3907
4002
  }
3908
4003
  }
3909
- }
3910
- else if (Array.isArray(diffContext.$jsxValue$)) {
3911
- descend(diffContext, diffContext.$jsxValue$, false);
3912
- }
3913
- else if (isSignal(diffContext.$jsxValue$)) {
3914
- expectVirtual(diffContext, "S" /* VirtualType.WrappedSignal */, null);
3915
- const unwrappedSignal = diffContext.$jsxValue$ instanceof WrappedSignalImpl
3916
- ? diffContext.$jsxValue$.$unwrapIfSignal$()
3917
- : diffContext.$jsxValue$;
3918
- const signals = diffContext.$vCurrent$?.[_EFFECT_BACK_REF]?.get("." /* EffectProperty.VNODE */)?.backRef;
3919
- let hasUnwrappedSignal = signals?.has(unwrappedSignal);
3920
- if (signals && unwrappedSignal instanceof WrappedSignalImpl) {
3921
- hasUnwrappedSignal = containsWrappedSignal(signals, unwrappedSignal);
3922
- }
3923
- if (!hasUnwrappedSignal) {
3924
- const vHost = (diffContext.$vNewNode$ || diffContext.$vCurrent$);
3925
- descend(diffContext, resolveSignalAndDescend(diffContext, () => trackSignalAndAssignHost(unwrappedSignal, vHost, "." /* EffectProperty.VNODE */, diffContext.$container$)), true);
4004
+ else if (isPromise(diffContext.$jsxValue$)) {
4005
+ expectVirtual(diffContext, "A" /* VirtualType.Awaited */, null);
4006
+ diffContext.$asyncQueue$.push(diffContext.$jsxValue$, diffContext.$vNewNode$ || diffContext.$vCurrent$);
3926
4007
  }
3927
4008
  }
3928
- else if (isPromise(diffContext.$jsxValue$)) {
3929
- expectVirtual(diffContext, "A" /* VirtualType.Awaited */, null);
3930
- diffContext.$asyncQueue$.push(diffContext.$jsxValue$, diffContext.$vNewNode$ || diffContext.$vCurrent$);
4009
+ else if (diffContext.$jsxValue$ === SkipRender) {
4010
+ // do nothing, we are skipping this node
3931
4011
  }
4012
+ else {
4013
+ expectText(diffContext, '');
4014
+ }
4015
+ advance(diffContext);
3932
4016
  }
3933
- else if (diffContext.$jsxValue$ === SkipRender) ;
3934
- else {
3935
- expectText(diffContext, '');
3936
- }
3937
- advance(diffContext);
4017
+ expectNoMore(diffContext);
4018
+ cleanupSideBuffer(diffContext);
4019
+ ascend(diffContext);
3938
4020
  }
3939
- expectNoMore(diffContext);
3940
- cleanupSideBuffer(diffContext);
3941
- ascend(diffContext);
4021
+ }
4022
+ finally {
4023
+ diffContext.$isCreationMode$ = previousCreationMode;
3942
4024
  }
3943
4025
  }
3944
4026
  function resolveSignalAndDescend(diffContext, fn) {
@@ -3978,7 +4060,7 @@ function advance(diffContext) {
3978
4060
  diffContext.$vNewNode$ = null;
3979
4061
  }
3980
4062
  else {
3981
- diffContext.$vCurrent$ = peekNextSibling(diffContext.$vCurrent$);
4063
+ diffContext.$vCurrent$ = peekNextSiblingWithinBoundary(diffContext, diffContext.$vCurrent$);
3982
4064
  }
3983
4065
  }
3984
4066
  /**
@@ -4068,11 +4150,10 @@ function stackPush(diffContext, children, descendVNode) {
4068
4150
  }
4069
4151
  function getInsertBefore(diffContext) {
4070
4152
  if (diffContext.$vNewNode$) {
4071
- return diffContext.$vCurrent$;
4072
- }
4073
- else {
4074
- return peekNextSibling(diffContext.$vCurrent$);
4153
+ return getCurrentInsertBefore(diffContext);
4075
4154
  }
4155
+ return (peekNextSiblingWithinBoundary(diffContext, diffContext.$vCurrent$) ||
4156
+ getLevelBoundary(diffContext));
4076
4157
  }
4077
4158
  /////////////////////////////////////////////////////////////////////////////
4078
4159
  /////////////////////////////////////////////////////////////////////////////
@@ -4163,7 +4244,7 @@ function expectSlot(diffContext) {
4163
4244
  vHost && vnode_setProp(vHost, slotNameKey, diffContext.$vNewNode$);
4164
4245
  isDev &&
4165
4246
  vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "P" /* VirtualType.Projection */); // Nothing to project, so render content of the slot.
4166
- vnode_insertBefore(diffContext.$journal$, diffContext.$vParent$, diffContext.$vNewNode$, diffContext.$vCurrent$ && getInsertBefore(diffContext));
4247
+ vnode_insertBefore(diffContext.$journal$, diffContext.$vParent$, diffContext.$vNewNode$, getInsertBefore(diffContext));
4167
4248
  return false;
4168
4249
  }
4169
4250
  else if (vProjectedNode === diffContext.$vCurrent$) ;
@@ -4176,7 +4257,7 @@ function expectSlot(diffContext) {
4176
4257
  isDev &&
4177
4258
  vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "P" /* VirtualType.Projection */);
4178
4259
  vnode_inflateProjectionTrailingText(diffContext.$journal$, diffContext.$vNewNode$);
4179
- vnode_insertBefore(diffContext.$journal$, diffContext.$vParent$, diffContext.$vNewNode$, diffContext.$vCurrent$ && getInsertBefore(diffContext));
4260
+ vnode_insertBefore(diffContext.$journal$, diffContext.$vParent$, diffContext.$vNewNode$, getInsertBefore(diffContext));
4180
4261
  // If we moved from a q:template and it's now empty, remove it
4181
4262
  if (oldParent &&
4182
4263
  vnode_isElementVNode(oldParent) &&
@@ -4258,12 +4339,13 @@ function expectNoChildren(diffContext, removeDOM = true) {
4258
4339
  }
4259
4340
  /** Expect no more nodes - Any nodes which are still at cursor, need to be removed. */
4260
4341
  function expectNoMore(diffContext) {
4342
+ const boundary = getLevelBoundary(diffContext);
4261
4343
  isDev &&
4262
4344
  assertFalse(diffContext.$vParent$ === diffContext.$vCurrent$, "Parent and current can't be the same");
4263
- if (diffContext.$vCurrent$ !== null) {
4264
- while (diffContext.$vCurrent$) {
4345
+ if (diffContext.$vCurrent$ !== null && diffContext.$vCurrent$ !== boundary) {
4346
+ while (diffContext.$vCurrent$ && diffContext.$vCurrent$ !== boundary) {
4265
4347
  const toRemove = diffContext.$vCurrent$;
4266
- diffContext.$vCurrent$ = peekNextSibling(diffContext.$vCurrent$);
4348
+ diffContext.$vCurrent$ = peekNextSiblingWithinBoundary(diffContext, diffContext.$vCurrent$);
4267
4349
  if (diffContext.$vParent$ === toRemove.parent) {
4268
4350
  cleanup(diffContext.$container$, diffContext.$journal$, toRemove, diffContext.$cursor$);
4269
4351
  // If we are diffing projection than the parent is not the parent of the node.
@@ -4276,12 +4358,52 @@ function expectNoMore(diffContext) {
4276
4358
  function expectNoTextNode(diffContext) {
4277
4359
  if (diffContext.$vCurrent$ !== null && vnode_isTextVNode(diffContext.$vCurrent$)) {
4278
4360
  const toRemove = diffContext.$vCurrent$;
4279
- diffContext.$vCurrent$ = peekNextSibling(diffContext.$vCurrent$);
4361
+ diffContext.$vCurrent$ = peekNextSiblingWithinBoundary(diffContext, diffContext.$vCurrent$);
4280
4362
  vnode_remove(diffContext.$journal$, diffContext.$vParent$, toRemove, true);
4281
4363
  }
4282
4364
  }
4365
+ function applyRef(value, element, currentFile) {
4366
+ if (isSignal(value)) {
4367
+ value.value = element;
4368
+ return true;
4369
+ }
4370
+ if (typeof value === 'function') {
4371
+ value(element);
4372
+ return true;
4373
+ }
4374
+ if (value == null) {
4375
+ return true;
4376
+ }
4377
+ throw qError(15 /* QError.invalidRefValue */, [currentFile]);
4378
+ }
4379
+ function resolveSignalValue(container, vHost, key, signal, subscriptionData) {
4380
+ return retryOnPromise(() => trackSignalAndAssignHost(signal, vHost, key, container, subscriptionData));
4381
+ }
4382
+ function queueConstAttributePromise(diffContext, element, key, value, isSvg) {
4383
+ const scopedStyleIdPrefix = diffContext.$scopedStyleIdPrefix$;
4384
+ const attributePromise = value.then((resolvedValue) => directSetAttribute(element, key, serializeAttribute(key, resolvedValue, scopedStyleIdPrefix), isSvg));
4385
+ diffContext.$asyncAttributePromises$.push(attributePromise);
4386
+ }
4387
+ function applyConstInnerHtml(element, value) {
4388
+ if (value) {
4389
+ element.innerHTML = String(value);
4390
+ element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
4391
+ }
4392
+ }
4393
+ function applyConstTextareaValue(element, value, currentFile) {
4394
+ if (value && typeof value !== 'string') {
4395
+ if (isDev) {
4396
+ throw qError(23 /* QError.wrongTextareaValue */, [currentFile, value]);
4397
+ }
4398
+ return true;
4399
+ }
4400
+ element.value = escapeHTML(value || '');
4401
+ return true;
4402
+ }
4283
4403
  function createNewElement(diffContext, jsx, elementName, currentFile) {
4284
4404
  const element = createElementWithNamespace(diffContext, elementName);
4405
+ const vHost = diffContext.$vNewNode$;
4406
+ const isSvg = (vHost.flags & 512 /* VNodeFlags.NS_svg */) !== 0;
4285
4407
  const { constProps } = jsx;
4286
4408
  if (constProps) {
4287
4409
  // Const props are, well, constant, they will never change!
@@ -4290,54 +4412,29 @@ function createNewElement(diffContext, jsx, elementName, currentFile) {
4290
4412
  for (const key in constProps) {
4291
4413
  let value = constProps[key];
4292
4414
  if (isHtmlAttributeAnEventName(key)) {
4293
- registerEventHandlers(key, value, element, diffContext.$vNewNode$, diffContext);
4415
+ registerEventHandlers(key, value, element, vHost, diffContext);
4294
4416
  continue;
4295
4417
  }
4296
- if (key === 'ref') {
4297
- if (isSignal(value)) {
4298
- value.value = element;
4299
- continue;
4300
- }
4301
- else if (typeof value === 'function') {
4302
- value(element);
4303
- continue;
4304
- }
4305
- else if (value == null) {
4306
- continue;
4307
- }
4308
- else {
4309
- throw qError(15 /* QError.invalidRefValue */, [currentFile]);
4310
- }
4418
+ if (key === 'ref' && applyRef(value, element, currentFile)) {
4419
+ continue;
4311
4420
  }
4312
4421
  if (isSignal(value)) {
4313
- const vHost = diffContext.$vNewNode$;
4314
- const signal = value;
4315
- value = retryOnPromise(() => trackSignalAndAssignHost(signal, vHost, key, diffContext.$container$, diffContext.$subscriptionData$.$const$));
4422
+ value = resolveSignalValue(diffContext.$container$, diffContext.$vNewNode$, key, value, diffContext.$subscriptionData$.$const$);
4316
4423
  }
4317
4424
  if (isPromise(value)) {
4318
- const vHost = diffContext.$vNewNode$;
4319
- const attributePromise = value.then((resolvedValue) => directSetAttribute(element, key, serializeAttribute(key, resolvedValue, diffContext.$scopedStyleIdPrefix$), (vHost.flags & 512 /* VNodeFlags.NS_svg */) !== 0));
4320
- diffContext.$asyncAttributePromises$.push(attributePromise);
4425
+ queueConstAttributePromise(diffContext, element, key, value, isSvg);
4321
4426
  continue;
4322
4427
  }
4323
4428
  if (key === dangerouslySetInnerHTML) {
4324
- if (value) {
4325
- element.innerHTML = String(value);
4326
- element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
4327
- }
4429
+ applyConstInnerHtml(element, value);
4328
4430
  continue;
4329
4431
  }
4330
- if (elementName === 'textarea' && key === 'value') {
4331
- if (value && typeof value !== 'string') {
4332
- if (isDev) {
4333
- throw qError(23 /* QError.wrongTextareaValue */, [currentFile, value]);
4334
- }
4335
- continue;
4336
- }
4337
- element.value = escapeHTML(value || '');
4432
+ if (elementName === 'textarea' &&
4433
+ key === 'value' &&
4434
+ applyConstTextareaValue(element, value, currentFile)) {
4338
4435
  continue;
4339
4436
  }
4340
- directSetAttribute(element, key, serializeAttribute(key, value, diffContext.$scopedStyleIdPrefix$), (diffContext.$vNewNode$.flags & 512 /* VNodeFlags.NS_svg */) !== 0);
4437
+ directSetAttribute(element, key, serializeAttribute(key, value, diffContext.$scopedStyleIdPrefix$), isSvg);
4341
4438
  }
4342
4439
  }
4343
4440
  const key = jsx.key;
@@ -4352,7 +4449,7 @@ function createNewElement(diffContext, jsx, elementName, currentFile) {
4352
4449
  element.setAttribute('class', diffContext.$scopedStyleIdPrefix$);
4353
4450
  }
4354
4451
  }
4355
- vnode_insertElementBefore(diffContext.$journal$, diffContext.$vParent$, diffContext.$vNewNode$, diffContext.$vCurrent$);
4452
+ vnode_insertElementBefore(diffContext.$journal$, diffContext.$vParent$, diffContext.$vNewNode$, getCurrentInsertBefore(diffContext));
4356
4453
  }
4357
4454
  function registerEventHandlers(key, value, element, vnode, diffContext) {
4358
4455
  const scopedKebabName = key.slice(2);
@@ -4374,13 +4471,11 @@ function registerEventHandlers(key, value, element, vnode, diffContext) {
4374
4471
  }
4375
4472
  }
4376
4473
  if (handlers.length > 0) {
4377
- (element._qDispatch ||= {})[scopedKebabName] = handlers;
4474
+ (element._qDispatch ||= {})[scopedKebabName] = handlers.length === 1 ? handlers[0] : handlers;
4378
4475
  }
4379
4476
  }
4380
4477
  else if (value) {
4381
- (element._qDispatch ||= {})[scopedKebabName] = [
4382
- runEventHandlerQRL.bind(null, value),
4383
- ];
4478
+ (element._qDispatch ||= {})[scopedKebabName] = runEventHandlerQRL.bind(null, value);
4384
4479
  }
4385
4480
  // window and document events need attrs so qwik loader can find them
4386
4481
  // TODO only do these when not already present
@@ -4485,17 +4580,9 @@ const patchProperty = (diffContext, vnode, key, value, currentFile) => {
4485
4580
  const originalValue = value;
4486
4581
  if (key === 'ref') {
4487
4582
  const element = vnode.node;
4488
- if (isSignal(value)) {
4489
- value.value = element;
4583
+ if (applyRef(value, element, currentFile)) {
4490
4584
  return;
4491
4585
  }
4492
- else if (typeof value === 'function') {
4493
- value(element);
4494
- return;
4495
- }
4496
- else {
4497
- throw qError(15 /* QError.invalidRefValue */, [currentFile]);
4498
- }
4499
4586
  }
4500
4587
  const currentEffect = vnode[_EFFECT_BACK_REF]?.get(key);
4501
4588
  if (isSignal(value)) {
@@ -4507,7 +4594,7 @@ const patchProperty = (diffContext, vnode, key, value, currentFile) => {
4507
4594
  clearEffectSubscription(diffContext.$container$, currentEffect);
4508
4595
  }
4509
4596
  const vHost = vnode;
4510
- value = retryOnPromise(() => trackSignalAndAssignHost(unwrappedSignal, vHost, key, diffContext.$container$, diffContext.$subscriptionData$.$var$));
4597
+ value = resolveSignalValue(diffContext.$container$, vHost, key, unwrappedSignal, diffContext.$subscriptionData$.$var$);
4511
4598
  }
4512
4599
  else {
4513
4600
  if (currentEffect) {
@@ -4548,8 +4635,9 @@ function retrieveChildWithKey(diffContext, nodeName, key) {
4548
4635
  // it is not materialized; so materialize it.
4549
4636
  diffContext.$vSiblings$ = new Map();
4550
4637
  diffContext.$vSiblingsArray$ = [];
4638
+ const boundary = getLevelBoundary(diffContext);
4551
4639
  let vNode = diffContext.$vCurrent$;
4552
- while (vNode) {
4640
+ while (vNode && vNode !== boundary) {
4553
4641
  const name = vnode_isElementVNode(vNode) ? vnode_getElementName(vNode) : null;
4554
4642
  const vKey = getKey(vNode) ||
4555
4643
  getComponentHash(vNode, diffContext.$container$.$getObjectById$);
@@ -4608,8 +4696,9 @@ function collectSideBufferSiblings(diffContext, targetNode) {
4608
4696
  return;
4609
4697
  }
4610
4698
  // Walk from vCurrent up to the target node and collect all keyed siblings
4699
+ const boundary = getLevelBoundary(diffContext);
4611
4700
  let vNode = diffContext.$vCurrent$;
4612
- while (vNode && vNode !== targetNode) {
4701
+ while (vNode && vNode !== targetNode && vNode !== boundary) {
4613
4702
  const name = vnode_isElementVNode(vNode) ? vnode_getElementName(vNode) : null;
4614
4703
  const vKey = getKey(vNode) ||
4615
4704
  getComponentHash(vNode, diffContext.$container$.$getObjectById$);
@@ -4652,7 +4741,7 @@ function moveOrCreateKeyedNode(diffContext, nodeName, lookupKey, sideBufferKey,
4652
4741
  diffContext.$vNewNode$ = retrieveChildWithKey(diffContext, nodeName, lookupKey);
4653
4742
  if (diffContext.$vNewNode$) {
4654
4743
  if (!sideBufferKey) {
4655
- vnode_insertBefore(diffContext.$journal$, parentForInsert, diffContext.$vNewNode$, diffContext.$vCurrent$);
4744
+ vnode_insertBefore(diffContext.$journal$, parentForInsert, diffContext.$vNewNode$, getCurrentInsertBefore(diffContext));
4656
4745
  }
4657
4746
  diffContext.$vCurrent$ = diffContext.$vNewNode$;
4658
4747
  diffContext.$vNewNode$ = null;
@@ -4679,7 +4768,7 @@ function moveOrCreateKeyedNode(diffContext, nodeName, lookupKey, sideBufferKey,
4679
4768
  }
4680
4769
  // Only move if the node is not already in the correct position
4681
4770
  if (buffered !== diffContext.$vCurrent$) {
4682
- vnode_insertBefore(diffContext.$journal$, parentForInsert, buffered, diffContext.$vCurrent$);
4771
+ vnode_insertBefore(diffContext.$journal$, parentForInsert, buffered, getCurrentInsertBefore(diffContext));
4683
4772
  }
4684
4773
  diffContext.$vCurrent$ = buffered;
4685
4774
  diffContext.$vNewNode$ = null;
@@ -4701,13 +4790,13 @@ function expectVirtual(diffContext, type, jsxKey) {
4701
4790
  }
4702
4791
  // For fragments without a key, always create a new virtual node (ensures rerender semantics)
4703
4792
  if (jsxKey === null || diffContext.$isCreationMode$) {
4704
- vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), diffContext.$vCurrent$ && getInsertBefore(diffContext));
4793
+ vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
4705
4794
  diffContext.$vNewNode$.key = jsxKey;
4706
4795
  isDev && vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, type);
4707
4796
  return;
4708
4797
  }
4709
4798
  if (moveOrCreateKeyedNode(diffContext, null, jsxKey, getSideBufferKey(null, jsxKey), diffContext.$vParent$, true)) {
4710
- vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), diffContext.$vCurrent$ && getInsertBefore(diffContext));
4799
+ vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
4711
4800
  diffContext.$vNewNode$.key = jsxKey;
4712
4801
  isDev && vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, type);
4713
4802
  }
@@ -4809,7 +4898,7 @@ function insertNewComponent(diffContext, host, componentQRL, jsxProps) {
4809
4898
  if (host) {
4810
4899
  clearAllEffects(diffContext.$container$, host);
4811
4900
  }
4812
- vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), diffContext.$vCurrent$ && getInsertBefore(diffContext));
4901
+ vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
4813
4902
  const jsxNode = diffContext.$jsxValue$;
4814
4903
  isDev && vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "C" /* VirtualType.Component */);
4815
4904
  vnode_setProp(diffContext.$vNewNode$, OnRenderProp, componentQRL);
@@ -4817,7 +4906,7 @@ function insertNewComponent(diffContext, host, componentQRL, jsxProps) {
4817
4906
  diffContext.$vNewNode$.key = jsxNode.key;
4818
4907
  }
4819
4908
  function insertNewInlineComponent(diffContext) {
4820
- vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), diffContext.$vCurrent$ && getInsertBefore(diffContext));
4909
+ vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
4821
4910
  const jsxNode = diffContext.$jsxValue$;
4822
4911
  isDev &&
4823
4912
  vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "I" /* VirtualType.InlineComponent */);
@@ -4837,7 +4926,7 @@ function expectText(diffContext, text) {
4837
4926
  return;
4838
4927
  }
4839
4928
  }
4840
- vnode_insertElementBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newText((import.meta.env.TEST ? diffContext.$container$.document : document).createTextNode(text), text)), diffContext.$vCurrent$);
4929
+ vnode_insertElementBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newText((import.meta.env.TEST ? diffContext.$container$.document : document).createTextNode(text), text)), getCurrentInsertBefore(diffContext));
4841
4930
  }
4842
4931
  /**
4843
4932
  * Retrieve the key from the VNode.
@@ -5160,6 +5249,221 @@ function containsWrappedSignal(data, signal) {
5160
5249
  return false;
5161
5250
  }
5162
5251
 
5252
+ function collectChildren(first, last = null) {
5253
+ const children = [];
5254
+ let child = first;
5255
+ while (child) {
5256
+ children.push(child);
5257
+ if (child === last) {
5258
+ break;
5259
+ }
5260
+ child = child.nextSibling;
5261
+ }
5262
+ return children;
5263
+ }
5264
+ function renderKeyedRow(item, index, key, renderItem) {
5265
+ const jsx = renderItem(item, index);
5266
+ if (isDev && !isJSXNode(jsx)) {
5267
+ throw new Error('Each item$ must return a single JSX node');
5268
+ }
5269
+ jsx.key = key;
5270
+ return jsx;
5271
+ }
5272
+ function buildJsxRange(items, nextKeys, keyOf, renderItem, start, end) {
5273
+ const jsxItems = new Array(end - start + 1);
5274
+ for (let i = start; i <= end; i++) {
5275
+ const key = nextKeys[i] ?? (nextKeys[i] = keyOf(items[i], i));
5276
+ jsxItems[i - start] = renderKeyedRow(items[i], i, key, renderItem);
5277
+ }
5278
+ return jsxItems;
5279
+ }
5280
+ function firstInsertedBeforeAnchor(parent, anchor, count) {
5281
+ let inserted = (anchor ? anchor.previousSibling : parent.lastChild);
5282
+ for (let i = 1; i < count && inserted; i++) {
5283
+ inserted = inserted.previousSibling;
5284
+ }
5285
+ return inserted;
5286
+ }
5287
+ function longestIncreasingSubsequencePositions(arr) {
5288
+ const n = arr.length;
5289
+ if (n === 0) {
5290
+ return [];
5291
+ }
5292
+ const tails = [];
5293
+ const prev = new Array(n).fill(-1);
5294
+ for (let i = 0; i < n; i++) {
5295
+ const x = arr[i];
5296
+ let low = 0;
5297
+ let high = tails.length;
5298
+ while (low < high) {
5299
+ const mid = (low + high) >> 1;
5300
+ if (arr[tails[mid]] < x) {
5301
+ low = mid + 1;
5302
+ }
5303
+ else {
5304
+ high = mid;
5305
+ }
5306
+ }
5307
+ if (low > 0) {
5308
+ prev[i] = tails[low - 1];
5309
+ }
5310
+ if (low === tails.length) {
5311
+ tails.push(i);
5312
+ }
5313
+ else {
5314
+ tails[low] = i;
5315
+ }
5316
+ }
5317
+ const lis = [];
5318
+ let current = tails[tails.length - 1];
5319
+ while (current !== -1) {
5320
+ lis.push(current);
5321
+ current = prev[current];
5322
+ }
5323
+ lis.reverse();
5324
+ return lis;
5325
+ }
5326
+ function reconcileKeyedLoopToParent(container, journal, parent, cursor, items, keyOf, renderItem) {
5327
+ const clientContainer = container;
5328
+ const nextLength = items.length;
5329
+ const firstLoopChild = vnode_getFirstChild(parent);
5330
+ const diffContext = createDiffContext(clientContainer, journal, cursor, null);
5331
+ const nextKeys = new Array(nextLength);
5332
+ if (firstLoopChild === null) {
5333
+ if (nextLength > 0) {
5334
+ return vnode_diff_range(clientContainer, journal, buildJsxRange(items, nextKeys, keyOf, renderItem, 0, nextLength - 1), parent, null, null, cursor, null, true, diffContext);
5335
+ }
5336
+ return;
5337
+ }
5338
+ if (nextLength === 0) {
5339
+ vnode_truncate(journal, parent, firstLoopChild, true);
5340
+ return;
5341
+ }
5342
+ let start = 0;
5343
+ let nextEnd = nextLength - 1;
5344
+ let oldStart = firstLoopChild;
5345
+ while (oldStart &&
5346
+ start <= nextEnd &&
5347
+ oldStart.key === (nextKeys[start] ??= keyOf(items[start], start))) {
5348
+ oldStart = oldStart.nextSibling;
5349
+ start++;
5350
+ }
5351
+ if (oldStart === null) {
5352
+ if (start > nextEnd) {
5353
+ return;
5354
+ }
5355
+ return vnode_diff_range(clientContainer, journal, buildJsxRange(items, nextKeys, keyOf, renderItem, start, nextEnd), parent, null, null, cursor, null, true, diffContext);
5356
+ }
5357
+ if (start > nextEnd) {
5358
+ vnode_truncate(journal, parent, oldStart, true);
5359
+ return;
5360
+ }
5361
+ const oldStartBoundary = oldStart.previousSibling;
5362
+ let oldEnd = parent.lastChild;
5363
+ while (oldEnd !== oldStartBoundary &&
5364
+ nextEnd >= start &&
5365
+ oldEnd.key === (nextKeys[nextEnd] ??= keyOf(items[nextEnd], nextEnd))) {
5366
+ oldEnd = oldEnd.previousSibling;
5367
+ nextEnd--;
5368
+ }
5369
+ const suffixAnchor = oldEnd ? oldEnd.nextSibling : oldStart;
5370
+ if (start > nextEnd) {
5371
+ let child = oldStart;
5372
+ while (child && child !== suffixAnchor) {
5373
+ const nextChild = child.nextSibling;
5374
+ vnode_remove(journal, parent, child, true);
5375
+ child = nextChild;
5376
+ }
5377
+ return;
5378
+ }
5379
+ if (oldEnd === oldStartBoundary) {
5380
+ const anchor = suffixAnchor;
5381
+ return vnode_diff_range(clientContainer, journal, buildJsxRange(items, nextKeys, keyOf, renderItem, start, nextEnd), parent, anchor, anchor, cursor, null, true, diffContext);
5382
+ }
5383
+ for (let i = start; i <= nextEnd; i++) {
5384
+ nextKeys[i] ??= keyOf(items[i], i);
5385
+ }
5386
+ const middleLength = nextEnd - start + 1;
5387
+ const nextIndexByKey = new Map();
5388
+ for (let i = start; i <= nextEnd; i++) {
5389
+ nextIndexByKey.set(nextKeys[i], i - start);
5390
+ }
5391
+ const prev = collectChildren(oldStart, oldEnd);
5392
+ const survivors = [];
5393
+ const prevRelIndexByKey = new Map();
5394
+ for (let i = 0; i < prev.length; i++) {
5395
+ const prevNode = prev[i];
5396
+ const prevKey = prevNode.key;
5397
+ if (nextIndexByKey.has(prevKey)) {
5398
+ prevRelIndexByKey.set(prevKey, survivors.length);
5399
+ survivors.push(prevNode);
5400
+ }
5401
+ else {
5402
+ vnode_remove(journal, parent, prevNode, true);
5403
+ }
5404
+ }
5405
+ const nextRefs = new Int32Array(middleLength);
5406
+ nextRefs.fill(-1);
5407
+ const seq = [];
5408
+ const seqOffsets = [];
5409
+ for (let offset = 0; offset < middleLength; offset++) {
5410
+ const relIndex = prevRelIndexByKey.get(nextKeys[start + offset]);
5411
+ if (relIndex !== undefined) {
5412
+ nextRefs[offset] = relIndex;
5413
+ seqOffsets.push(offset);
5414
+ seq.push(relIndex);
5415
+ }
5416
+ }
5417
+ const keepMask = new Uint8Array(middleLength);
5418
+ const lisPositions = longestIncreasingSubsequencePositions(seq);
5419
+ for (let i = 0; i < lisPositions.length; i++) {
5420
+ keepMask[seqOffsets[lisPositions[i]]] = 1;
5421
+ }
5422
+ let index = nextEnd;
5423
+ let anchor = suffixAnchor;
5424
+ const resume = () => {
5425
+ while (index >= start) {
5426
+ const offset = index - start;
5427
+ if (keepMask[offset] === 1) {
5428
+ anchor = survivors[nextRefs[offset]];
5429
+ index--;
5430
+ continue;
5431
+ }
5432
+ const existingRelIndex = nextRefs[offset];
5433
+ if (existingRelIndex !== -1) {
5434
+ const node = survivors[existingRelIndex];
5435
+ const alreadyPlaced = anchor === null ? parent.lastChild === node : node.nextSibling === anchor;
5436
+ if (!alreadyPlaced) {
5437
+ vnode_insertBefore(journal, parent, node, anchor);
5438
+ }
5439
+ anchor = node;
5440
+ index--;
5441
+ continue;
5442
+ }
5443
+ let blockStart = index;
5444
+ while (blockStart > start) {
5445
+ const prevOffset = blockStart - 1 - start;
5446
+ if (keepMask[prevOffset] === 1 || nextRefs[prevOffset] !== -1) {
5447
+ break;
5448
+ }
5449
+ blockStart--;
5450
+ }
5451
+ const blockLength = index - blockStart + 1;
5452
+ const result = vnode_diff_range(clientContainer, journal, buildJsxRange(items, nextKeys, null, renderItem, blockStart, index), parent, anchor, anchor, cursor, null, true, diffContext);
5453
+ return maybeThen(result, () => {
5454
+ const firstInserted = firstInsertedBeforeAnchor(parent, anchor, blockLength);
5455
+ if (isDev && !firstInserted) {
5456
+ throw new Error('Failed to insert keyed loop block');
5457
+ }
5458
+ anchor = firstInserted;
5459
+ index = blockStart - 1;
5460
+ return resume();
5461
+ });
5462
+ }
5463
+ };
5464
+ return resume();
5465
+ }
5466
+
5163
5467
  /**
5164
5468
  * Executes tasks for a vNode if the TASKS dirty bit is set. Tasks are stored in the ELEMENT_SEQ
5165
5469
  * property and executed in order.
@@ -5232,6 +5536,8 @@ function executeNodeDiff(vNode, container, journal, cursor) {
5232
5536
  if (!jsx) {
5233
5537
  return;
5234
5538
  }
5539
+ // cleanup payload
5540
+ setNodeDiffPayload(vNode, null);
5235
5541
  if (isSignal(jsx)) {
5236
5542
  jsx = jsx.value;
5237
5543
  }
@@ -5392,6 +5698,35 @@ function executeCompute(vNode, container) {
5392
5698
  }
5393
5699
  });
5394
5700
  }
5701
+ /**
5702
+ * Executes a reconcile chore for a vNode if the RECONCILE dirty bit is set. This handles the
5703
+ * reconciliation of a keyed loop.
5704
+ *
5705
+ * @param container - The container
5706
+ * @param journal - The journal
5707
+ * @param vNode - The vNode
5708
+ * @returns Promise if reconcile is async, void otherwise
5709
+ */
5710
+ function executeReconcile(vNode, container, journal, cursor) {
5711
+ vNode.dirty &= -129 /* ChoreBits.RECONCILE */;
5712
+ const host = vNode;
5713
+ const props = container.getHostProp(host, ELEMENT_PROPS) || null;
5714
+ if (!props) {
5715
+ return;
5716
+ }
5717
+ let items = _getProps(props, 'items');
5718
+ if (isSignal(items)) {
5719
+ items = untrack(items);
5720
+ }
5721
+ const keyQrl = _getProps(props, 'key$');
5722
+ const itemQrl = _getProps(props, 'item$');
5723
+ const keyOf = keyQrl.resolved;
5724
+ const itemFn = itemQrl.resolved;
5725
+ if (keyOf !== undefined && itemFn !== undefined) {
5726
+ return reconcileKeyedLoopToParent(container, journal, host, cursor, items, keyOf, itemFn);
5727
+ }
5728
+ return maybeThen(keyQrl.resolve(), (resolvedKeyOf) => maybeThen(itemQrl.resolve(), (resolvedItemFn) => reconcileKeyedLoopToParent(container, journal, host, cursor, items, resolvedKeyOf, resolvedItemFn)));
5729
+ }
5395
5730
 
5396
5731
  /**
5397
5732
  * Executes the flush phase for a cursor.
@@ -5698,7 +6033,7 @@ function walkCursor(cursor, options) {
5698
6033
  return;
5699
6034
  }
5700
6035
  // Skip if the vNode is not dirty
5701
- if (!(currentVNode.dirty & 127 /* ChoreBits.DIRTY_MASK */)) {
6036
+ if (!(currentVNode.dirty & 255 /* ChoreBits.DIRTY_MASK */)) {
5702
6037
  // Move to next node
5703
6038
  setCursorPosition(container, cursorData, getNextVNode(currentVNode, cursor));
5704
6039
  continue;
@@ -5717,7 +6052,7 @@ function walkCursor(cursor, options) {
5717
6052
  }
5718
6053
  }
5719
6054
  // Clear dirty bits and move to next node
5720
- currentVNode.dirty &= -128 /* ChoreBits.DIRTY_MASK */;
6055
+ currentVNode.dirty &= -256 /* ChoreBits.DIRTY_MASK */;
5721
6056
  setCursorPosition(container, cursorData, getNextVNode(currentVNode, cursor));
5722
6057
  continue;
5723
6058
  }
@@ -5733,6 +6068,9 @@ function walkCursor(cursor, options) {
5733
6068
  else if (currentVNode.dirty & 4 /* ChoreBits.COMPONENT */) {
5734
6069
  result = executeComponentChore(currentVNode, container, journal, cursor);
5735
6070
  }
6071
+ else if (currentVNode.dirty & 128 /* ChoreBits.RECONCILE */) {
6072
+ result = executeReconcile(currentVNode, container, journal, cursor);
6073
+ }
5736
6074
  else if (currentVNode.dirty & 8 /* ChoreBits.NODE_PROPS */) {
5737
6075
  executeNodeProps(currentVNode, journal);
5738
6076
  }
@@ -5778,11 +6116,11 @@ function walkCursor(cursor, options) {
5778
6116
  }
5779
6117
  }
5780
6118
  isDev &&
5781
- assertFalse(!!(cursor.dirty & 127 /* ChoreBits.DIRTY_MASK */ && !cursorData.position), 'Cursor is still dirty and position is not set after walking');
6119
+ assertFalse(!!(cursor.dirty & 255 /* ChoreBits.DIRTY_MASK */ && !cursorData.position), 'Cursor is still dirty and position is not set after walking');
5782
6120
  finishWalk(container, cursor, cursorData, isRunningOnServer);
5783
6121
  }
5784
6122
  function finishWalk(container, cursor, cursorData, isServer) {
5785
- if (!(cursor.dirty & 127 /* ChoreBits.DIRTY_MASK */)) {
6123
+ if (!(cursor.dirty & 255 /* ChoreBits.DIRTY_MASK */)) {
5786
6124
  removeCursorFromQueue(cursor, container);
5787
6125
  if (!isServer) {
5788
6126
  executeFlushPhase(cursor, container);
@@ -5854,7 +6192,7 @@ function partitionDirtyChildren(dirtyChildren, parent) {
5854
6192
  /** @returns Next vNode to process, or null if traversal is complete */
5855
6193
  function getNextVNode(vNode, cursor) {
5856
6194
  if (vNode === cursor) {
5857
- if (cursor.dirty & 127 /* ChoreBits.DIRTY_MASK */) {
6195
+ if (cursor.dirty & 255 /* ChoreBits.DIRTY_MASK */) {
5858
6196
  return cursor;
5859
6197
  }
5860
6198
  return null;
@@ -5868,7 +6206,7 @@ function getNextVNode(vNode, cursor) {
5868
6206
  parent = vNode.parent;
5869
6207
  }
5870
6208
  if (!parent) {
5871
- if (cursor.dirty & 127 /* ChoreBits.DIRTY_MASK */) {
6209
+ if (cursor.dirty & 255 /* ChoreBits.DIRTY_MASK */) {
5872
6210
  return cursor;
5873
6211
  }
5874
6212
  return null;
@@ -5879,7 +6217,7 @@ function getNextVNode(vNode, cursor) {
5879
6217
  let count = len;
5880
6218
  while (count-- > 0) {
5881
6219
  const nextVNode = dirtyChildren[index];
5882
- if (nextVNode.dirty & 127 /* ChoreBits.DIRTY_MASK */) {
6220
+ if (nextVNode.dirty & 255 /* ChoreBits.DIRTY_MASK */) {
5883
6221
  parent.nextDirtyChildIndex = (index + 1) % len;
5884
6222
  return nextVNode;
5885
6223
  }
@@ -5955,7 +6293,7 @@ function _executeSsrChores(container, ssrNode) {
5955
6293
  if (ssrNode.dirty & 16 /* ChoreBits.COMPUTE */) {
5956
6294
  executeCompute(ssrNode, container);
5957
6295
  }
5958
- if (ssrNode.dirty & 127 /* ChoreBits.DIRTY_MASK */) {
6296
+ if (ssrNode.dirty & 255 /* ChoreBits.DIRTY_MASK */) {
5959
6297
  // We are running on the server.
5960
6298
  // On server we can't schedule task for a different host!
5961
6299
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -5970,7 +6308,7 @@ function _executeSsrChores(container, ssrNode) {
5970
6308
  This is often caused by modifying a signal in an already rendered component during SSR.`;
5971
6309
  logWarn(warningMessage);
5972
6310
  }
5973
- ssrNode.dirty &= -128 /* ChoreBits.DIRTY_MASK */;
6311
+ ssrNode.dirty &= -256 /* ChoreBits.DIRTY_MASK */;
5974
6312
  return;
5975
6313
  }
5976
6314
  let promise = null;
@@ -5980,13 +6318,17 @@ function _executeSsrChores(container, ssrNode) {
5980
6318
  promise = result;
5981
6319
  }
5982
6320
  }
6321
+ if (ssrNode.dirty & 128 /* ChoreBits.RECONCILE */) {
6322
+ const result = executeReconcileChore(container, ssrNode);
6323
+ promise = promise ? promise.then(() => result) : result;
6324
+ }
5983
6325
  // In SSR, we don't handle the COMPONENT bit here.
5984
6326
  // During initial render, if a task completes and marks the component dirty,
5985
6327
  // we want to leave the COMPONENT bit set so that executeComponent can detect
5986
6328
  // it after $waitOn$ completes and re-execute the component function.
5987
6329
  // executeComponent will clear the bit after re-executing.
5988
6330
  // Clear all dirty bits EXCEPT COMPONENT
5989
- ssrNode.dirty &= -124;
6331
+ ssrNode.dirty &= -252;
5990
6332
  if (promise) {
5991
6333
  return promise;
5992
6334
  }
@@ -6028,6 +6370,32 @@ function executeNodePropChore(container, ssrNode) {
6028
6370
  container.addBackpatchEntry(ssrNode.id, property, serializedValue);
6029
6371
  }
6030
6372
  }
6373
+ async function executeReconcileChore(container, ssrNode) {
6374
+ ssrNode.dirty &= -129 /* ChoreBits.RECONCILE */;
6375
+ const host = ssrNode;
6376
+ const props = container.getHostProp(host, ELEMENT_PROPS) || null;
6377
+ if (!props) {
6378
+ return;
6379
+ }
6380
+ let items = _getProps(props, 'items');
6381
+ if (isSignal(items)) {
6382
+ items = untrack(items);
6383
+ }
6384
+ const keyOf = (await _getProps(props, 'key$').resolve());
6385
+ const itemFn = (await _getProps(props, 'item$').resolve());
6386
+ const children = [];
6387
+ for (let i = 0; i < items.length; i++) {
6388
+ const item = items[i];
6389
+ const jsx = itemFn(item, i);
6390
+ const key = keyOf(item, i);
6391
+ jsx.key = key;
6392
+ children.push(jsx);
6393
+ }
6394
+ await container.renderJSX(children, {
6395
+ currentStyleScoped: null,
6396
+ parentComponentFrame: container.getComponentFrame(0),
6397
+ });
6398
+ }
6031
6399
 
6032
6400
  /** Reusable path array to avoid allocations */
6033
6401
  const reusablePath = [];
@@ -6051,7 +6419,7 @@ function propagateToCursorRoot(vNode, cursorRoot) {
6051
6419
  reusablePath.push(vNode);
6052
6420
  let current = vNode.slotParent || vNode.parent;
6053
6421
  while (current) {
6054
- const isDirty = current.dirty & 127 /* ChoreBits.DIRTY_MASK */;
6422
+ const isDirty = current.dirty & 255 /* ChoreBits.DIRTY_MASK */;
6055
6423
  const currentIsCursor = isCursor(current);
6056
6424
  // Stop when we reach the cursor root or a dirty ancestor
6057
6425
  if (current === cursorRoot || isDirty) {
@@ -6123,9 +6491,9 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
6123
6491
  }
6124
6492
  return;
6125
6493
  }
6126
- const isRealDirty = bits & 127 /* ChoreBits.DIRTY_MASK */;
6494
+ const isRealDirty = bits & 255 /* ChoreBits.DIRTY_MASK */;
6127
6495
  // If already dirty, no need to propagate again
6128
- if ((isRealDirty ? prevDirty & 127 /* ChoreBits.DIRTY_MASK */ : prevDirty) || vNode === cursorRoot) {
6496
+ if ((isRealDirty ? prevDirty & 255 /* ChoreBits.DIRTY_MASK */ : prevDirty) || vNode === cursorRoot) {
6129
6497
  return;
6130
6498
  }
6131
6499
  const parent = vNode.slotParent || vNode.parent;
@@ -6135,7 +6503,7 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
6135
6503
  return;
6136
6504
  }
6137
6505
  // We must attach to a cursor subtree if it exists
6138
- if (parent && parent.dirty & 127 /* ChoreBits.DIRTY_MASK */) {
6506
+ if (parent && parent.dirty & 255 /* ChoreBits.DIRTY_MASK */) {
6139
6507
  if (isRealDirty) {
6140
6508
  parent.dirty |= 32 /* ChoreBits.CHILDREN */;
6141
6509
  }
@@ -6516,16 +6884,26 @@ const vnode_ensureElementInflated = (container, vnode) => {
6516
6884
  }
6517
6885
  }
6518
6886
  };
6887
+ const unwrapEventHandlerQrl = (handler) => {
6888
+ if (handler.$symbol$ === '_run') {
6889
+ const innerHandler = handler.getCaptured()?.[0];
6890
+ if (isQrl(innerHandler)) {
6891
+ return innerHandler;
6892
+ }
6893
+ }
6894
+ return handler;
6895
+ };
6519
6896
  function registerQrlHandlers(attr, key, container, element) {
6520
6897
  const value = attr.value;
6521
6898
  const scopedKebabName = key.slice(2);
6522
6899
  const qrls = value.split('|');
6523
- const handlers = qrls.map((qrl) => {
6524
- const handler = parseQRL(qrl, container);
6900
+ const handlers = [];
6901
+ for (let i = 0; i < qrls.length; i++) {
6902
+ const handler = unwrapEventHandlerQrl(parseQRL(qrls[i], container));
6525
6903
  // These QRLs are mostly _run and _task and don't need wrapping with retryOnPromise
6526
- return handler;
6527
- });
6528
- (element._qDispatch ||= {})[scopedKebabName] = handlers;
6904
+ handlers.push(runEventHandlerQRL.bind(null, handler));
6905
+ }
6906
+ (element._qDispatch ||= {})[scopedKebabName] = handlers.length === 1 ? handlers[0] : handlers;
6529
6907
  }
6530
6908
  /** Walks the direct children of a parent node and calls the callback for each child. */
6531
6909
  function vnode_walkDirectChildren(journal, vParent, callback) {
@@ -7204,9 +7582,17 @@ const vnode_truncate = (journal, vParent, vDelete, removeDOM = true) => {
7204
7582
  addVNodeOperation(journal, createRemoveAllChildrenOperation(vParent.node));
7205
7583
  }
7206
7584
  else {
7207
- vnode_walkDirectChildren(journal, vParent, (vNode) => {
7208
- addVNodeOperation(journal, createDeleteOperation(vNode.node));
7209
- });
7585
+ const domParentVNode = vnode_getDomParentVNode(vParent, false);
7586
+ if (domParentVNode &&
7587
+ domParentVNode.firstChild === vParent &&
7588
+ domParentVNode.lastChild === vParent) {
7589
+ addVNodeOperation(journal, createRemoveAllChildrenOperation(parent));
7590
+ }
7591
+ else {
7592
+ vnode_walkDirectChildren(journal, vParent, (vNode) => {
7593
+ addVNodeOperation(journal, createDeleteOperation(vNode.node));
7594
+ });
7595
+ }
7210
7596
  }
7211
7597
  }
7212
7598
  const vPrevious = vDelete.previousSibling;
@@ -8198,7 +8584,7 @@ const useSequentialScope = () => {
8198
8584
  seq.push(undefined);
8199
8585
  }
8200
8586
  const set = (value) => {
8201
- if (qDev && qSerialize) {
8587
+ if (qDev) {
8202
8588
  verifySerializable(value);
8203
8589
  }
8204
8590
  return (seq[seqIdx] = value);
@@ -8335,7 +8721,7 @@ const useContextProvider = (context, newValue) => {
8335
8721
  if (qDev) {
8336
8722
  validateContext(context);
8337
8723
  }
8338
- if (qDev && qSerialize) {
8724
+ if (qDev) {
8339
8725
  verifySerializable(newValue);
8340
8726
  }
8341
8727
  iCtx.$container$.setContext(iCtx.$hostElement$, context, newValue);
@@ -8756,13 +9142,6 @@ function retrieveVNodeOrDocument(container, value) {
8756
9142
  : container.element?.ownerDocument;
8757
9143
  }
8758
9144
 
8759
- // https://regexr.com/68v72
8760
- // @ts-expect-error this is a valid regex
8761
- const EXTRACT_IMPORT_PATH = /\(\s*(['"])([^\1]+)\1\s*\)/;
8762
- // https://regexr.com/690ds
8763
- const EXTRACT_SELF_IMPORT = /Promise\s*\.\s*resolve/;
8764
- // https://regexr.com/6a83h
8765
- const EXTRACT_FILE_NAME = /[\\/(]([\w\d.\-_]+\.(js|ts)x?):/;
8766
9145
  // <docs markdown="../../readme.md#qrl">
8767
9146
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
8768
9147
  // (edit ../../readme.md#qrl instead and run `pnpm docs.sync`)
@@ -8784,29 +9163,6 @@ const qrl = (chunkOrFn, symbol, lexicalScopeCapture, stackOffset = 0) => {
8784
9163
  let symbolFn = null;
8785
9164
  if (isFunction(chunkOrFn)) {
8786
9165
  symbolFn = chunkOrFn;
8787
- if (qSerialize) {
8788
- let match;
8789
- const srcCode = String(chunkOrFn);
8790
- if ((match = srcCode.match(EXTRACT_IMPORT_PATH)) && match[2]) {
8791
- chunk = match[2];
8792
- }
8793
- else if ((match = srcCode.match(EXTRACT_SELF_IMPORT))) {
8794
- const ref = 'QWIK-SELF';
8795
- const frames = new Error(ref).stack.split('\n');
8796
- const start = frames.findIndex((f) => f.includes(ref));
8797
- const frame = frames[start + 2 + stackOffset];
8798
- match = frame.match(EXTRACT_FILE_NAME);
8799
- if (!match) {
8800
- chunk = 'main';
8801
- }
8802
- else {
8803
- chunk = match[1];
8804
- }
8805
- }
8806
- else {
8807
- throw qError(6 /* QError.dynamicImportFailed */, [srcCode]);
8808
- }
8809
- }
8810
9166
  }
8811
9167
  else if (isString(chunkOrFn)) {
8812
9168
  chunk = chunkOrFn;
@@ -10450,9 +10806,7 @@ const _hmr = (event, element) => {
10450
10806
  // Maybe we should use a qrl registry to invalidate all QRLs from a parent?
10451
10807
  const qrl = container.getHostProp(host, OnRenderProp);
10452
10808
  if (qrl) {
10453
- // This code is highly coupled to the internal implementation of QRL
10454
- const instance = qrl.__proto__;
10455
- const lazy = instance.$lazy$;
10809
+ const lazy = qrl.$lazy$;
10456
10810
  const chunk = lazy.$chunk$;
10457
10811
  if (chunk) {
10458
10812
  /**
@@ -10462,7 +10816,7 @@ const _hmr = (event, element) => {
10462
10816
  const bustUrl = chunk.split('?')[0] + '?t=' + Date.now();
10463
10817
  lazy.$chunk$ = bustUrl;
10464
10818
  lazy.$ref$ = undefined;
10465
- instance.resolved = undefined;
10819
+ qrl.resolved = undefined;
10466
10820
  // Force rerender
10467
10821
  markVNodeDirty(container, host, 4 /* ChoreBits.COMPONENT */);
10468
10822
  }
@@ -11345,6 +11699,7 @@ class DomContainer extends _SharedContainer {
11345
11699
  this.$setServerData$();
11346
11700
  element.setAttribute(QContainerAttr, "resumed" /* QContainerValue.RESUMED */);
11347
11701
  element.qContainer = this;
11702
+ element.qDestroy = () => this.$destroy$();
11348
11703
  const qwikStates = element.querySelectorAll('script[type="qwik/state"]');
11349
11704
  if (qwikStates.length !== 0) {
11350
11705
  const lastState = qwikStates[qwikStates.length - 1];
@@ -11357,6 +11712,19 @@ class DomContainer extends _SharedContainer {
11357
11712
  element.dispatchEvent(new CustomEvent('qresume', { bubbles: true }));
11358
11713
  }
11359
11714
  }
11715
+ /** Tear down this container so stale references fail gracefully. */
11716
+ $destroy$() {
11717
+ this.vNodeLocate = () => null;
11718
+ this.$rawStateData$.length = 0;
11719
+ this.$stateData$.length = 0;
11720
+ this.$getObjectById$ = () => undefined;
11721
+ const el = this.element;
11722
+ el.qContainer = undefined;
11723
+ el.qVnodeData = undefined;
11724
+ el.qVNodeRefs = undefined;
11725
+ el.removeAttribute(QContainerAttr);
11726
+ el.ownerDocument.qVNodeData = undefined;
11727
+ }
11360
11728
  /**
11361
11729
  * The first time we render we need to hoist the styles. (Meaning we need to move all styles from
11362
11730
  * component inline to <head>)
@@ -11562,23 +11930,45 @@ const useTaskQrl = (qrl, opts) => {
11562
11930
  }
11563
11931
  };
11564
11932
  const runTask = (task, container, host) => {
11933
+ const pendingTask = task.$taskPromise$;
11934
+ if (pendingTask) {
11935
+ return pendingTask;
11936
+ }
11565
11937
  task.$flags$ &= -5 /* TaskFlags.DIRTY */;
11566
- cleanupDestroyable(task);
11567
- const iCtx = newInvokeContext(container.$locale$, host, TaskEvent);
11568
- iCtx.$container$ = container;
11569
- const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
11570
- const track = trackFn(task, container);
11571
- const [cleanup] = cleanupFn(task, (reason) => container.handleError(reason, host));
11572
- const taskApi = { track, cleanup };
11573
- return safeCall(() => taskFn(taskApi), cleanup, (err) => {
11574
- // If a Promise is thrown, that means we need to re-run the task.
11575
- if (isPromise(err)) {
11576
- return err.then(() => runTask(task, container, host));
11577
- }
11578
- else {
11579
- container.handleError(err, host);
11580
- }
11938
+ const handleError = (reason) => container.handleError(reason, host);
11939
+ let taskPromise = null;
11940
+ const result = maybeThen(cleanupAsyncDestroyable(task, handleError), () => {
11941
+ const iCtx = newInvokeContext(container.$locale$, host, TaskEvent);
11942
+ iCtx.$container$ = container;
11943
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
11944
+ const track = trackFn(task, container);
11945
+ const [cleanup] = cleanupFn(task, handleError);
11946
+ const taskApi = { track, cleanup };
11947
+ return safeCall(() => taskFn(taskApi), cleanup, (err) => {
11948
+ // If a Promise is thrown, that means we need to re-run the task.
11949
+ if (isPromise(err)) {
11950
+ return err.then(() => {
11951
+ if (task.$taskPromise$ === taskPromise) {
11952
+ task.$taskPromise$ = null;
11953
+ }
11954
+ return runTask(task, container, host);
11955
+ });
11956
+ }
11957
+ else {
11958
+ handleError(err);
11959
+ }
11960
+ });
11581
11961
  });
11962
+ if (isPromise(result)) {
11963
+ taskPromise = result.finally(() => {
11964
+ if (task.$taskPromise$ === taskPromise) {
11965
+ task.$taskPromise$ = null;
11966
+ }
11967
+ });
11968
+ task.$taskPromise$ = taskPromise;
11969
+ return taskPromise;
11970
+ }
11971
+ return result;
11582
11972
  };
11583
11973
  class Task extends BackRef {
11584
11974
  $flags$;
@@ -11587,6 +11977,8 @@ class Task extends BackRef {
11587
11977
  $qrl$;
11588
11978
  $state$;
11589
11979
  $destroy$;
11980
+ $destroyPromise$;
11981
+ $taskPromise$ = null;
11590
11982
  constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
11591
11983
  super();
11592
11984
  this.$flags$ = $flags$;
@@ -11707,8 +12099,8 @@ const scheduleEffects = (container, signal, effects) => {
11707
12099
  }
11708
12100
  };
11709
12101
  const effectsSnapshot = Array.from(effects);
11710
- for (const effect of effectsSnapshot) {
11711
- scheduleEffect(effect);
12102
+ for (let i = 0; i < effectsSnapshot.length; i++) {
12103
+ scheduleEffect(effectsSnapshot[i]);
11712
12104
  }
11713
12105
  }
11714
12106
  };
@@ -12613,37 +13005,26 @@ class LazyRef {
12613
13005
  return this.$ref$;
12614
13006
  }
12615
13007
  }
13008
+ const QRL_STATE = Symbol('qrl-state');
12616
13009
  /**
12617
- * When a method is called on the qrlFn wrapper function, `this` is the function, not the QRLClass
12618
- * instance that holds the data. This helper returns the actual instance by checking whether `this`
12619
- * owns `resolved` (always set on the instance).
13010
+ * QRL methods may run with `this` set either to the callable wrapper or directly to the backing
13011
+ * state object. This helper normalizes both cases to the shared backing state.
12620
13012
  */
12621
13013
  const getInstance = (instance) => {
12622
- return Object.prototype.hasOwnProperty.call(instance, 'resolved')
12623
- ? instance
12624
- : Object.getPrototypeOf(instance);
13014
+ return instance?.[QRL_STATE] ?? instance;
12625
13015
  };
12626
13016
  /**
12627
- * We use a class here to avoid copying all the methods for every QRL instance. The QRL itself is a
12628
- * function that calls the internal $callFn$ method, and we set the prototype to the class instance
12629
- * so it has access to all the properties and methods. That's why we need to extend Function, so
12630
- * that `.apply()` etc work.
12631
- *
12632
- * So a QRL is a function that has a prototype of a QRLClass instance. This is unconventional, but
12633
- * it allows us to have a callable QRL that is also a class.
12634
- *
12635
- * Note the use of getInstance everywhere when writing to `this`. If you write to `this` directly,
12636
- * it will be stored on the function itself, and we don't want that because the QRLClass instance
12637
- * doesn't have access to it, and it uses more memory.
13017
+ * QRL state lives in a plain object. The callable wrapper stores that state under a symbol and uses
13018
+ * a shared prototype derived from Function.prototype for methods/getters. This keeps QRLs callable
13019
+ * without using a unique state object as each function's prototype.
12638
13020
  */
12639
- class QRLClass extends Function {
13021
+ class QRLClass {
12640
13022
  $lazy$;
12641
13023
  resolved = undefined;
12642
13024
  // This is defined or undefined for the lifetime of the QRL, so we set it lazily
12643
13025
  $captures$;
12644
13026
  $container$;
12645
13027
  constructor($lazy$, $captures$, container) {
12646
- super();
12647
13028
  this.$lazy$ = $lazy$;
12648
13029
  if ($captures$) {
12649
13030
  this.$captures$ = $captures$;
@@ -12662,76 +13043,140 @@ class QRLClass extends Function {
12662
13043
  // If it is plain value with deserialized or missing captures, resolve it immediately
12663
13044
  // Otherwise we keep using the async path so we can wait for qrls to load
12664
13045
  if ($lazy$.$ref$ != null && typeof this.$captures$ !== 'string' && !isPromise($lazy$.$ref$)) {
12665
- // we can pass this instead of using getInstance because we know we are not the qrlFn
12666
13046
  this.resolved = bindCaptures(this, $lazy$.$ref$);
12667
13047
  }
12668
13048
  }
12669
- w(captures) {
12670
- const newQrl = new QRLClass(this.$lazy$, captures, this.$captures$ ? this.$container$ : undefined);
12671
- return makeQrlFn(newQrl);
12672
- }
12673
- s(ref) {
12674
- const qrl = getInstance(this);
12675
- qrl.$lazy$.$setRef$(ref);
12676
- qrl.resolved = bindCaptures(qrl, ref);
12677
- }
12678
- // --- Getter proxies for backward compat ---
12679
- get $chunk$() {
12680
- return this.$lazy$.$chunk$;
12681
- }
12682
- get $symbol$() {
12683
- return this.$lazy$.$symbol$;
12684
- }
12685
- get $hash$() {
12686
- return this.$lazy$.$hash$;
12687
- }
12688
- get dev() {
12689
- return this.$lazy$.dev;
13049
+ }
13050
+ const qrlCallFn = function (withThis, ...args) {
13051
+ const qrl = getInstance(this);
13052
+ if (qrl.resolved) {
13053
+ return qrl.resolved.apply(withThis, args);
12690
13054
  }
12691
- $callFn$(withThis, ...args) {
12692
- if (this.resolved) {
12693
- return this.resolved.apply(withThis, args);
13055
+ // Not resolved yet: we'll return a promise
13056
+ // grab the context while we are sync
13057
+ const ctx = tryGetInvokeContext();
13058
+ return qrlResolve
13059
+ .call(qrl, ctx?.$container$)
13060
+ .then(() => invokeApply.call(withThis, ctx, qrl.resolved, args));
13061
+ };
13062
+ const qrlWithCaptures = function (captures) {
13063
+ const qrl = getInstance(this);
13064
+ const newQrl = new QRLClass(qrl.$lazy$, captures, qrl.$captures$ ? qrl.$container$ : undefined);
13065
+ return makeQrlFn(newQrl);
13066
+ };
13067
+ const qrlSetRef = function (ref) {
13068
+ const qrl = getInstance(this);
13069
+ qrl.$lazy$.$setRef$(ref);
13070
+ qrl.resolved = bindCaptures(qrl, ref);
13071
+ };
13072
+ const qrlResolve = async function (container) {
13073
+ const qrl = getInstance(this);
13074
+ return maybeThen($resolve$(qrl, container), () => qrl.resolved);
13075
+ };
13076
+ const qrlGetSymbol = function () {
13077
+ return getInstance(this).$lazy$.$symbol$;
13078
+ };
13079
+ const qrlGetHash = function () {
13080
+ return getInstance(this).$lazy$.$hash$;
13081
+ };
13082
+ const qrlGetCaptured = function () {
13083
+ const qrl = getInstance(this);
13084
+ ensureQrlCaptures(qrl);
13085
+ return qrl.$captures$;
13086
+ };
13087
+ const qrlGetFn = function (currentCtx, beforeFn) {
13088
+ const qrl = getInstance(this);
13089
+ const bound = (...args) => {
13090
+ if (!qrl.resolved) {
13091
+ return qrlResolve.call(qrl).then((fn) => {
13092
+ if (qDev && !isFunction(fn)) {
13093
+ throw qError(5 /* QError.qrlIsNotFunction */);
13094
+ }
13095
+ return bound(...args);
13096
+ });
12694
13097
  }
12695
- // Not resolved yet: we'll return a promise
12696
- // grab the context while we are sync
12697
- const ctx = tryGetInvokeContext();
12698
- return this.resolve(ctx?.$container$).then(() => invokeApply.call(withThis, ctx, this.resolved, args));
12699
- }
12700
- async resolve(container) {
12701
- // We need to write to the QRLClass instance, not the function
12702
- const qrl = getInstance(this);
12703
- return maybeThen($resolve$(qrl, container), () => qrl.resolved);
12704
- }
12705
- getSymbol() {
12706
- return this.$symbol$;
12707
- }
12708
- getHash() {
12709
- return this.$hash$;
12710
- }
12711
- getCaptured() {
12712
- const qrl = getInstance(this);
12713
- ensureQrlCaptures(qrl);
12714
- return qrl.$captures$;
12715
- }
12716
- getFn(currentCtx, beforeFn) {
12717
- const qrl = getInstance(this);
12718
- const bound = (...args) => {
12719
- if (!qrl.resolved) {
12720
- return qrl.resolve().then((fn) => {
12721
- if (qDev && !isFunction(fn)) {
12722
- throw qError(5 /* QError.qrlIsNotFunction */);
12723
- }
12724
- return bound(...args);
12725
- });
12726
- }
12727
- if (beforeFn && beforeFn() === false) {
12728
- return undefined;
12729
- }
12730
- return invokeApply(currentCtx, qrl.resolved, args);
12731
- };
12732
- return bound;
12733
- }
12734
- }
13098
+ if (beforeFn && beforeFn() === false) {
13099
+ return undefined;
13100
+ }
13101
+ return invokeApply(currentCtx, qrl.resolved, args);
13102
+ };
13103
+ return bound;
13104
+ };
13105
+ const QRL_FUNCTION_PROTO = Object.create(Function.prototype, {
13106
+ resolved: {
13107
+ get() {
13108
+ return this[QRL_STATE].resolved;
13109
+ },
13110
+ set(value) {
13111
+ this[QRL_STATE].resolved = value;
13112
+ },
13113
+ },
13114
+ $captures$: {
13115
+ get() {
13116
+ return this[QRL_STATE].$captures$;
13117
+ },
13118
+ set(value) {
13119
+ this[QRL_STATE].$captures$ = value;
13120
+ },
13121
+ },
13122
+ $container$: {
13123
+ get() {
13124
+ return this[QRL_STATE].$container$;
13125
+ },
13126
+ set(value) {
13127
+ this[QRL_STATE].$container$ = value;
13128
+ },
13129
+ },
13130
+ $lazy$: {
13131
+ get() {
13132
+ return this[QRL_STATE].$lazy$;
13133
+ },
13134
+ },
13135
+ $chunk$: {
13136
+ get() {
13137
+ return this[QRL_STATE].$lazy$.$chunk$;
13138
+ },
13139
+ },
13140
+ $symbol$: {
13141
+ get() {
13142
+ return this[QRL_STATE].$lazy$.$symbol$;
13143
+ },
13144
+ },
13145
+ $hash$: {
13146
+ get() {
13147
+ return this[QRL_STATE].$lazy$.$hash$;
13148
+ },
13149
+ },
13150
+ dev: {
13151
+ get() {
13152
+ return this[QRL_STATE].$lazy$.dev;
13153
+ },
13154
+ },
13155
+ $callFn$: {
13156
+ value: qrlCallFn,
13157
+ },
13158
+ w: {
13159
+ value: qrlWithCaptures,
13160
+ },
13161
+ s: {
13162
+ value: qrlSetRef,
13163
+ },
13164
+ resolve: {
13165
+ value: qrlResolve,
13166
+ },
13167
+ getSymbol: {
13168
+ value: qrlGetSymbol,
13169
+ },
13170
+ getHash: {
13171
+ value: qrlGetHash,
13172
+ },
13173
+ getCaptured: {
13174
+ value: qrlGetCaptured,
13175
+ },
13176
+ getFn: {
13177
+ value: qrlGetFn,
13178
+ },
13179
+ });
12735
13180
  /**
12736
13181
  * The current captured scope during QRL invocation. This is used to provide the lexical scope for
12737
13182
  * QRL functions. It is used one time per invocation, synchronously, so it is safe to store it in
@@ -12829,12 +13274,12 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, captures, container) => {
12829
13274
  return makeQrlFn(qrl);
12830
13275
  };
12831
13276
  const makeQrlFn = (qrl) => {
12832
- // The QRL has to be callable, so we create a function that calls the internal $callFn$
13277
+ // The QRL has to be callable, so we create a function and attach the per-instance state to it.
12833
13278
  const qrlFn = async function (...args) {
12834
- return qrl.$callFn$(this, ...args);
13279
+ return qrlCallFn.call(qrlFn, this, ...args);
12835
13280
  };
12836
- // ...and set the prototype to the QRL instance so it has all the properties and methods without copying them
12837
- Object.setPrototypeOf(qrlFn, qrl);
13281
+ qrlFn[QRL_STATE] = qrl;
13282
+ Object.setPrototypeOf(qrlFn, QRL_FUNCTION_PROTO);
12838
13283
  return qrlFn;
12839
13284
  };
12840
13285
  const EMITTED = /*#__PURE__*/ new Set();
@@ -14046,6 +14491,31 @@ const useErrorBoundary = () => {
14046
14491
  return error;
14047
14492
  };
14048
14493
 
14494
+ /** @internal */
14495
+ const eachCmpTask = async ({ track }) => {
14496
+ const props = _captures[0];
14497
+ track(() => props.items);
14498
+ const context = tryGetInvokeContext();
14499
+ const host = context.$hostElement$;
14500
+ const container = context.$container$;
14501
+ markVNodeDirty(container, host, 128 /* ChoreBits.RECONCILE */);
14502
+ const isSsr = import.meta.env.TEST ? isServerPlatform() : isServer;
14503
+ if (isSsr) {
14504
+ await container.$renderPromise$;
14505
+ }
14506
+ };
14507
+ /** @internal */
14508
+ const eachCmp = (props) => {
14509
+ if (!__EXPERIMENTAL__.each) {
14510
+ throw new Error('Each is experimental and must be enabled with `experimental: ["each"]` in the `qwikVite` plugin.');
14511
+ }
14512
+ useTaskQrl(/*#__PURE__*/ inlinedQrl(eachCmpTask, '_eaT', [props]));
14513
+ return SkipRender;
14514
+ };
14515
+ /** @public @experimental */
14516
+ const Each = /*#__PURE__*/ componentQrl(
14517
+ /*#__PURE__*/ inlinedQrl(eachCmp, '_eaC'));
14518
+
14049
14519
  // keep this import from core/build so the cjs build works
14050
14520
  /**
14051
14521
  * @deprecated This is no longer needed as the preloading happens automatically in qrl-class.ts.
@@ -14140,5 +14610,5 @@ if (import.meta.hot) {
14140
14610
  });
14141
14611
  }
14142
14612
 
14143
- export { $, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, createQRL as _createQRL, _deserialize, _dumpState, _executeSsrChores, _fnSignal, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, _getVarProps, _hasStoreEffects, _hmr, isJSXNode as _isJSXNode, isStore as _isStore, isStringifiable as _isStringifiable, isTask as _isTask, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, mapApp_findIndx as _mapApp_findIndx, mapArray_get as _mapArray_get, mapArray_set as _mapArray_set, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, qrlToString as _qrlToString, _regSymbol, _removeProjection, _res, _resolveContextWithoutSequentialScope, _restProps, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, scheduleTask as _task, _updateProjectionProps, _useHmr, _val, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getFirstChild as _vnode_getFirstChild, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _wrapProp, _wrapSignal, component$, componentQrl, createAsync$, createAsyncSignal as createAsyncQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, forceStoreEffects, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsync$, useAsyncQrl, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useSerializer$, useSerializerQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
14613
+ export { $, Each, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, createQRL as _createQRL, _deserialize, _dumpState, eachCmp as _eaC, eachCmpTask as _eaT, _executeSsrChores, _fnSignal, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, _getVarProps, _hasStoreEffects, _hmr, isJSXNode as _isJSXNode, isStore as _isStore, isStringifiable as _isStringifiable, isTask as _isTask, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, mapApp_findIndx as _mapApp_findIndx, mapArray_get as _mapArray_get, mapArray_set as _mapArray_set, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, qrlToString as _qrlToString, _regSymbol, _removeProjection, _res, _resolveContextWithoutSequentialScope, _restProps, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, scheduleTask as _task, _updateProjectionProps, _useHmr, _val, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getFirstChild as _vnode_getFirstChild, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _wrapProp, _wrapSignal, component$, componentQrl, createAsync$, createAsyncSignal as createAsyncQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, forceStoreEffects, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsync$, useAsyncQrl, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useSerializer$, useSerializerQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
14144
14614
  //# sourceMappingURL=core.mjs.map