@qwik.dev/core 2.0.0-beta.24 → 2.0.0-beta.26

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.24-dev+314726b
3
+ * @qwik.dev/core 2.0.0-beta.26-dev+c693cf5
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
@@ -1007,7 +1007,7 @@ const COMMA = ',';
1007
1007
  *
1008
1008
  * @public
1009
1009
  */
1010
- const version = "2.0.0-beta.24-dev+314726b";
1010
+ const version = "2.0.0-beta.26-dev+c693cf5";
1011
1011
 
1012
1012
  // keep this import from core/build so the cjs build works
1013
1013
  const createPlatform = () => {
@@ -1020,6 +1020,8 @@ const createPlatform = () => {
1020
1020
  if (regSym) {
1021
1021
  return regSym;
1022
1022
  }
1023
+ // we never lazy import on the server
1024
+ throw qError(6 /* QError.dynamicImportFailed */, [symbolName]);
1023
1025
  }
1024
1026
  if (!url) {
1025
1027
  throw qError(14 /* QError.qrlMissingChunk */, [symbolName]);
@@ -3068,6 +3070,14 @@ const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
3068
3070
  }
3069
3071
  delete constProps[k];
3070
3072
  }
3073
+ else if (k === BIND_CHECKED) {
3074
+ // Set flag, will process after walk
3075
+ bindCheckedSignal = constProps[k];
3076
+ }
3077
+ else if (k === BIND_VALUE) {
3078
+ // Set flag, will process after walk
3079
+ bindValueSignal = constProps[k];
3080
+ }
3071
3081
  processedKeys.add(k);
3072
3082
  }
3073
3083
  }
@@ -3097,44 +3107,65 @@ const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
3097
3107
  }
3098
3108
  processedKeys.add(k);
3099
3109
  }
3100
- // Handle bind:* - only in varProps, bind:* should be moved to varProps
3101
- if (bindCheckedSignal || bindValueSignal) {
3102
- if (!varPropsCopied) {
3103
- varProps = { ...varProps };
3104
- varPropsCopied = true;
3105
- }
3106
- if (bindCheckedSignal) {
3110
+ }
3111
+ // Handle bind:* - only in varProps, bind:* should be moved to varProps
3112
+ if (bindCheckedSignal || bindValueSignal) {
3113
+ if (!varPropsCopied) {
3114
+ varProps = { ...varProps };
3115
+ varPropsCopied = true;
3116
+ }
3117
+ varProps ||= {};
3118
+ if (bindCheckedSignal) {
3119
+ // Delete from both varProps and constProps if present
3120
+ if (varProps && _hasOwnProperty$1.call(varProps, BIND_CHECKED)) {
3107
3121
  delete varProps[BIND_CHECKED];
3108
- varProps.checked = bindCheckedSignal;
3109
- const handler = createQRL(null, '_chk', _chk, null, [bindCheckedSignal]);
3110
- // Move q-e:input from constProps if it exists
3111
- if (constProps && _hasOwnProperty$1.call(constProps, 'q-e:input')) {
3112
- if (!constPropsCopied) {
3113
- constProps = { ...constProps };
3114
- constPropsCopied = true;
3115
- }
3116
- const existingHandler = constProps['q-e:input'];
3117
- delete constProps['q-e:input'];
3118
- toSort = mergeHandlers(varProps, 'q-e:input', existingHandler) || toSort;
3122
+ }
3123
+ if (constProps && _hasOwnProperty$1.call(constProps, BIND_CHECKED)) {
3124
+ if (!constPropsCopied) {
3125
+ constProps = { ...constProps };
3126
+ constPropsCopied = true;
3127
+ }
3128
+ delete constProps[BIND_CHECKED];
3129
+ }
3130
+ varProps.checked = bindCheckedSignal;
3131
+ const handler = createQRL(null, '_chk', _chk, null, [bindCheckedSignal]);
3132
+ // Move q-e:input from constProps if it exists
3133
+ if (constProps && _hasOwnProperty$1.call(constProps, 'q-e:input')) {
3134
+ if (!constPropsCopied) {
3135
+ constProps = { ...constProps };
3136
+ constPropsCopied = true;
3119
3137
  }
3120
- toSort = mergeHandlers(varProps, 'q-e:input', handler) || toSort;
3138
+ const existingHandler = constProps['q-e:input'];
3139
+ delete constProps['q-e:input'];
3140
+ toSort = mergeHandlers(varProps, 'q-e:input', existingHandler) || toSort;
3121
3141
  }
3122
- else if (bindValueSignal) {
3142
+ toSort = mergeHandlers(varProps, 'q-e:input', handler) || toSort;
3143
+ }
3144
+ else if (bindValueSignal) {
3145
+ // Delete from both varProps and constProps if present
3146
+ if (varProps && _hasOwnProperty$1.call(varProps, BIND_VALUE)) {
3123
3147
  delete varProps[BIND_VALUE];
3124
- varProps.value = bindValueSignal;
3125
- const handler = createQRL(null, '_val', _val, null, [bindValueSignal]);
3126
- // Move q-e:input from constProps if it exists
3127
- if (constProps && _hasOwnProperty$1.call(constProps, 'q-e:input')) {
3128
- if (!constPropsCopied) {
3129
- constProps = { ...constProps };
3130
- constPropsCopied = true;
3131
- }
3132
- const existingHandler = constProps['q-e:input'];
3133
- delete constProps['q-e:input'];
3134
- toSort = mergeHandlers(varProps, 'q-e:input', existingHandler) || toSort;
3148
+ }
3149
+ if (constProps && _hasOwnProperty$1.call(constProps, BIND_VALUE)) {
3150
+ if (!constPropsCopied) {
3151
+ constProps = { ...constProps };
3152
+ constPropsCopied = true;
3135
3153
  }
3136
- toSort = mergeHandlers(varProps, 'q-e:input', handler) || toSort;
3154
+ delete constProps[BIND_VALUE];
3137
3155
  }
3156
+ varProps.value = bindValueSignal;
3157
+ const handler = createQRL(null, '_val', _val, null, [bindValueSignal]);
3158
+ // Move q-e:input from constProps if it exists
3159
+ if (constProps && _hasOwnProperty$1.call(constProps, 'q-e:input')) {
3160
+ if (!constPropsCopied) {
3161
+ constProps = { ...constProps };
3162
+ constPropsCopied = true;
3163
+ }
3164
+ const existingHandler = constProps['q-e:input'];
3165
+ delete constProps['q-e:input'];
3166
+ toSort = mergeHandlers(varProps, 'q-e:input', existingHandler) || toSort;
3167
+ }
3168
+ toSort = mergeHandlers(varProps, 'q-e:input', handler) || toSort;
3138
3169
  }
3139
3170
  }
3140
3171
  // Transform className -> class
@@ -3676,7 +3707,14 @@ const cleanupDestroyable = (destroyable) => {
3676
3707
  * This safely calls an event handler, handling errors and retrying on thrown Promises, and
3677
3708
  * providing extra parameters defined on the elements as arguments (used for loop optimization)
3678
3709
  */
3679
- function runEventHandlerQRL(handler, event, element, ctx = newInvokeContextFromDOM(event, element)) {
3710
+ function runEventHandlerQRL(handler, event, element, ctx) {
3711
+ if (!element.isConnected) {
3712
+ // ignore events on disconnected elements, this can happen when the event is triggered while the element is being removed
3713
+ return;
3714
+ }
3715
+ if (!ctx) {
3716
+ ctx = newInvokeContextFromDOM(event, element);
3717
+ }
3680
3718
  const container = ctx.$container$;
3681
3719
  const hostElement = ctx.$hostElement$;
3682
3720
  vnode_ensureElementInflated(container, hostElement);
@@ -3712,6 +3750,10 @@ function runEventHandlerQRL(handler, event, element, ctx = newInvokeContextFromD
3712
3750
  * @internal
3713
3751
  */
3714
3752
  function _run(event, element) {
3753
+ if (!element.isConnected) {
3754
+ // ignore events on disconnected elements, this can happen when the event is triggered while the element is being removed
3755
+ return;
3756
+ }
3715
3757
  const ctx = newInvokeContextFromDOM(event, element);
3716
3758
  if (typeof this === 'string') {
3717
3759
  setCaptures(deserializeCaptures(ctx.$container$, this));
@@ -3737,8 +3779,8 @@ function setAttribute(journal, vnode, key, value, scopedStyleIdPrefix, originalV
3737
3779
  vnode_setProp(vnode, key, originalValue);
3738
3780
  addVNodeOperation(journal, createSetAttributeOperation(vnode.node, key, value, scopedStyleIdPrefix, (vnode.flags & 512 /* VNodeFlags.NS_svg */) !== 0));
3739
3781
  }
3740
- const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyleIdPrefix) => {
3741
- const diffContext = {
3782
+ function createDiffContext(container, journal, cursor, scopedStyleIdPrefix) {
3783
+ return {
3742
3784
  container,
3743
3785
  journal,
3744
3786
  cursor,
@@ -3769,6 +3811,9 @@ const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyle
3769
3811
  }),
3770
3812
  },
3771
3813
  };
3814
+ }
3815
+ const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyleIdPrefix) => {
3816
+ const diffContext = createDiffContext(container, journal, cursor, scopedStyleIdPrefix);
3772
3817
  ////////////////////////////////
3773
3818
  diff(diffContext, jsxNode, vStartNode);
3774
3819
  const result = drainAsyncQueue(diffContext);
@@ -4123,6 +4168,7 @@ function expectSlot(diffContext) {
4123
4168
  vHost && vnode_setProp(vHost, slotNameKey, diffContext.vNewNode);
4124
4169
  isDev &&
4125
4170
  vnode_setProp(diffContext.vNewNode, DEBUG_TYPE, "P" /* VirtualType.Projection */);
4171
+ vnode_inflateProjectionTrailingText(diffContext.journal, diffContext.vNewNode);
4126
4172
  vnode_insertBefore(diffContext.journal, diffContext.vParent, diffContext.vNewNode, diffContext.vCurrent && getInsertBefore(diffContext));
4127
4173
  // If we moved from a q:template and it's now empty, remove it
4128
4174
  if (oldParent &&
@@ -4673,26 +4719,34 @@ function expectComponent(diffContext, component) {
4673
4719
  const vNodeLookupKey = getKey(host) || vNodeComponentHash;
4674
4720
  const lookupKeysAreEqual = lookupKey === vNodeLookupKey;
4675
4721
  const hashesAreEqual = componentHash === vNodeComponentHash;
4676
- if (!lookupKeysAreEqual) {
4677
- if (moveOrCreateKeyedNode(diffContext, null, lookupKey, lookupKey, diffContext.vParent)) {
4722
+ if (lookupKeysAreEqual) {
4723
+ if (hashesAreEqual) {
4724
+ deleteFromSideBuffer(diffContext, null, lookupKey);
4725
+ }
4726
+ else {
4678
4727
  insertNewComponent(diffContext, host, componentQRL, jsxProps);
4728
+ host = diffContext.vNewNode;
4679
4729
  shouldRender = true;
4680
4730
  }
4681
- host = (diffContext.vNewNode || diffContext.vCurrent);
4682
- }
4683
- else if (!hashesAreEqual || !jsxNode.key) {
4684
- insertNewComponent(diffContext, host, componentQRL, jsxProps);
4685
- host = diffContext.vNewNode;
4686
- shouldRender = true;
4687
4731
  }
4688
4732
  else {
4689
- // delete the key from the side buffer if it is the same component
4690
- deleteFromSideBuffer(diffContext, null, lookupKey);
4733
+ if (moveOrCreateKeyedNode(diffContext, null, lookupKey, lookupKey, diffContext.vParent)) {
4734
+ insertNewComponent(diffContext, host, componentQRL, jsxProps);
4735
+ shouldRender = true;
4736
+ }
4737
+ host = (diffContext.vNewNode || diffContext.vCurrent);
4691
4738
  }
4692
4739
  if (host) {
4693
4740
  const vNodeProps = vnode_getProp(host, ELEMENT_PROPS, diffContext.container.$getObjectById$);
4694
4741
  if (!shouldRender) {
4695
- shouldRender ||= handleProps(host, jsxProps, vNodeProps, diffContext.container);
4742
+ const propsChanged = handleProps(host, jsxProps, vNodeProps, diffContext.container);
4743
+ // if props changed but key is null we need to insert a new component, because we need to execute hooks etc
4744
+ if (propsChanged && jsxNode.key == null) {
4745
+ insertNewComponent(diffContext, host, componentQRL, jsxProps);
4746
+ host = diffContext.vNewNode;
4747
+ shouldRender = true;
4748
+ }
4749
+ shouldRender ||= propsChanged;
4696
4750
  }
4697
4751
  if (shouldRender) {
4698
4752
  // Assign the new QRL instance to the host.
@@ -4714,7 +4768,7 @@ function expectComponent(diffContext, component) {
4714
4768
  const lookupKeysAreEqual = lookupKey === vNodeLookupKey;
4715
4769
  const vNodeComponentHash = getComponentHash(host, diffContext.container.$getObjectById$);
4716
4770
  const isInlineComponent = vNodeComponentHash == null;
4717
- if ((host && !isInlineComponent) || lookupKey == null) {
4771
+ if ((host && !isInlineComponent) || !host) {
4718
4772
  insertNewInlineComponent(diffContext);
4719
4773
  host = diffContext.vNewNode;
4720
4774
  }
@@ -5706,7 +5760,7 @@ function walkCursor(cursor, options) {
5706
5760
  return;
5707
5761
  }
5708
5762
  // Check time budget (only for DOM, not SSR)
5709
- if (!isRunningOnServer && !import.meta.env.TEST) {
5763
+ if (isBrowser) {
5710
5764
  const elapsed = performance.now() - startTime;
5711
5765
  if (elapsed >= timeBudget) {
5712
5766
  // Schedule continuation as macrotask to actually yield to browser
@@ -5748,10 +5802,27 @@ function tryDescendDirtyChildren(container, cursorData, currentVNode, cursor) {
5748
5802
  return null;
5749
5803
  }
5750
5804
  partitionDirtyChildren(dirtyChildren, currentVNode);
5805
+ // Scan dirtyChildren directly instead of going through getNextVNode.
5806
+ // getNextVNode follows the child's parent/slotParent pointer, which for Projection nodes
5807
+ // points to the DOM insertion location rather than currentVNode — that would scan the
5808
+ // wrong dirtyChildren array and potentially cause infinite loops.
5809
+ // const len = dirtyChildren.length;
5810
+ // for (let i = 0; i < len; i++) {
5811
+ // const child = dirtyChildren[i];
5812
+ // if (child.dirty & ChoreBits.DIRTY_MASK) {
5813
+ // currentVNode.nextDirtyChildIndex = (i + 1) % len;
5814
+ // setCursorPosition(container, cursorData, child);
5815
+ // return child;
5816
+ // }
5817
+ // }
5818
+ // // No dirty child found — clean up
5819
+ // currentVNode.dirty &= ~ChoreBits.CHILDREN;
5820
+ // currentVNode.dirtyChildren = null;
5751
5821
  currentVNode.nextDirtyChildIndex = 0;
5752
5822
  const next = getNextVNode(dirtyChildren[0], cursor);
5753
5823
  setCursorPosition(container, cursorData, next);
5754
5824
  return next;
5825
+ // return null;
5755
5826
  }
5756
5827
  /**
5757
5828
  * Partitions dirtyChildren array so non-projections come first, projections last. Uses in-place
@@ -5780,14 +5851,14 @@ function getNextVNode(vNode, cursor) {
5780
5851
  }
5781
5852
  return null;
5782
5853
  }
5783
- // Prefer parent if it's dirty, otherwise try slotParent
5854
+ // Prefer slotParent (logical owner) for Projections, fall back to parent
5784
5855
  let parent = null;
5785
- if (vNode.parent && vNode.parent.dirty & 32 /* ChoreBits.CHILDREN */) {
5786
- parent = vNode.parent;
5787
- }
5788
- else if (vNode.slotParent && vNode.slotParent.dirty & 32 /* ChoreBits.CHILDREN */) {
5856
+ if (vNode.slotParent && vNode.slotParent.dirty & 32 /* ChoreBits.CHILDREN */) {
5789
5857
  parent = vNode.slotParent;
5790
5858
  }
5859
+ else if (vNode.parent && vNode.parent.dirty & 32 /* ChoreBits.CHILDREN */) {
5860
+ parent = vNode.parent;
5861
+ }
5791
5862
  if (!parent) {
5792
5863
  if (cursor.dirty & 127 /* ChoreBits.DIRTY_MASK */) {
5793
5864
  return cursor;
@@ -5959,7 +6030,9 @@ function propagatePath(target) {
5959
6030
  const parent = reusablePath[i + 1] || target;
5960
6031
  parent.dirty |= 32 /* ChoreBits.CHILDREN */;
5961
6032
  parent.dirtyChildren ||= [];
5962
- parent.dirtyChildren.push(child);
6033
+ if (!parent.dirtyChildren.includes(child)) {
6034
+ parent.dirtyChildren.push(child);
6035
+ }
5963
6036
  }
5964
6037
  }
5965
6038
  /**
@@ -5968,7 +6041,7 @@ function propagatePath(target) {
5968
6041
  */
5969
6042
  function propagateToCursorRoot(vNode, cursorRoot) {
5970
6043
  reusablePath.push(vNode);
5971
- let current = vNode.parent || vNode.slotParent;
6044
+ let current = vNode.slotParent || vNode.parent;
5972
6045
  while (current) {
5973
6046
  const isDirty = current.dirty & 127 /* ChoreBits.DIRTY_MASK */;
5974
6047
  const currentIsCursor = isCursor(current);
@@ -5993,7 +6066,7 @@ function propagateToCursorRoot(vNode, cursorRoot) {
5993
6066
  }
5994
6067
  }
5995
6068
  reusablePath.push(current);
5996
- current = current.parent || current.slotParent;
6069
+ current = current.slotParent || current.parent;
5997
6070
  }
5998
6071
  reusablePath.length = 0;
5999
6072
  throwErrorAndStop('Cursor root not found in current path!');
@@ -6004,7 +6077,7 @@ function propagateToCursorRoot(vNode, cursorRoot) {
6004
6077
  */
6005
6078
  function findAndPropagateToBlockingCursor(vNode) {
6006
6079
  reusablePath.push(vNode);
6007
- let current = vNode.parent || vNode.slotParent;
6080
+ let current = vNode.slotParent || vNode.parent;
6008
6081
  while (current) {
6009
6082
  const currentIsCursor = isCursor(current);
6010
6083
  if (currentIsCursor) {
@@ -6013,7 +6086,7 @@ function findAndPropagateToBlockingCursor(vNode) {
6013
6086
  return true;
6014
6087
  }
6015
6088
  reusablePath.push(current);
6016
- current = current.parent || current.slotParent;
6089
+ current = current.slotParent || current.parent;
6017
6090
  }
6018
6091
  reusablePath.length = 0;
6019
6092
  return false;
@@ -6047,7 +6120,7 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
6047
6120
  if ((isRealDirty ? prevDirty & 127 /* ChoreBits.DIRTY_MASK */ : prevDirty) || vNode === cursorRoot) {
6048
6121
  return;
6049
6122
  }
6050
- const parent = vNode.parent || vNode.slotParent;
6123
+ const parent = vNode.slotParent || vNode.parent;
6051
6124
  // If cursorRoot is provided, propagate up to it
6052
6125
  if (cursorRoot && isRealDirty && parent && !parent.dirty) {
6053
6126
  propagateToCursorRoot(vNode, cursorRoot);
@@ -6059,7 +6132,9 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
6059
6132
  parent.dirty |= 32 /* ChoreBits.CHILDREN */;
6060
6133
  }
6061
6134
  parent.dirtyChildren ||= [];
6062
- parent.dirtyChildren.push(vNode);
6135
+ if (!parent.dirtyChildren.includes(vNode)) {
6136
+ parent.dirtyChildren.push(vNode);
6137
+ }
6063
6138
  if (isRealDirty && vNode.dirtyChildren) {
6064
6139
  // this node is maybe an ancestor of the current cursor position
6065
6140
  // if so we must restart from here
@@ -6070,7 +6145,7 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
6070
6145
  if (cursorPosition) {
6071
6146
  // find the ancestor of the cursor position that is current vNode
6072
6147
  while (cursorPosition !== cursor) {
6073
- cursorPosition = cursorPosition.parent || cursorPosition.slotParent;
6148
+ cursorPosition = cursorPosition.slotParent || cursorPosition.parent;
6074
6149
  if (cursorPosition === vNode) {
6075
6150
  // set cursor position to this node
6076
6151
  cursorData.position = vNode;
@@ -6454,7 +6529,7 @@ function vnode_walkVNode(vNode, callback) {
6454
6529
  let vCursor = vNode;
6455
6530
  // Depth first traversal
6456
6531
  if (vnode_isTextVNode(vNode)) {
6457
- // Text nodes don't have subscriptions or children;
6532
+ callback?.(vNode, null);
6458
6533
  return;
6459
6534
  }
6460
6535
  let vParent = null;
@@ -6602,11 +6677,6 @@ const vnode_getDomSibling = (vNode, nextDirection, descend) => {
6602
6677
  }
6603
6678
  return null;
6604
6679
  };
6605
- const vnode_ensureInflatedIfText = (journal, vNode) => {
6606
- if (vnode_isTextVNode(vNode)) {
6607
- vnode_ensureTextInflated(journal, vNode);
6608
- }
6609
- };
6610
6680
  const vnode_ensureTextInflated = (journal, vnode) => {
6611
6681
  const textVNode = ensureTextVNode(vnode);
6612
6682
  const flags = textVNode.flags;
@@ -6932,7 +7002,9 @@ const vnode_findInsertBefore = (journal, parent, insertBefore) => {
6932
7002
  else {
6933
7003
  adjustedInsertBefore = insertBefore;
6934
7004
  }
6935
- adjustedInsertBefore && vnode_ensureInflatedIfText(journal, adjustedInsertBefore);
7005
+ adjustedInsertBefore &&
7006
+ vnode_isTextVNode(adjustedInsertBefore) &&
7007
+ vnode_ensureTextInflated(journal, adjustedInsertBefore);
6936
7008
  return adjustedInsertBefore;
6937
7009
  };
6938
7010
  const vnode_connectSiblings = (parent, vNode, vNext) => {
@@ -6996,6 +7068,45 @@ const vnode_unlinkFromOldParent = (journal, currentParent, newParent, newChild)
6996
7068
  vnode_remove(journal, currentParent, newChild, false);
6997
7069
  }
6998
7070
  };
7071
+ /**
7072
+ * When a projection vnode is about to be repositioned (moved in the vnode tree), its trailing text
7073
+ * node must be inflated before the projection is unlinked from its current sibling chain.
7074
+ * `vnode_ensureTextInflated` relies on `vnode_getDomSibling` to locate adjacent text nodes and
7075
+ * decide which one is "last" (i.e. the one that gets to reuse the shared SSR DOM `Text` node). Once
7076
+ * the projection is unlinked, its `nextSibling` becomes `null`, so `getDomSibling` can no longer
7077
+ * cross the boundary to find a trailing sibling such as an empty-string text node — causing
7078
+ * `isLastNode` to be `true` prematurely and corrupting the shared DOM text node. Inflating the
7079
+ * trailing text node while the siblings are still connected gives it its own fresh DOM node and
7080
+ * avoids the corruption.
7081
+ *
7082
+ * Example:
7083
+ *
7084
+ * ```
7085
+ * <Component>
7086
+ * <button>
7087
+ * <InlineComponent>
7088
+ * <span>
7089
+ * "*"
7090
+ * </span>
7091
+ * </InlineComponent>
7092
+ * <Projection> // <-- this projection when unlinked from the siblings will cause the "test" text node to be considered the last node without inflating it
7093
+ * "test" // <-- this text node is sharing the same DOM node with the ""
7094
+ * </Projection>
7095
+ * "" <-- this text node is sharing the same DOM node with the "test"
7096
+ * </button>
7097
+ * </Component>
7098
+ * ```
7099
+ */
7100
+ const vnode_inflateProjectionTrailingText = (journal, projection) => {
7101
+ // Follow lastChild through any inner virtual wrappers to reach the actual trailing text node.
7102
+ let last = projection;
7103
+ while (last && vnode_isVirtualVNode(last)) {
7104
+ last = last.lastChild;
7105
+ }
7106
+ if (last && vnode_isTextVNode(last) && (last.flags & 8 /* VNodeFlags.Inflated */) === 0) {
7107
+ vnode_ensureTextInflated(journal, last);
7108
+ }
7109
+ };
6999
7110
  const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
7000
7111
  if (vnode_isElementOrTextVNode(newChild)) {
7001
7112
  vnode_insertElementBefore(journal, parent, newChild, insertBefore);
@@ -7619,6 +7730,15 @@ function materializeFromVNodeData(vParent, vData, element, child) {
7619
7730
  vnode_ensureElementKeyInflated(elementVNode);
7620
7731
  addVNode(elementVNode);
7621
7732
  child = fastNextSibling(child);
7733
+ while (
7734
+ // skip only elements, not text nodes
7735
+ isElement(child) &&
7736
+ shouldSkipElement(child)) {
7737
+ child = fastNextSibling(child);
7738
+ if (!child && value > 0) {
7739
+ throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), nextToConsumeIdx]);
7740
+ }
7741
+ }
7622
7742
  }
7623
7743
  // collect the elements;
7624
7744
  }
@@ -9010,12 +9130,7 @@ class Serializer {
9010
9130
  ]);
9011
9131
  }
9012
9132
  else if (value instanceof EffectSubscription) {
9013
- this.output(32 /* TypeIds.EffectSubscription */, [
9014
- value.consumer,
9015
- value.property,
9016
- value.backRef,
9017
- value.data,
9018
- ]);
9133
+ this.output(32 /* TypeIds.EffectSubscription */, [value.consumer, value.property, value.data]);
9019
9134
  }
9020
9135
  else if (isStore(value)) {
9021
9136
  const storeHandler = getStoreHandler(value);
@@ -9083,19 +9198,13 @@ class Serializer {
9083
9198
  this.output(2 /* TypeIds.ForwardRef */, forwardRefId);
9084
9199
  }
9085
9200
  else {
9086
- this.output(26 /* TypeIds.SerializerSignal */, [
9087
- value.$computeQrl$,
9088
- filterEffectBackRefs(value[_EFFECT_BACK_REF]),
9089
- value.$effects$,
9090
- maybeValue,
9091
- ]);
9201
+ this.output(26 /* TypeIds.SerializerSignal */, [value.$computeQrl$, value.$effects$, maybeValue]);
9092
9202
  }
9093
9203
  return;
9094
9204
  }
9095
9205
  if (value instanceof WrappedSignalImpl) {
9096
9206
  this.output(23 /* TypeIds.WrappedSignal */, [
9097
9207
  ...serializeWrappingFn(this.$serializationContext$, value),
9098
- filterEffectBackRefs(value[_EFFECT_BACK_REF]),
9099
9208
  value.$flags$,
9100
9209
  value.$hostElement$,
9101
9210
  ...(value.$effects$ || []),
@@ -9123,11 +9232,7 @@ class Serializer {
9123
9232
  else if (shouldNeverSerialize) {
9124
9233
  v = NEEDS_COMPUTATION;
9125
9234
  }
9126
- const out = [
9127
- value.$computeQrl$,
9128
- filterEffectBackRefs(value[_EFFECT_BACK_REF]),
9129
- value.$effects$,
9130
- ];
9235
+ const out = [value.$computeQrl$, value.$effects$];
9131
9236
  if (isAsync) {
9132
9237
  // After SSR, the signal is never loading, so no need to send it
9133
9238
  out.push(value.$loadingEffects$, value.$errorEffects$, value.$untrackedError$);
@@ -9254,14 +9359,7 @@ class Serializer {
9254
9359
  this.output(29 /* TypeIds.JSXNode */, out);
9255
9360
  }
9256
9361
  else if (value instanceof Task) {
9257
- const out = [
9258
- value.$qrl$,
9259
- value.$flags$,
9260
- value.$index$,
9261
- value.$el$,
9262
- value[_EFFECT_BACK_REF],
9263
- value.$state$,
9264
- ];
9362
+ const out = [value.$qrl$, value.$flags$, value.$index$, value.$el$, value.$state$];
9265
9363
  while (out[out.length - 1] === undefined) {
9266
9364
  out.pop();
9267
9365
  }
@@ -9460,18 +9558,6 @@ function serializeWrappingFn(serializationContext, value) {
9460
9558
  const syncFnId = serializationContext.$addSyncFn$(value.$funcStr$, value.$args$.length, value.$func$);
9461
9559
  return [syncFnId, value.$args$];
9462
9560
  }
9463
- function filterEffectBackRefs(effectBackRef) {
9464
- let effectBackRefToSerialize = undefined;
9465
- if (effectBackRef) {
9466
- for (const [effectProp, effect] of effectBackRef) {
9467
- if (effect.backRef) {
9468
- effectBackRefToSerialize ||= new Map();
9469
- effectBackRefToSerialize.set(effectProp, effect);
9470
- }
9471
- }
9472
- }
9473
- return effectBackRefToSerialize;
9474
- }
9475
9561
  function tryGetBackRefs(props) {
9476
9562
  return Object.prototype.hasOwnProperty.call(props, QBackRefs)
9477
9563
  ? props[QBackRefs]
@@ -10195,8 +10281,7 @@ const inflate = (container, target, typeId, data) => {
10195
10281
  task.$flags$ = v[1];
10196
10282
  task.$index$ = v[2];
10197
10283
  task.$el$ = v[3];
10198
- task[_EFFECT_BACK_REF] = v[4];
10199
- task.$state$ = v[5];
10284
+ task.$state$ = v[4];
10200
10285
  break;
10201
10286
  case 21 /* TypeIds.Component */:
10202
10287
  target[SERIALIZABLE_STATE][0] = data[0];
@@ -10217,6 +10302,7 @@ const inflate = (container, target, typeId, data) => {
10217
10302
  const storeHandler = getStoreHandler(target);
10218
10303
  storeHandler.$flags$ = flags;
10219
10304
  storeHandler.$effects$ = effects;
10305
+ restoreEffectBackRefForEffectsMap(storeHandler.$effects$, store);
10220
10306
  break;
10221
10307
  }
10222
10308
  case 22 /* TypeIds.Signal */: {
@@ -10224,6 +10310,7 @@ const inflate = (container, target, typeId, data) => {
10224
10310
  const d = data;
10225
10311
  signal.$untrackedValue$ = d[0];
10226
10312
  signal.$effects$ = new Set(d.slice(1));
10313
+ restoreEffectBackRefForEffects(signal.$effects$, signal);
10227
10314
  break;
10228
10315
  }
10229
10316
  case 23 /* TypeIds.WrappedSignal */: {
@@ -10231,41 +10318,43 @@ const inflate = (container, target, typeId, data) => {
10231
10318
  const d = data;
10232
10319
  signal.$func$ = container.getSyncFn(d[0]);
10233
10320
  signal.$args$ = d[1];
10234
- signal[_EFFECT_BACK_REF] = d[2];
10235
10321
  signal.$untrackedValue$ = NEEDS_COMPUTATION;
10236
- signal.$flags$ = d[3];
10322
+ signal.$flags$ = d[2];
10237
10323
  signal.$flags$ |= 1 /* SignalFlags.INVALID */;
10238
- signal.$hostElement$ = d[4];
10239
- signal.$effects$ = new Set(d.slice(5));
10324
+ signal.$hostElement$ = d[3];
10325
+ signal.$effects$ = new Set(d.slice(4));
10240
10326
  inflateWrappedSignalValue(signal);
10327
+ restoreEffectBackRefForEffects(signal.$effects$, signal);
10241
10328
  break;
10242
10329
  }
10243
10330
  case 25 /* TypeIds.AsyncSignal */: {
10244
10331
  const asyncSignal = target;
10245
10332
  const d = data;
10246
10333
  asyncSignal.$computeQrl$ = d[0];
10247
- asyncSignal[_EFFECT_BACK_REF] = d[1];
10248
- asyncSignal.$effects$ = new Set(d[2]);
10249
- asyncSignal.$loadingEffects$ = new Set(d[3]);
10250
- asyncSignal.$errorEffects$ = new Set(d[4]);
10251
- asyncSignal.$untrackedError$ = d[5];
10252
- asyncSignal.$flags$ = d[6] ?? 0;
10334
+ asyncSignal.$effects$ = new Set(d[1]);
10335
+ asyncSignal.$loadingEffects$ = new Set(d[2]);
10336
+ asyncSignal.$errorEffects$ = new Set(d[3]);
10337
+ asyncSignal.$untrackedError$ = d[4];
10338
+ asyncSignal.$flags$ = d[5] ?? 0;
10253
10339
  if (asyncSignal.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
10254
10340
  // If it's client only, it was serialized because it pretended to be loading
10255
10341
  asyncSignal.$untrackedLoading$ = true;
10256
10342
  }
10257
- const hasValue = d.length > 7;
10343
+ const hasValue = d.length > 6;
10258
10344
  if (hasValue) {
10259
- asyncSignal.$untrackedValue$ = d[7];
10345
+ asyncSignal.$untrackedValue$ = d[6];
10260
10346
  }
10261
10347
  // can happen when never serialize etc
10262
10348
  if (asyncSignal.$untrackedValue$ === NEEDS_COMPUTATION) {
10263
10349
  asyncSignal.$flags$ |= 1 /* SignalFlags.INVALID */;
10264
10350
  }
10265
10351
  // Note, we use the setter so that it schedules polling if needed
10266
- asyncSignal.interval = (d[8] ?? 0);
10267
- asyncSignal.$concurrency$ = (d[9] ?? 1);
10268
- asyncSignal.$timeoutMs$ = (d[10] ?? 0);
10352
+ asyncSignal.interval = (d[7] ?? 0);
10353
+ asyncSignal.$concurrency$ = (d[8] ?? 1);
10354
+ asyncSignal.$timeoutMs$ = (d[9] ?? 0);
10355
+ restoreEffectBackRefForEffects(asyncSignal.$effects$, asyncSignal);
10356
+ restoreEffectBackRefForEffects(asyncSignal.$loadingEffects$, asyncSignal);
10357
+ restoreEffectBackRefForEffects(asyncSignal.$errorEffects$, asyncSignal);
10269
10358
  break;
10270
10359
  }
10271
10360
  // Inflating a SerializerSignal is the same as inflating a ComputedSignal
@@ -10283,19 +10372,19 @@ const inflate = (container, target, typeId, data) => {
10283
10372
  // ignore preload errors
10284
10373
  });
10285
10374
  loading = loading.finally(() => p);
10286
- computed[_EFFECT_BACK_REF] = d[1];
10287
- if (d[2]) {
10288
- computed.$effects$ = new Set(d[2]);
10375
+ if (d[1]) {
10376
+ computed.$effects$ = new Set(d[1]);
10289
10377
  }
10290
- const hasValue = d.length > 3;
10378
+ const hasValue = d.length > 2;
10291
10379
  if (hasValue) {
10292
- computed.$untrackedValue$ = d[3];
10380
+ computed.$untrackedValue$ = d[2];
10293
10381
  }
10294
10382
  if (typeId !== 26 /* TypeIds.SerializerSignal */ && computed.$untrackedValue$ !== NEEDS_COMPUTATION) {
10295
10383
  // If we have a value after SSR, it will always be mean the signal was not invalid
10296
10384
  // The serialized signal is always left invalid so it can recreate the custom object
10297
10385
  computed.$flags$ &= -2 /* SignalFlags.INVALID */;
10298
10386
  }
10387
+ restoreEffectBackRefForEffects(computed.$effects$, computed);
10299
10388
  break;
10300
10389
  }
10301
10390
  case 15 /* TypeIds.Error */: {
@@ -10370,7 +10459,9 @@ const inflate = (container, target, typeId, data) => {
10370
10459
  owner._proxy = propsProxy;
10371
10460
  }
10372
10461
  propsProxy[_OWNER] = owner;
10373
- propsProxy[_PROPS_HANDLER].$effects$ = d[3];
10462
+ const propsHandler = propsProxy[_PROPS_HANDLER];
10463
+ propsHandler.$effects$ = d[3];
10464
+ restoreEffectBackRefForEffectsMap(propsHandler.$effects$, propsProxy);
10374
10465
  break;
10375
10466
  case 31 /* TypeIds.SubscriptionData */: {
10376
10467
  const effectData = target;
@@ -10383,14 +10474,15 @@ const inflate = (container, target, typeId, data) => {
10383
10474
  const d = data;
10384
10475
  effectSub.consumer = d[0];
10385
10476
  effectSub.property = d[1];
10386
- effectSub.backRef = d[2];
10387
- effectSub.data = d[3];
10477
+ effectSub.data = d[2];
10478
+ restoreEffectBackRefForConsumer(effectSub);
10388
10479
  break;
10389
10480
  }
10390
10481
  default:
10391
10482
  throw qError(16 /* QError.serializeErrorNotImplemented */, [typeId]);
10392
10483
  }
10393
- }; /**
10484
+ };
10485
+ /**
10394
10486
  * Restores an array eagerly. If you need it lazily, use `deserializeData(container, TypeIds.Array,
10395
10487
  * array)` instead
10396
10488
  */
@@ -10441,6 +10533,31 @@ function inflateWrappedSignalValue(signal) {
10441
10533
  }
10442
10534
  }
10443
10535
  }
10536
+ function restoreEffectBackRefForConsumer(effect) {
10537
+ const isServerSide = import.meta.env.TEST ? isServerPlatform() : isServer;
10538
+ const consumerBackRef = effect.consumer;
10539
+ if (isServerSide && !consumerBackRef) {
10540
+ // on browser, we don't serialize for example VNodes, so then on server side we don't have consumer
10541
+ return;
10542
+ }
10543
+ consumerBackRef[_EFFECT_BACK_REF] ||= new Map();
10544
+ consumerBackRef[_EFFECT_BACK_REF].set(effect.property, effect);
10545
+ }
10546
+ function restoreEffectBackRefForEffects(effects, consumer) {
10547
+ if (effects) {
10548
+ for (const effect of effects) {
10549
+ effect.backRef ||= new Set();
10550
+ effect.backRef.add(consumer);
10551
+ }
10552
+ }
10553
+ }
10554
+ function restoreEffectBackRefForEffectsMap(effectsMap, consumer) {
10555
+ if (effectsMap) {
10556
+ for (const [, effects] of effectsMap) {
10557
+ restoreEffectBackRefForEffects(effects, consumer);
10558
+ }
10559
+ }
10560
+ }
10444
10561
 
10445
10562
  /** Arrays/Objects are special-cased so their identifiers is a single digit. */
10446
10563
  const needsInflation = (typeId) => typeId >= 15 /* TypeIds.Error */ || typeId === 4 /* TypeIds.Array */ || typeId === 5 /* TypeIds.Object */;