@qwik.dev/core 2.0.0-beta.17 → 2.0.0-beta.18

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.17-dev+c34c237
3
+ * @qwik.dev/core 2.0.0-beta.18-dev+a8081d4
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
@@ -14,7 +14,7 @@ import { p } from '@qwik.dev/core/preloader';
14
14
  *
15
15
  * @public
16
16
  */
17
- const version = "2.0.0-beta.17-dev+c34c237";
17
+ const version = "2.0.0-beta.18-dev+a8081d4";
18
18
 
19
19
  // same as isDev but separate so we can test
20
20
  const qDev = globalThis.qDev !== false;
@@ -96,8 +96,8 @@ const isString = (v) => {
96
96
  const isFunction = (v) => {
97
97
  return typeof v === 'function';
98
98
  };
99
- const isPrimitive = (v) => {
100
- return typeof v !== 'object' && typeof v !== 'function' && v !== null && v !== undefined;
99
+ const isPrimitiveOrNullUndefined = (v) => {
100
+ return (typeof v !== 'object' && typeof v !== 'function') || v === null || v === undefined;
101
101
  };
102
102
 
103
103
  const codeToText = (code, ...parts) => {
@@ -248,6 +248,8 @@ const ELEMENT_KEY = 'q:key';
248
248
  const ELEMENT_PROPS = 'q:props';
249
249
  const ELEMENT_SEQ = 'q:seq';
250
250
  const ELEMENT_SEQ_IDX = 'q:seqIdx';
251
+ const ITERATION_ITEM_SINGLE = 'q:p'; // Single iteration parameter (not an array)
252
+ const ITERATION_ITEM_MULTI = 'q:ps'; // Multiple iteration parameters (array)
251
253
  /** Non serializable markers - always begins with `:` character */
252
254
  const NON_SERIALIZABLE_MARKER_PREFIX = ':';
253
255
  const USE_ON_LOCAL = NON_SERIALIZABLE_MARKER_PREFIX + 'on';
@@ -260,6 +262,7 @@ const STREAM_BLOCK_END_COMMENT = 'qkssr-po';
260
262
  const Q_PROPS_SEPARATOR = ':';
261
263
  const dangerouslySetInnerHTML = 'dangerouslySetInnerHTML';
262
264
  const qwikInspectorAttr = 'data-qwik-inspector';
265
+ const debugStyleScopeIdPrefixAttr = '__scopedStyleIdPrefix__';
263
266
 
264
267
  // keep this import from core/build so the cjs build works
265
268
  const createPlatform = () => {
@@ -388,17 +391,6 @@ const safeCall = (call, thenFn, rejectFn) => {
388
391
  return rejectFn(e);
389
392
  }
390
393
  };
391
- const catchError = (valueOrPromise, rejectFn) => {
392
- try {
393
- if (isPromise(valueOrPromise)) {
394
- return valueOrPromise.catch(rejectFn);
395
- }
396
- return valueOrPromise;
397
- }
398
- catch (e) {
399
- return rejectFn(e);
400
- }
401
- };
402
394
  const maybeThen = (valueOrPromise, thenFn) => {
403
395
  return isPromise(valueOrPromise)
404
396
  ? valueOrPromise.then(thenFn)
@@ -781,16 +773,11 @@ function vnode_cloneElementWithNamespace(elementVNode, parentVNode, namespace, n
781
773
  }
782
774
  return rootElement;
783
775
  }
784
- function isSvg(tagOrVNode) {
785
- return typeof tagOrVNode === 'string'
786
- ? isSvgElement(tagOrVNode)
787
- : (tagOrVNode.flags & 128 /* VNodeFlags.NS_svg */) !== 0;
788
- }
789
- function isMath(tagOrVNode) {
790
- return typeof tagOrVNode === 'string'
791
- ? isMathElement(tagOrVNode)
792
- : (tagOrVNode.flags & 256 /* VNodeFlags.NS_math */) !== 0;
793
- }
776
+ // reuse the same object to avoid creating new objects on every call
777
+ const NEW_NAMESPACE_DATA = {
778
+ elementNamespace: HTML_NS,
779
+ elementNamespaceFlag: 0 /* VNodeFlags.NS_html */,
780
+ };
794
781
  function getNewElementNamespaceData(domParentVNode, tagOrVNode) {
795
782
  const parentIsDefaultNamespace = domParentVNode
796
783
  ? !!vnode_getElementName(domParentVNode) && vnode_isDefaultNamespace(domParentVNode)
@@ -815,10 +802,28 @@ function getNewElementNamespaceData(domParentVNode, tagOrVNode) {
815
802
  elementNamespace = isParentSvg ? SVG_NS : isParentMath ? MATH_NS : HTML_NS;
816
803
  elementNamespaceFlag = domParentVNode.flags & 384 /* VNodeFlags.NAMESPACE_MASK */;
817
804
  }
818
- return {
819
- elementNamespace,
820
- elementNamespaceFlag,
821
- };
805
+ NEW_NAMESPACE_DATA.elementNamespace = elementNamespace;
806
+ NEW_NAMESPACE_DATA.elementNamespaceFlag = elementNamespaceFlag;
807
+ return NEW_NAMESPACE_DATA;
808
+ }
809
+ function isSvg(tagOrVNode) {
810
+ if (typeof tagOrVNode === 'string') {
811
+ return isSvgElement(tagOrVNode);
812
+ }
813
+ if (vnode_isElementVNode(tagOrVNode)) {
814
+ return (isSvgElement(vnode_getElementName(tagOrVNode)) || (tagOrVNode.flags & 128 /* VNodeFlags.NS_svg */) !== 0);
815
+ }
816
+ return false;
817
+ }
818
+ function isMath(tagOrVNode) {
819
+ if (typeof tagOrVNode === 'string') {
820
+ return isMathElement(tagOrVNode);
821
+ }
822
+ if (vnode_isElementVNode(tagOrVNode)) {
823
+ return (isMathElement(vnode_getElementName(tagOrVNode)) ||
824
+ (tagOrVNode.flags & 256 /* VNodeFlags.NS_math */) !== 0);
825
+ }
826
+ return false;
822
827
  }
823
828
  function getAttributeNamespace(attributeName) {
824
829
  switch (attributeName) {
@@ -854,18 +859,18 @@ class BackRef {
854
859
  }
855
860
 
856
861
  /** @internal */
857
- class VNode extends BackRef {
862
+ class VNode {
858
863
  flags;
859
864
  parent;
860
865
  previousSibling;
861
866
  nextSibling;
862
867
  props;
868
+ [_EFFECT_BACK_REF] = undefined;
863
869
  slotParent = null;
864
870
  dirty = 0 /* ChoreBits.NONE */;
865
871
  dirtyChildren = null;
866
872
  nextDirtyChildIndex = 0;
867
873
  constructor(flags, parent, previousSibling, nextSibling, props) {
868
- super();
869
874
  this.flags = flags;
870
875
  this.parent = parent;
871
876
  this.previousSibling = previousSibling;
@@ -877,22 +882,29 @@ class VNode extends BackRef {
877
882
  if (isDev) {
878
883
  return vnode_toString.call(this);
879
884
  }
880
- return super.toString();
885
+ return Object.prototype.toString.call(this);
881
886
  }
882
887
  }
883
888
 
884
889
  /** @internal */
885
- class ElementVNode extends VNode {
890
+ class VirtualVNode extends VNode {
886
891
  key;
887
892
  firstChild;
888
893
  lastChild;
889
- node;
890
- elementName;
891
- constructor(key, flags, parent, previousSibling, nextSibling, props, firstChild, lastChild, node, elementName) {
894
+ constructor(key, flags, parent, previousSibling, nextSibling, props, firstChild, lastChild) {
892
895
  super(flags, parent, previousSibling, nextSibling, props);
893
896
  this.key = key;
894
897
  this.firstChild = firstChild;
895
898
  this.lastChild = lastChild;
899
+ }
900
+ }
901
+
902
+ /** @internal */
903
+ class ElementVNode extends VirtualVNode {
904
+ node;
905
+ elementName;
906
+ constructor(key, flags, parent, previousSibling, nextSibling, props, firstChild, lastChild, node, elementName) {
907
+ super(key, flags, parent, previousSibling, nextSibling, props, firstChild, lastChild);
896
908
  this.node = node;
897
909
  this.elementName = elementName;
898
910
  }
@@ -911,19 +923,6 @@ class TextVNode extends VNode {
911
923
  }
912
924
  }
913
925
 
914
- /** @internal */
915
- class VirtualVNode extends VNode {
916
- key;
917
- firstChild;
918
- lastChild;
919
- constructor(key, flags, parent, previousSibling, nextSibling, props, firstChild, lastChild) {
920
- super(flags, parent, previousSibling, nextSibling, props);
921
- this.key = key;
922
- this.firstChild = firstChild;
923
- this.lastChild = lastChild;
924
- }
925
- }
926
-
927
926
  /**
928
927
  * @file Cursor queue management for cursor-based scheduling.
929
928
  *
@@ -977,6 +976,7 @@ function getHighestPriorityCursor() {
977
976
  function pauseCursor(cursor, container) {
978
977
  pausedCursorQueue.push(cursor);
979
978
  removeCursorFromQueue(cursor, container, true);
979
+ container.$pausedCursorCount$++;
980
980
  }
981
981
  function resumeCursor(cursor, container) {
982
982
  const index = pausedCursorQueue.indexOf(cursor);
@@ -986,6 +986,7 @@ function resumeCursor(cursor, container) {
986
986
  pausedCursorQueue[index] = pausedCursorQueue[lastIndex];
987
987
  }
988
988
  pausedCursorQueue.pop();
989
+ container.$pausedCursorCount$--;
989
990
  }
990
991
  addCursorToQueue(container, cursor);
991
992
  }
@@ -995,9 +996,6 @@ function resumeCursor(cursor, container) {
995
996
  * @param cursor - The cursor to remove
996
997
  */
997
998
  function removeCursorFromQueue(cursor, container, keepCursorFlag) {
998
- if (container.$cursorCount$ > 0) {
999
- container.$cursorCount$--;
1000
- }
1001
999
  if (!keepCursorFlag) {
1002
1000
  cursor.flags &= -65 /* VNodeFlags.Cursor */;
1003
1001
  }
@@ -1011,6 +1009,7 @@ function removeCursorFromQueue(cursor, container, keepCursorFlag) {
1011
1009
  // }
1012
1010
  // globalCursorQueue.pop();
1013
1011
  globalCursorQueue.splice(index, 1);
1012
+ container.$cursorCount$--;
1014
1013
  }
1015
1014
  }
1016
1015
 
@@ -1117,7 +1116,35 @@ class SignalImpl {
1117
1116
  this.$untrackedValue$ = value;
1118
1117
  }
1119
1118
  get value() {
1120
- return setupSignalValueAccess(this, () => (this.$effects$ ||= new Set()), () => this.untrackedValue);
1119
+ const ctx = tryGetInvokeContext();
1120
+ if (!ctx) {
1121
+ return this.untrackedValue;
1122
+ }
1123
+ if (this.$container$ === null) {
1124
+ if (!ctx.$container$) {
1125
+ return this.untrackedValue;
1126
+ }
1127
+ // Grab the container now we have access to it
1128
+ this.$container$ = ctx.$container$;
1129
+ }
1130
+ else {
1131
+ isDev &&
1132
+ assertTrue(!ctx.$container$ || ctx.$container$ === this.$container$, 'Do not use signals across containers');
1133
+ }
1134
+ const effectSubscriber = ctx.$effectSubscriber$;
1135
+ if (effectSubscriber) {
1136
+ // Let's make sure that we have a reference to this effect.
1137
+ // Adding reference is essentially adding a subscription, so if the signal
1138
+ // changes we know who to notify.
1139
+ ensureContainsSubscription((this.$effects$ ||= new Set()), effectSubscriber);
1140
+ // But when effect is scheduled in needs to be able to know which signals
1141
+ // to unsubscribe from. So we need to store the reference from the effect back
1142
+ // to this signal.
1143
+ ensureContainsBackRef(effectSubscriber, this);
1144
+ (import.meta.env.TEST ? !isDomContainer(this.$container$) : isServer) &&
1145
+ addQrlToSerializationCtx(effectSubscriber, this.$container$);
1146
+ }
1147
+ return this.untrackedValue;
1121
1148
  }
1122
1149
  set value(value) {
1123
1150
  if (value !== this.$untrackedValue$) {
@@ -1135,7 +1162,7 @@ class SignalImpl {
1135
1162
  if (isDev) {
1136
1163
  return (`[${this.constructor.name}${this.$flags$ & 1 /* SignalFlags.INVALID */ ? ' INVALID' : ''} ${String(this.$untrackedValue$)}]` +
1137
1164
  (Array.from(this.$effects$ || [])
1138
- .map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' '))
1165
+ .map((e) => '\n -> ' + pad(qwikDebugToString(e.consumer), ' '))
1139
1166
  .join('\n') || ''));
1140
1167
  }
1141
1168
  else {
@@ -1146,34 +1173,33 @@ class SignalImpl {
1146
1173
  return { value: this.$untrackedValue$ };
1147
1174
  }
1148
1175
  }
1149
- const addEffect = (signal, effectSubscriber, effects) => {
1150
- // Let's make sure that we have a reference to this effect.
1151
- // Adding reference is essentially adding a subscription, so if the signal
1152
- // changes we know who to notify.
1153
- ensureContainsSubscription(effects, effectSubscriber);
1154
- // But when effect is scheduled in needs to be able to know which signals
1155
- // to unsubscribe from. So we need to store the reference from the effect back
1156
- // to this signal.
1157
- ensureContainsBackRef(effectSubscriber, signal);
1158
- addQrlToSerializationCtx(effectSubscriber, signal.$container$);
1159
- };
1160
1176
  const setupSignalValueAccess = (target, effectsFn, returnValueFn) => {
1161
1177
  const ctx = tryGetInvokeContext();
1162
- if (ctx) {
1163
- if (target.$container$ === null) {
1164
- if (!ctx.$container$) {
1165
- return returnValueFn();
1166
- }
1167
- // Grab the container now we have access to it
1168
- target.$container$ = ctx.$container$;
1178
+ if (!ctx) {
1179
+ return returnValueFn();
1180
+ }
1181
+ if (target.$container$ === null) {
1182
+ if (!ctx.$container$) {
1183
+ return returnValueFn();
1169
1184
  }
1170
- else {
1185
+ // Grab the container now we have access to it
1186
+ target.$container$ = ctx.$container$;
1187
+ }
1188
+ else {
1189
+ isDev &&
1171
1190
  assertTrue(!ctx.$container$ || ctx.$container$ === target.$container$, 'Do not use signals across containers');
1172
- }
1173
- const effectSubscriber = ctx.$effectSubscriber$;
1174
- if (effectSubscriber) {
1175
- addEffect(target, effectSubscriber, effectsFn());
1176
- }
1191
+ }
1192
+ const effectSubscriber = ctx.$effectSubscriber$;
1193
+ if (effectSubscriber) {
1194
+ // Let's make sure that we have a reference to this effect.
1195
+ // Adding reference is essentially adding a subscription, so if the signal
1196
+ // changes we know who to notify.
1197
+ ensureContainsSubscription(effectsFn(), effectSubscriber);
1198
+ // But when effect is scheduled in needs to be able to know which signals
1199
+ // to unsubscribe from. So we need to store the reference from the effect back
1200
+ // to this signal.
1201
+ ensureContainsBackRef(effectSubscriber, target);
1202
+ addQrlToSerializationCtx(effectSubscriber, target.$container$);
1177
1203
  }
1178
1204
  return returnValueFn();
1179
1205
  };
@@ -1203,9 +1229,13 @@ const _UNINITIALIZED = Symbol('UNINITIALIZED');
1203
1229
  */
1204
1230
  const EVENT_SUFFIX = '$';
1205
1231
  const isHtmlAttributeAnEventName = (name) => {
1206
- return (name.startsWith("on:" /* EventNameHtmlScope.on */) ||
1207
- name.startsWith("on-window:" /* EventNameHtmlScope.window */) ||
1208
- name.startsWith("on-document:" /* EventNameHtmlScope.document */));
1232
+ if (name.charCodeAt(0) !== 111 /* o */ || name.charCodeAt(1) !== 110 /* n */) {
1233
+ return false;
1234
+ }
1235
+ if (name.charCodeAt(2) === 58 /* : */) {
1236
+ return true; // on:
1237
+ }
1238
+ return name.startsWith("on-window:" /* EventNameHtmlScope.window */) || name.startsWith("on-document:" /* EventNameHtmlScope.document */);
1209
1239
  };
1210
1240
  function jsxEventToHtmlAttribute(jsxEvent) {
1211
1241
  if (jsxEvent.endsWith(EVENT_SUFFIX)) {
@@ -1431,7 +1461,8 @@ const addPropsProxyEffect = (propsProxy, prop) => {
1431
1461
  }
1432
1462
  }
1433
1463
  else {
1434
- assertTrue(!ctx.$container$ || ctx.$container$ === propsProxy.$container$, 'Do not use props across containers');
1464
+ isDev &&
1465
+ assertTrue(!ctx.$container$ || ctx.$container$ === propsProxy.$container$, 'Do not use props across containers');
1435
1466
  }
1436
1467
  }
1437
1468
  const effectSubscriber = ctx?.$effectSubscriber$;
@@ -1444,6 +1475,7 @@ const triggerPropsProxyEffect = (propsProxy, prop) => {
1444
1475
  if (effects) {
1445
1476
  scheduleEffects(propsProxy.$container$, propsProxy, effects);
1446
1477
  }
1478
+ return !!effects;
1447
1479
  };
1448
1480
  function getEffects$1(effects, prop) {
1449
1481
  // TODO: Handle STORE_ALL_PROPS
@@ -1493,30 +1525,6 @@ const cleanupDestroyable = (destroyable) => {
1493
1525
  }
1494
1526
  };
1495
1527
 
1496
- function getSubscriber(effect, prop, data) {
1497
- if (!effect[_EFFECT_BACK_REF]) {
1498
- if (isServer && isSsrNode(effect)) {
1499
- effect.setProp(QBackRefs, new Map());
1500
- }
1501
- else {
1502
- effect[_EFFECT_BACK_REF] = new Map();
1503
- }
1504
- }
1505
- const subMap = effect[_EFFECT_BACK_REF];
1506
- let sub = subMap.get(prop);
1507
- if (!sub) {
1508
- sub = [effect, prop];
1509
- subMap.set(prop, sub);
1510
- }
1511
- if (data) {
1512
- sub[3 /* EffectSubscriptionProp.DATA */] = data;
1513
- }
1514
- return sub;
1515
- }
1516
- function isSsrNode(value) {
1517
- return '__brand__' in value && value.__brand__ === 'SsrNode';
1518
- }
1519
-
1520
1528
  /**
1521
1529
  * # ================================
1522
1530
  *
@@ -1529,6 +1537,54 @@ function isSsrNode(value) {
1529
1537
  * "marked as dirty" flag.
1530
1538
  */
1531
1539
  const NEEDS_COMPUTATION = Symbol('invalid');
1540
+ /**
1541
+ * An effect consumer plus type of effect, back references to producers and additional data
1542
+ *
1543
+ * An effect can be trigger by one or more of signal inputs. The first step of re-running an effect
1544
+ * is to clear its subscriptions so that the effect can re add new set of subscriptions. In order to
1545
+ * clear the subscriptions we need to store them here.
1546
+ *
1547
+ * Imagine you have effect such as:
1548
+ *
1549
+ * ```
1550
+ * function effect1() {
1551
+ * console.log(signalA.value ? signalB.value : 'default');
1552
+ * }
1553
+ * ```
1554
+ *
1555
+ * In the above case the `signalB` needs to be unsubscribed when `signalA` is falsy. We do this by
1556
+ * always clearing all of the subscriptions
1557
+ *
1558
+ * The `EffectSubscription` stores
1559
+ *
1560
+ * ```
1561
+ * subscription1 = [effectConsumer1, EffectProperty.COMPONENT, Set[(signalA, signalB)]];
1562
+ * ```
1563
+ *
1564
+ * The `signal1` and `signal2` back references are needed to "clear" existing subscriptions.
1565
+ *
1566
+ * Both `signalA` as well as `signalB` will have a reference to `subscription` to the so that the
1567
+ * effect can be scheduled if either `signalA` or `signalB` triggers. The `subscription1` is shared
1568
+ * between the signals.
1569
+ *
1570
+ * The second position `EffectProperty|string` store the property name of the effect.
1571
+ *
1572
+ * - Property name of the VNode
1573
+ * - `EffectProperty.COMPONENT` if component
1574
+ * - `EffectProperty.VNODE` if VNode
1575
+ */
1576
+ class EffectSubscription {
1577
+ consumer;
1578
+ property;
1579
+ backRef;
1580
+ data;
1581
+ constructor(consumer, property, backRef = null, data = null) {
1582
+ this.consumer = consumer;
1583
+ this.property = property;
1584
+ this.backRef = backRef;
1585
+ this.data = data;
1586
+ }
1587
+ }
1532
1588
  /**
1533
1589
  * # ================================
1534
1590
  *
@@ -1540,6 +1596,30 @@ const STORE_TARGET = Symbol('store.target');
1540
1596
  const STORE_HANDLER = Symbol('store.handler');
1541
1597
  const STORE_ALL_PROPS = Symbol('store.all');
1542
1598
 
1599
+ function getSubscriber(effect, prop, data) {
1600
+ if (!effect[_EFFECT_BACK_REF]) {
1601
+ if (isServer && isSsrNode(effect)) {
1602
+ effect.setProp(QBackRefs, new Map());
1603
+ }
1604
+ else {
1605
+ effect[_EFFECT_BACK_REF] = new Map();
1606
+ }
1607
+ }
1608
+ const subMap = effect[_EFFECT_BACK_REF];
1609
+ let sub = subMap.get(prop);
1610
+ if (!sub) {
1611
+ sub = new EffectSubscription(effect, prop);
1612
+ subMap.set(prop, sub);
1613
+ }
1614
+ if (data) {
1615
+ sub.data = data;
1616
+ }
1617
+ return sub;
1618
+ }
1619
+ function isSsrNode(value) {
1620
+ return '__brand__' in value && value.__brand__ === 'SsrNode';
1621
+ }
1622
+
1543
1623
  const trackFn = (target, container) => (obj, prop) => {
1544
1624
  const ctx = newInvokeContext();
1545
1625
  ctx.$effectSubscriber$ = getSubscriber(target, ":" /* EffectProperty.COMPONENT */);
@@ -1639,7 +1719,7 @@ class ComputedSignalImpl extends SignalImpl {
1639
1719
  }
1640
1720
  get untrackedValue() {
1641
1721
  this.$computeIfNeeded$();
1642
- assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
1722
+ isDev && assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
1643
1723
  return this.$untrackedValue$;
1644
1724
  }
1645
1725
  $computeIfNeeded$() {
@@ -1650,7 +1730,11 @@ class ComputedSignalImpl extends SignalImpl {
1650
1730
  throwIfQRLNotResolved(computeQrl);
1651
1731
  const ctx = tryGetInvokeContext();
1652
1732
  const previousEffectSubscription = ctx?.$effectSubscriber$;
1653
- ctx && (ctx.$effectSubscriber$ = getSubscriber(this, "." /* EffectProperty.VNODE */));
1733
+ if (ctx) {
1734
+ const effectSubscriber = getSubscriber(this, "." /* EffectProperty.VNODE */);
1735
+ clearEffectSubscription(this.$container$, effectSubscriber);
1736
+ ctx.$effectSubscriber$ = effectSubscriber;
1737
+ }
1654
1738
  try {
1655
1739
  const untrackedValue = computeQrl.getFn(ctx)();
1656
1740
  if (isPromise(untrackedValue)) {
@@ -1993,7 +2077,7 @@ const _wrapProp = (...args) => {
1993
2077
  }
1994
2078
  if (isSignal(obj)) {
1995
2079
  if (!(obj instanceof AsyncComputedSignalImpl)) {
1996
- assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
2080
+ isDev && assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
1997
2081
  }
1998
2082
  if (obj instanceof WrappedSignalImpl && obj.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */) {
1999
2083
  return obj;
@@ -2003,11 +2087,11 @@ const _wrapProp = (...args) => {
2003
2087
  if (isPropsProxy(obj)) {
2004
2088
  const constProps = obj[_CONST_PROPS];
2005
2089
  const varProps = obj[_VAR_PROPS];
2006
- if (constProps && prop in constProps) {
2090
+ if (constProps && Object.prototype.hasOwnProperty.call(constProps, prop)) {
2007
2091
  // Const props don't need wrapping
2008
2092
  return constProps[prop];
2009
2093
  }
2010
- else if (prop in varProps) {
2094
+ else if (Object.prototype.hasOwnProperty.call(varProps, prop)) {
2011
2095
  const value = varProps[prop];
2012
2096
  return wrapIfNotSignal(value, args);
2013
2097
  }
@@ -2086,7 +2170,7 @@ class WrappedSignalImpl extends SignalImpl {
2086
2170
  }
2087
2171
  get untrackedValue() {
2088
2172
  this.$computeIfNeeded$();
2089
- assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
2173
+ isDev && assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
2090
2174
  return this.$untrackedValue$;
2091
2175
  }
2092
2176
  $computeIfNeeded$() {
@@ -2137,7 +2221,7 @@ function clearAllEffects(container, consumer) {
2137
2221
  effects.clear();
2138
2222
  }
2139
2223
  function clearEffectSubscription(container, effect) {
2140
- const backRefs = effect[2 /* EffectSubscriptionProp.BACK_REF */];
2224
+ const backRefs = effect.backRef;
2141
2225
  if (!backRefs) {
2142
2226
  return;
2143
2227
  }
@@ -2221,15 +2305,17 @@ const useLexicalScope = () => {
2221
2305
  let qrl = context.$qrl$;
2222
2306
  if (!qrl) {
2223
2307
  const el = context.$hostElement$ instanceof ElementVNode ? context.$hostElement$.node : undefined;
2224
- assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
2308
+ isDev && assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
2225
2309
  const containerElement = _getQContainerElement(el);
2226
- assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
2310
+ isDev && assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
2227
2311
  const container = getDomContainer(containerElement);
2312
+ context.$container$ ||= container;
2228
2313
  qrl = container.parseQRL(decodeURIComponent(String(context.$url$)));
2229
2314
  }
2230
2315
  else {
2231
- assertQrl(qrl);
2232
- assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
2316
+ isDev && assertQrl(qrl);
2317
+ isDev &&
2318
+ assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
2233
2319
  }
2234
2320
  return qrl.$captureRef$;
2235
2321
  };
@@ -2253,85 +2339,30 @@ const _chk = (_, element) => {
2253
2339
  signal.value = element.checked;
2254
2340
  };
2255
2341
 
2256
- const _hasOwnProperty$1 = Object.prototype.hasOwnProperty;
2257
- const BIND_VALUE = 'bind:value';
2258
- const BIND_CHECKED = 'bind:checked';
2342
+ const _hasOwnProperty$2 = Object.prototype.hasOwnProperty;
2259
2343
  // TODO store props as the arrays the vnodes also use?
2260
2344
  class JSXNodeImpl {
2261
2345
  type;
2346
+ children;
2262
2347
  toSort;
2263
2348
  key;
2264
2349
  varProps;
2265
2350
  constProps;
2266
- children;
2267
2351
  dev;
2268
2352
  _proxy = null;
2269
2353
  constructor(type, varProps, constProps, children, key, toSort, dev) {
2270
2354
  this.type = type;
2355
+ this.children = children;
2271
2356
  this.toSort = !!toSort;
2272
- this.key = key == null ? null : String(key);
2357
+ this.key = key === null || key === undefined ? null : typeof key === 'string' ? key : '' + key;
2273
2358
  this.varProps = !varProps || isEmpty(varProps) ? EMPTY_OBJ : varProps;
2274
2359
  this.constProps = !constProps || isEmpty(constProps) ? null : constProps;
2275
- this.children = children;
2276
2360
  if (qDev && dev) {
2277
2361
  this.dev = {
2278
2362
  ...dev,
2279
2363
  stack: new Error().stack?.split('\n').slice(2).join('\n'),
2280
2364
  };
2281
2365
  }
2282
- if (typeof type === 'string') {
2283
- // convert onEvent$ to on:event
2284
- for (const k in this.constProps) {
2285
- const attr = jsxEventToHtmlAttribute(k);
2286
- if (attr) {
2287
- mergeHandlers(this.constProps, attr, this.constProps[k]);
2288
- delete this.constProps[k];
2289
- }
2290
- }
2291
- for (const k in this.varProps) {
2292
- const attr = jsxEventToHtmlAttribute(k);
2293
- if (attr) {
2294
- // constProps always wins
2295
- if (!constProps || !_hasOwnProperty$1.call(constProps, k)) {
2296
- toSort = mergeHandlers(this.varProps, attr, this.varProps[k]) || toSort;
2297
- }
2298
- delete this.varProps[k];
2299
- }
2300
- }
2301
- // bind:*
2302
- if (_hasOwnProperty$1.call(this.varProps, BIND_CHECKED)) {
2303
- toSort = handleBindProp(this.varProps, BIND_CHECKED) || toSort;
2304
- }
2305
- else if (_hasOwnProperty$1.call(this.varProps, BIND_VALUE)) {
2306
- toSort = handleBindProp(this.varProps, BIND_VALUE) || toSort;
2307
- }
2308
- else if (this.constProps) {
2309
- if (_hasOwnProperty$1.call(this.constProps, BIND_CHECKED)) {
2310
- handleBindProp(this.constProps, BIND_CHECKED);
2311
- }
2312
- else {
2313
- if (_hasOwnProperty$1.call(this.constProps, BIND_VALUE)) {
2314
- handleBindProp(this.constProps, BIND_VALUE);
2315
- }
2316
- }
2317
- }
2318
- if (_hasOwnProperty$1.call(this.varProps, 'className')) {
2319
- this.varProps.class = this.varProps.className;
2320
- this.varProps.className = undefined;
2321
- toSort = true;
2322
- if (qDev) {
2323
- logOnceWarn(`jsx${dev ? ` ${dev.fileName}${dev?.lineNumber ? `:${dev.lineNumber}` : ''}` : ''}: \`className\` is deprecated. Use \`class\` instead.`);
2324
- }
2325
- }
2326
- // TODO let the optimizer do this instead
2327
- if (this.constProps && _hasOwnProperty$1.call(this.constProps, 'className')) {
2328
- this.constProps.class = this.constProps.className;
2329
- this.constProps.className = undefined;
2330
- if (qDev) {
2331
- logOnceWarn(`jsx${dev ? ` ${dev.fileName}${dev?.lineNumber ? `:${dev.lineNumber}` : ''}` : ''}: \`className\` is deprecated. Use \`class\` instead.`);
2332
- }
2333
- }
2334
- }
2335
2366
  seal(this);
2336
2367
  }
2337
2368
  get props() {
@@ -2362,9 +2393,9 @@ const isJSXNode = (n) => {
2362
2393
  return true;
2363
2394
  }
2364
2395
  if (isObject(n) &&
2365
- _hasOwnProperty$1.call(n, 'key') &&
2366
- _hasOwnProperty$1.call(n, 'props') &&
2367
- _hasOwnProperty$1.call(n, 'type')) {
2396
+ _hasOwnProperty$2.call(n, 'key') &&
2397
+ _hasOwnProperty$2.call(n, 'props') &&
2398
+ _hasOwnProperty$2.call(n, 'type')) {
2368
2399
  logWarn(`Duplicate implementations of "JSXNode" found`);
2369
2400
  return true;
2370
2401
  }
@@ -2375,29 +2406,12 @@ const isJSXNode = (n) => {
2375
2406
  }
2376
2407
  };
2377
2408
  const isEmpty = (obj) => {
2378
- for (const prop in obj) {
2379
- if (obj[prop] !== undefined) {
2380
- return false;
2381
- }
2382
- }
2383
- return true;
2384
- };
2385
- const handleBindProp = (props, prop) => {
2386
- const value = props[prop];
2387
- props[prop] = undefined;
2388
- if (value) {
2389
- if (prop === BIND_CHECKED) {
2390
- props.checked = value;
2391
- props['on:input'] = createQRL(null, '_chk', _chk, null, null, [value]);
2392
- }
2393
- else {
2394
- props.value = value;
2395
- props['on:input'] = createQRL(null, '_val', _val, null, null, [value]);
2396
- }
2397
- return true;
2398
- }
2409
+ return Object.keys(obj).length === 0;
2399
2410
  };
2400
2411
 
2412
+ const BIND_VALUE = 'bind:value';
2413
+ const BIND_CHECKED = 'bind:checked';
2414
+ const _hasOwnProperty$1 = Object.prototype.hasOwnProperty;
2401
2415
  /**
2402
2416
  * Create a JSXNode with the properties fully split into variable and constant parts, and children
2403
2417
  * separated out. Furthermore, the varProps must be a sorted object, that is, the keys must be
@@ -2415,7 +2429,7 @@ const handleBindProp = (props, prop) => {
2415
2429
  * @internal
2416
2430
  */
2417
2431
  const _jsxSorted = (type, varProps, constProps, children, flags, key, dev) => {
2418
- return untrack(() => new JSXNodeImpl(type, varProps, constProps, children, key, false, dev));
2432
+ return new JSXNodeImpl(type, varProps, constProps, children, key, false, dev);
2419
2433
  };
2420
2434
  /**
2421
2435
  * Create a JSXNode, with the properties split into variable and constant parts, but the variable
@@ -2434,24 +2448,158 @@ const _jsxSorted = (type, varProps, constProps, children, flags, key, dev) => {
2434
2448
  * @internal
2435
2449
  */
2436
2450
  const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
2437
- return untrack(() => {
2451
+ let toSort = false;
2452
+ let constPropsCopied = false;
2453
+ let varPropsCopied = false;
2454
+ let bindValueSignal = null;
2455
+ let bindCheckedSignal = null;
2456
+ // Apply transformations for native HTML elements only
2457
+ if (typeof type === 'string') {
2458
+ // Transform event names (onClick$ -> on:click)
2459
+ if (constProps) {
2460
+ const processedKeys = new Set();
2461
+ for (const k in constProps) {
2462
+ const attr = jsxEventToHtmlAttribute(k);
2463
+ if (attr) {
2464
+ if (!constPropsCopied) {
2465
+ constProps = { ...constProps };
2466
+ constPropsCopied = true;
2467
+ }
2468
+ if (!_hasOwnProperty$1.call(constProps, attr) || processedKeys.has(attr)) {
2469
+ constProps[attr] = constProps[k];
2470
+ }
2471
+ delete constProps[k];
2472
+ }
2473
+ processedKeys.add(k);
2474
+ }
2475
+ }
2438
2476
  if (varProps) {
2477
+ const processedKeys = new Set();
2439
2478
  for (const k in varProps) {
2440
- if (k === 'children') {
2441
- children ||= varProps.children;
2442
- delete varProps.children;
2479
+ const attr = jsxEventToHtmlAttribute(k);
2480
+ if (attr) {
2481
+ if (!varPropsCopied) {
2482
+ varProps = { ...varProps };
2483
+ varPropsCopied = true;
2484
+ }
2485
+ // Transform event name in place
2486
+ if (!_hasOwnProperty$1.call(varProps, attr) || processedKeys.has(attr)) {
2487
+ varProps[attr] = varProps[k];
2488
+ }
2489
+ delete varProps[k];
2490
+ toSort = true;
2491
+ }
2492
+ else if (k === BIND_CHECKED) {
2493
+ // Set flag, will process after walk
2494
+ bindCheckedSignal = varProps[k];
2495
+ }
2496
+ else if (k === BIND_VALUE) {
2497
+ // Set flag, will process after walk
2498
+ bindValueSignal = varProps[k];
2499
+ }
2500
+ processedKeys.add(k);
2501
+ }
2502
+ // Handle bind:* - only in varProps, bind:* should be moved to varProps
2503
+ if (bindCheckedSignal || bindValueSignal) {
2504
+ if (!varPropsCopied) {
2505
+ varProps = { ...varProps };
2506
+ varPropsCopied = true;
2507
+ }
2508
+ if (bindCheckedSignal) {
2509
+ delete varProps[BIND_CHECKED];
2510
+ varProps.checked = bindCheckedSignal;
2511
+ const handler = createQRL(null, '_chk', _chk, null, null, [bindCheckedSignal]);
2512
+ // Move on:input from constProps if it exists
2513
+ if (constProps && _hasOwnProperty$1.call(constProps, 'on:input')) {
2514
+ if (!constPropsCopied) {
2515
+ constProps = { ...constProps };
2516
+ constPropsCopied = true;
2517
+ }
2518
+ const existingHandler = constProps['on:input'];
2519
+ delete constProps['on:input'];
2520
+ toSort = mergeHandlers(varProps, 'on:input', existingHandler) || toSort;
2521
+ }
2522
+ toSort = mergeHandlers(varProps, 'on:input', handler) || toSort;
2523
+ }
2524
+ else if (bindValueSignal) {
2525
+ delete varProps[BIND_VALUE];
2526
+ varProps.value = bindValueSignal;
2527
+ const handler = createQRL(null, '_val', _val, null, null, [bindValueSignal]);
2528
+ // Move on:input from constProps if it exists
2529
+ if (constProps && _hasOwnProperty$1.call(constProps, 'on:input')) {
2530
+ if (!constPropsCopied) {
2531
+ constProps = { ...constProps };
2532
+ constPropsCopied = true;
2533
+ }
2534
+ const existingHandler = constProps['on:input'];
2535
+ delete constProps['on:input'];
2536
+ toSort = mergeHandlers(varProps, 'on:input', existingHandler) || toSort;
2537
+ }
2538
+ toSort = mergeHandlers(varProps, 'on:input', handler) || toSort;
2443
2539
  }
2444
- else if (k === 'key') {
2445
- key ||= varProps.key;
2446
- delete varProps.key;
2540
+ }
2541
+ }
2542
+ // Transform className -> class
2543
+ if (varProps && _hasOwnProperty$1.call(varProps, 'className')) {
2544
+ if (!varPropsCopied) {
2545
+ varProps = { ...varProps };
2546
+ varPropsCopied = true;
2547
+ }
2548
+ varProps.class = varProps.className;
2549
+ varProps.className = undefined;
2550
+ toSort = true;
2551
+ if (qDev) {
2552
+ logOnceWarn(`jsx${dev ? ` ${dev.fileName}${dev?.lineNumber ? `:${dev.lineNumber}` : ''}` : ''}: \`className\` is deprecated. Use \`class\` instead.`);
2553
+ }
2554
+ }
2555
+ if (constProps && _hasOwnProperty$1.call(constProps, 'className')) {
2556
+ if (!constPropsCopied) {
2557
+ constProps = { ...constProps };
2558
+ constPropsCopied = true;
2559
+ }
2560
+ constProps.class = constProps.className;
2561
+ constProps.className = undefined;
2562
+ if (qDev) {
2563
+ logOnceWarn(`jsx${dev ? ` ${dev.fileName}${dev?.lineNumber ? `:${dev.lineNumber}` : ''}` : ''}: \`className\` is deprecated. Use \`class\` instead.`);
2564
+ }
2565
+ }
2566
+ }
2567
+ if (varProps) {
2568
+ for (const k in varProps) {
2569
+ if (k === 'children') {
2570
+ if (!varPropsCopied) {
2571
+ varProps = { ...varProps };
2572
+ varPropsCopied = true;
2447
2573
  }
2448
- else if (constProps && k in constProps) {
2449
- delete varProps[k];
2574
+ children ||= varProps.children;
2575
+ delete varProps.children;
2576
+ }
2577
+ else if (k === 'key') {
2578
+ if (!varPropsCopied) {
2579
+ varProps = { ...varProps };
2580
+ varPropsCopied = true;
2581
+ }
2582
+ key ||= varProps.key;
2583
+ delete varProps.key;
2584
+ }
2585
+ else if (constProps && k in constProps) {
2586
+ if (!varPropsCopied) {
2587
+ varProps = { ...varProps };
2588
+ varPropsCopied = true;
2589
+ }
2590
+ delete varProps[k];
2591
+ }
2592
+ else if (varProps[k] === null) {
2593
+ if (!varPropsCopied) {
2594
+ varProps = { ...varProps };
2595
+ varPropsCopied = true;
2450
2596
  }
2597
+ // Clean up null markers (from event conversions)
2598
+ delete varProps[k];
2451
2599
  }
2452
2600
  }
2453
- return new JSXNodeImpl(type, varProps, constProps, children, key, true, dev);
2454
- });
2601
+ }
2602
+ return new JSXNodeImpl(type, varProps, constProps, children, key, toSort || true, dev);
2455
2603
  };
2456
2604
  /** @internal @deprecated v1 compat */
2457
2605
  const _jsxC = (type, mutable, _flags, key) => jsx(type, mutable, key);
@@ -2547,7 +2695,7 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2547
2695
  let isInlineComponent = false;
2548
2696
  if (componentQRL === null) {
2549
2697
  componentQRL = container.getHostProp(renderHost, OnRenderProp);
2550
- assertDefined(componentQRL, 'No Component found at this location');
2698
+ isDev && assertDefined(componentQRL, 'No Component found at this location');
2551
2699
  }
2552
2700
  if (isQrl(componentQRL)) {
2553
2701
  props = props || container.getHostProp(renderHost, ELEMENT_PROPS) || EMPTY_OBJ;
@@ -2566,6 +2714,7 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2566
2714
  const inlineComponent = componentQRL;
2567
2715
  componentFn = () => invokeApply(iCtx, inlineComponent, [props || EMPTY_OBJ]);
2568
2716
  }
2717
+ const isSsr = import.meta.env.TEST ? isServerPlatform() : isServer;
2569
2718
  const executeComponentWithPromiseExceptionRetry = (retryCount = 0) => safeCall(() => {
2570
2719
  if (!isInlineComponent) {
2571
2720
  container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
@@ -2576,6 +2725,19 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2576
2725
  }
2577
2726
  return maybeThen(componentFn(props), (jsx) => maybeThen(iCtx.$waitOn$, () => jsx));
2578
2727
  }, (jsx) => {
2728
+ // In SSR, check if the component was marked dirty (COMPONENT bit) during execution.
2729
+ // This happens when something completes and updates reactive primitives
2730
+ // while we're waiting on $waitOn$. If so, we need to re-execute the component
2731
+ // to get fresh JSX with updated values.
2732
+ if (isSsr && !isInlineComponent) {
2733
+ const ssrNode = renderHost;
2734
+ if (ssrNode.dirty & 4 /* ChoreBits.COMPONENT */) {
2735
+ ssrNode.dirty &= ~4 /* ChoreBits.COMPONENT */;
2736
+ if (retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
2737
+ return executeComponentWithPromiseExceptionRetry(retryCount + 1);
2738
+ }
2739
+ }
2740
+ }
2579
2741
  const useOnEvents = container.getHostProp(renderHost, USE_ON_LOCAL);
2580
2742
  if (useOnEvents) {
2581
2743
  return addUseOnEvents(jsx, useOnEvents);
@@ -2744,7 +2906,7 @@ function injectPlaceholderElement(jsx) {
2744
2906
  return [placeholder, jsx];
2745
2907
  }
2746
2908
  // For primitives, we can't add children, so we wrap them in a fragment.
2747
- if (isPrimitive(jsx)) {
2909
+ if (isPrimitiveOrNullUndefined(jsx)) {
2748
2910
  const placeholder = createPlaceholderScriptNode();
2749
2911
  return [placeholder, _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null)];
2750
2912
  }
@@ -2758,7 +2920,7 @@ function injectPlaceholderElement(jsx) {
2758
2920
  }
2759
2921
  /** @returns An empty <script> element for adding qwik metadata attributes to */
2760
2922
  function createPlaceholderScriptNode() {
2761
- return new JSXNodeImpl('script', null, { hidden: '' });
2923
+ return new JSXNodeImpl('script', null, { hidden: '' }, null, null);
2762
2924
  }
2763
2925
 
2764
2926
  /**
@@ -2874,15 +3036,12 @@ const _restProps = (props, omit = [], target = {}) => {
2874
3036
  varPropsTarget[key] = varProps[key];
2875
3037
  }
2876
3038
  }
2877
- return createPropsProxy(new JSXNodeImpl(null, varPropsTarget, constPropsTarget));
3039
+ return createPropsProxy(new JSXNodeImpl(null, varPropsTarget, constPropsTarget, null, null));
2878
3040
  };
2879
3041
 
2880
3042
  const styleContent = (styleId) => {
2881
3043
  return ComponentStylesPrefixContent + styleId;
2882
3044
  };
2883
- function hasClassAttr(props) {
2884
- return 'class' in props;
2885
- }
2886
3045
  function isClassAttr(key) {
2887
3046
  return key === 'class';
2888
3047
  }
@@ -3061,119 +3220,9 @@ const styleKey = (qStyles, index) => {
3061
3220
  return `${hashCode(qStyles.$hash$)}-${index}`;
3062
3221
  };
3063
3222
 
3064
- /**
3065
- * @internal
3066
- * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
3067
- */
3068
- const useSequentialScope = () => {
3069
- const iCtx = useInvokeContext();
3070
- const hostElement = iCtx.$hostElement$;
3071
- const host = hostElement;
3072
- let seq = iCtx.$container$.getHostProp(host, ELEMENT_SEQ);
3073
- if (seq === null) {
3074
- seq = [];
3075
- iCtx.$container$.setHostProp(host, ELEMENT_SEQ, seq);
3076
- }
3077
- let seqIdx = iCtx.$container$.getHostProp(host, ELEMENT_SEQ_IDX);
3078
- if (seqIdx === null) {
3079
- seqIdx = 0;
3080
- }
3081
- iCtx.$container$.setHostProp(host, ELEMENT_SEQ_IDX, seqIdx + 1);
3082
- while (seq.length <= seqIdx) {
3083
- seq.push(undefined);
3084
- }
3085
- const set = (value) => {
3086
- if (qDev && qSerialize) {
3087
- verifySerializable(value);
3088
- }
3089
- return (seq[seqIdx] = value);
3090
- };
3091
- return {
3092
- val: seq[seqIdx],
3093
- set,
3094
- i: seqIdx,
3095
- iCtx,
3096
- };
3097
- };
3098
-
3099
- /** @internal */
3100
- const useTaskQrl = (qrl, opts) => {
3101
- const { val, set, iCtx, i } = useSequentialScope();
3102
- if (val) {
3103
- return;
3104
- }
3105
- assertQrl(qrl);
3106
- set(1);
3107
- const taskFlags =
3108
- // enabled by default
3109
- opts?.deferUpdates === false ? 0 : 16 /* TaskFlags.RENDER_BLOCKING */;
3110
- const task = new Task(8 /* TaskFlags.DIRTY */ | 2 /* TaskFlags.TASK */ | taskFlags, i, iCtx.$hostElement$, qrl, undefined, null);
3111
- // In V2 we add the task to the sequential scope. We need to do this
3112
- // in order to be able to retrieve it later when the parent element is
3113
- // deleted and we need to be able to release the task subscriptions.
3114
- set(task);
3115
- const container = iCtx.$container$;
3116
- const { $waitOn$: waitOn } = iCtx;
3117
- const result = maybeThen(waitOn, () => runTask(task, container, iCtx.$hostElement$));
3118
- if (isPromise(result)) {
3119
- iCtx.$waitOn$ = result;
3120
- }
3121
- };
3122
- const runTask = (task, container, host) => {
3123
- task.$flags$ &= -9 /* TaskFlags.DIRTY */;
3124
- cleanupDestroyable(task);
3125
- const iCtx = newInvokeContext(container.$locale$, host, TaskEvent);
3126
- iCtx.$container$ = container;
3127
- const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
3128
- const track = trackFn(task, container);
3129
- const [cleanup] = cleanupFn(task, (reason) => container.handleError(reason, host));
3130
- const taskApi = { track, cleanup };
3131
- return safeCall(() => taskFn(taskApi), cleanup, (err) => {
3132
- // If a Promise is thrown, that means we need to re-run the task.
3133
- if (isPromise(err)) {
3134
- return err.then(() => runTask(task, container, host));
3135
- }
3136
- else {
3137
- container.handleError(err, host);
3138
- }
3139
- });
3140
- };
3141
- class Task extends BackRef {
3142
- $flags$;
3143
- $index$;
3144
- $el$;
3145
- $qrl$;
3146
- $state$;
3147
- $destroy$;
3148
- constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
3149
- super();
3150
- this.$flags$ = $flags$;
3151
- this.$index$ = $index$;
3152
- this.$el$ = $el$;
3153
- this.$qrl$ = $qrl$;
3154
- this.$state$ = $state$;
3155
- this.$destroy$ = $destroy$;
3156
- }
3157
- }
3158
- /** @internal */
3159
- const isTask = (value) => {
3160
- return value instanceof Task;
3161
- };
3162
- /**
3163
- * Used internally as a qrl event handler to schedule a task.
3164
- *
3165
- * @internal
3166
- */
3167
- const scheduleTask = (_event, element) => {
3168
- const [task] = useLexicalScope();
3169
- const container = getDomContainer(element);
3170
- task.$flags$ |= 8 /* TaskFlags.DIRTY */;
3171
- markVNodeDirty(container, task.$el$, 1 /* ChoreBits.TASKS */);
3172
- };
3173
-
3174
3223
  /** @internal */
3175
3224
  const mapApp_findIndx = (array, key, start) => {
3176
- assertTrue(start % 2 === 0, 'Expecting even number.');
3225
+ isDev && assertTrue(start % 2 === 0, 'Expecting even number.');
3177
3226
  let bottom = start >> 1;
3178
3227
  let top = (array.length - 2) >> 1;
3179
3228
  while (bottom <= top) {
@@ -3220,6 +3269,127 @@ const mapArray_has = (array, key, start) => {
3220
3269
  return mapApp_findIndx(array, key, start) >= 0;
3221
3270
  };
3222
3271
 
3272
+ class DeleteOperation {
3273
+ target;
3274
+ constructor(target) {
3275
+ this.target = target;
3276
+ }
3277
+ }
3278
+ class RemoveAllChildrenOperation {
3279
+ target;
3280
+ constructor(target) {
3281
+ this.target = target;
3282
+ }
3283
+ }
3284
+ class SetTextOperation {
3285
+ target;
3286
+ text;
3287
+ operationType = 16 /* VNodeOperationType.SetText */;
3288
+ constructor(target, text) {
3289
+ this.target = target;
3290
+ this.text = text;
3291
+ }
3292
+ }
3293
+ class InsertOrMoveOperation {
3294
+ target;
3295
+ parent;
3296
+ beforeTarget;
3297
+ constructor(target, parent, beforeTarget) {
3298
+ this.target = target;
3299
+ this.parent = parent;
3300
+ this.beforeTarget = beforeTarget;
3301
+ }
3302
+ }
3303
+ class SetAttributeOperation {
3304
+ target;
3305
+ attrName;
3306
+ attrValue;
3307
+ scopedStyleIdPrefix;
3308
+ isSvg;
3309
+ constructor(target, attrName, attrValue, scopedStyleIdPrefix, isSvg) {
3310
+ this.target = target;
3311
+ this.attrName = attrName;
3312
+ this.attrValue = attrValue;
3313
+ this.scopedStyleIdPrefix = scopedStyleIdPrefix;
3314
+ this.isSvg = isSvg;
3315
+ }
3316
+ }
3317
+ /** Factory functions to create operations with consistent hidden classes. */
3318
+ const createDeleteOperation = (target) => new DeleteOperation(target);
3319
+ const createRemoveAllChildrenOperation = (target) => new RemoveAllChildrenOperation(target);
3320
+ const createSetTextOperation = (target, text) => new SetTextOperation(target, text);
3321
+ const createInsertOrMoveOperation = (target, parent, beforeTarget) => new InsertOrMoveOperation(target, parent, beforeTarget);
3322
+ const createSetAttributeOperation = (target, attrName, attrValue, scopedStyleIdPrefix = null, isSvg = false) => new SetAttributeOperation(target, attrName, attrValue, scopedStyleIdPrefix, isSvg);
3323
+
3324
+ function callQrl(container, host, qrl, event, element, useGetObjectById) {
3325
+ const getObjectById = useGetObjectById ? container?.$getObjectById$ || null : null;
3326
+ const singleItem = vnode_getProp(host, ITERATION_ITEM_SINGLE, getObjectById);
3327
+ if (singleItem !== null) {
3328
+ return qrl(event, element, singleItem);
3329
+ }
3330
+ const multiItems = vnode_getProp(host, ITERATION_ITEM_MULTI, getObjectById);
3331
+ if (multiItems !== null) {
3332
+ return qrl(event, element, ...multiItems);
3333
+ }
3334
+ return qrl(event, element);
3335
+ }
3336
+ /**
3337
+ * This is called by qwik-loader to run a QRL. It has to be synchronous.
3338
+ *
3339
+ * @internal
3340
+ */
3341
+ const _run = (...args) => {
3342
+ // This will already check container
3343
+ const [qrl] = useLexicalScope();
3344
+ const context = getInvokeContext();
3345
+ const hostElement = context.$hostElement$;
3346
+ if (hostElement) {
3347
+ context.$container$ ||= getDomContainer(hostElement.node);
3348
+ vnode_ensureElementInflated(hostElement);
3349
+ return retryOnPromise(() => {
3350
+ if (!(hostElement.flags & 32 /* VNodeFlags.Deleted */)) {
3351
+ return callQrl(context.$container$, hostElement, qrl, args[0], args[1], true).catch((err) => {
3352
+ const container = context.$container$;
3353
+ if (container) {
3354
+ container.handleError(err, hostElement);
3355
+ }
3356
+ else {
3357
+ throw err;
3358
+ }
3359
+ });
3360
+ }
3361
+ });
3362
+ }
3363
+ };
3364
+
3365
+ let _setAttribute = null;
3366
+ const fastSetAttribute = (target, name, value) => {
3367
+ if (!_setAttribute) {
3368
+ _setAttribute = target.setAttribute;
3369
+ }
3370
+ _setAttribute.call(target, name, value);
3371
+ };
3372
+ let _setAttributeNS = null;
3373
+ const fastSetAttributeNS = (target, namespace, name, value) => {
3374
+ if (!_setAttributeNS) {
3375
+ _setAttributeNS = target.setAttributeNS;
3376
+ }
3377
+ _setAttributeNS.call(target, namespace, name, value);
3378
+ };
3379
+ function directSetAttribute(element, attrName, attrValue, isSvg) {
3380
+ if (attrValue != null) {
3381
+ if (isSvg) {
3382
+ // only svg elements can have namespace attributes
3383
+ const namespace = getAttributeNamespace(attrName);
3384
+ if (namespace) {
3385
+ fastSetAttributeNS(element, namespace, attrName, attrValue);
3386
+ return;
3387
+ }
3388
+ }
3389
+ fastSetAttribute(element, attrName, attrValue);
3390
+ }
3391
+ }
3392
+
3223
3393
  /**
3224
3394
  * Helper to get the next sibling of a VNode. Extracted to module scope to help V8 inline it
3225
3395
  * reliably.
@@ -3229,9 +3399,12 @@ function peekNextSibling(vCurrent) {
3229
3399
  }
3230
3400
  const _hasOwnProperty = Object.prototype.hasOwnProperty;
3231
3401
  /** Helper to set an attribute on a vnode. Extracted to module scope to avoid closure allocation. */
3232
- function setAttribute(journal, vnode, key, value, scopedStyleIdPrefix) {
3233
- const serializedValue = value != null ? serializeAttribute(key, value, scopedStyleIdPrefix) : null;
3234
- vnode_setAttr(journal, vnode, key, serializedValue);
3402
+ function setAttribute(journal, vnode, key, value, scopedStyleIdPrefix, originalValue) {
3403
+ import.meta.env.TEST &&
3404
+ scopedStyleIdPrefix &&
3405
+ vnode_setProp(vnode, debugStyleScopeIdPrefixAttr, scopedStyleIdPrefix);
3406
+ vnode_setProp(vnode, key, originalValue);
3407
+ addVNodeOperation(journal, createSetAttributeOperation(vnode.node, key, value, scopedStyleIdPrefix, (vnode.flags & 128 /* VNodeFlags.NS_svg */) !== 0));
3235
3408
  }
3236
3409
  const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyleIdPrefix) => {
3237
3410
  const diffContext = {
@@ -3253,6 +3426,7 @@ const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyle
3253
3426
  jsxIdx: 0,
3254
3427
  jsxCount: 0,
3255
3428
  shouldAdvance: true,
3429
+ isCreationMode: false,
3256
3430
  subscriptionData: {
3257
3431
  const: new SubscriptionData({
3258
3432
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
@@ -3281,8 +3455,8 @@ const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyle
3281
3455
  //////////////////////////////////////////////
3282
3456
  //////////////////////////////////////////////
3283
3457
  function diff(diffContext, jsxNode, vStartNode) {
3284
- assertFalse(vnode_isVNode(jsxNode), 'JSXNode should not be a VNode');
3285
- assertTrue(vnode_isVNode(vStartNode), 'vStartNode should be a VNode');
3458
+ isDev && assertFalse(vnode_isVNode(jsxNode), 'JSXNode should not be a VNode');
3459
+ isDev && assertTrue(vnode_isVNode(vStartNode), 'vStartNode should be a VNode');
3286
3460
  diffContext.vParent = vStartNode;
3287
3461
  diffContext.vNewNode = null;
3288
3462
  diffContext.vCurrent = vnode_getFirstChild(vStartNode);
@@ -3293,7 +3467,8 @@ function diff(diffContext, jsxNode, vStartNode) {
3293
3467
  }
3294
3468
  while (diffContext.stack.length) {
3295
3469
  while (diffContext.jsxIdx < diffContext.jsxCount) {
3296
- assertFalse(diffContext.vParent === diffContext.vCurrent, "Parent and current can't be the same");
3470
+ isDev &&
3471
+ assertFalse(diffContext.vParent === diffContext.vCurrent, "Parent and current can't be the same");
3297
3472
  if (typeof diffContext.jsxValue === 'string') {
3298
3473
  expectText(diffContext, diffContext.jsxValue);
3299
3474
  }
@@ -3301,26 +3476,7 @@ function diff(diffContext, jsxNode, vStartNode) {
3301
3476
  expectText(diffContext, String(diffContext.jsxValue));
3302
3477
  }
3303
3478
  else if (diffContext.jsxValue && typeof diffContext.jsxValue === 'object') {
3304
- if (Array.isArray(diffContext.jsxValue)) {
3305
- descend(diffContext, diffContext.jsxValue, false);
3306
- }
3307
- else if (isSignal(diffContext.jsxValue)) {
3308
- expectVirtual(diffContext, "S" /* VirtualType.WrappedSignal */, null);
3309
- const unwrappedSignal = diffContext.jsxValue instanceof WrappedSignalImpl
3310
- ? diffContext.jsxValue.$unwrapIfSignal$()
3311
- : diffContext.jsxValue;
3312
- const hasUnwrappedSignal = diffContext.vCurrent?.[_EFFECT_BACK_REF]
3313
- ?.get("." /* EffectProperty.VNODE */)?.[2 /* EffectSubscriptionProp.BACK_REF */]?.has(unwrappedSignal);
3314
- if (!hasUnwrappedSignal) {
3315
- const vHost = (diffContext.vNewNode || diffContext.vCurrent);
3316
- descend(diffContext, resolveSignalAndDescend(diffContext, () => trackSignalAndAssignHost(unwrappedSignal, vHost, "." /* EffectProperty.VNODE */, diffContext.container)), true);
3317
- }
3318
- }
3319
- else if (isPromise(diffContext.jsxValue)) {
3320
- expectVirtual(diffContext, "A" /* VirtualType.Awaited */, null);
3321
- diffContext.asyncQueue.push(diffContext.jsxValue, diffContext.vNewNode || diffContext.vCurrent);
3322
- }
3323
- else if (isJSXNode(diffContext.jsxValue)) {
3479
+ if (isJSXNode(diffContext.jsxValue)) {
3324
3480
  const type = diffContext.jsxValue.type;
3325
3481
  if (typeof type === 'string') {
3326
3482
  expectNoMoreTextNodes(diffContext);
@@ -3368,6 +3524,28 @@ function diff(diffContext, jsxNode, vStartNode) {
3368
3524
  }
3369
3525
  }
3370
3526
  }
3527
+ else if (Array.isArray(diffContext.jsxValue)) {
3528
+ descend(diffContext, diffContext.jsxValue, false);
3529
+ }
3530
+ else if (isSignal(diffContext.jsxValue)) {
3531
+ expectVirtual(diffContext, "S" /* VirtualType.WrappedSignal */, null);
3532
+ const unwrappedSignal = diffContext.jsxValue instanceof WrappedSignalImpl
3533
+ ? diffContext.jsxValue.$unwrapIfSignal$()
3534
+ : diffContext.jsxValue;
3535
+ const signals = diffContext.vCurrent?.[_EFFECT_BACK_REF]?.get("." /* EffectProperty.VNODE */)?.backRef;
3536
+ let hasUnwrappedSignal = signals?.has(unwrappedSignal);
3537
+ if (signals && unwrappedSignal instanceof WrappedSignalImpl) {
3538
+ hasUnwrappedSignal = containsWrappedSignal(signals, unwrappedSignal);
3539
+ }
3540
+ if (!hasUnwrappedSignal) {
3541
+ const vHost = (diffContext.vNewNode || diffContext.vCurrent);
3542
+ descend(diffContext, resolveSignalAndDescend(diffContext, () => trackSignalAndAssignHost(unwrappedSignal, vHost, "." /* EffectProperty.VNODE */, diffContext.container)), true);
3543
+ }
3544
+ }
3545
+ else if (isPromise(diffContext.jsxValue)) {
3546
+ expectVirtual(diffContext, "A" /* VirtualType.Awaited */, null);
3547
+ diffContext.asyncQueue.push(diffContext.jsxValue, diffContext.vNewNode || diffContext.vCurrent);
3548
+ }
3371
3549
  }
3372
3550
  else if (diffContext.jsxValue === SkipRender) ;
3373
3551
  else {
@@ -3447,7 +3625,12 @@ function descend(diffContext, children, descendVNode, shouldExpectNoChildren = t
3447
3625
  }
3448
3626
  stackPush(diffContext, children, descendVNode);
3449
3627
  if (descendVNode) {
3450
- assertDefined(diffContext.vCurrent || diffContext.vNewNode, 'Expecting vCurrent to be defined.');
3628
+ isDev &&
3629
+ assertDefined(diffContext.vCurrent || diffContext.vNewNode, 'Expecting vCurrent to be defined.');
3630
+ const creationMode = diffContext.isCreationMode ||
3631
+ !!diffContext.vNewNode ||
3632
+ !vnode_getFirstChild(diffContext.vCurrent);
3633
+ diffContext.isCreationMode = creationMode;
3451
3634
  diffContext.vSideBuffer = null;
3452
3635
  diffContext.vSiblings = null;
3453
3636
  diffContext.vSiblingsArray = null;
@@ -3460,6 +3643,7 @@ function descend(diffContext, children, descendVNode, shouldExpectNoChildren = t
3460
3643
  function ascend(diffContext) {
3461
3644
  const descendVNode = diffContext.stack.pop(); // boolean: descendVNode
3462
3645
  if (descendVNode) {
3646
+ diffContext.isCreationMode = diffContext.stack.pop();
3463
3647
  diffContext.vSideBuffer = diffContext.stack.pop();
3464
3648
  diffContext.vSiblings = diffContext.stack.pop();
3465
3649
  diffContext.vSiblingsArray = diffContext.stack.pop();
@@ -3476,7 +3660,7 @@ function ascend(diffContext) {
3476
3660
  function stackPush(diffContext, children, descendVNode) {
3477
3661
  diffContext.stack.push(diffContext.jsxChildren, diffContext.jsxIdx, diffContext.jsxCount, diffContext.jsxValue);
3478
3662
  if (descendVNode) {
3479
- diffContext.stack.push(diffContext.vParent, diffContext.vCurrent, diffContext.vNewNode, diffContext.vSiblingsArray, diffContext.vSiblings, diffContext.vSideBuffer);
3663
+ diffContext.stack.push(diffContext.vParent, diffContext.vCurrent, diffContext.vNewNode, diffContext.vSiblingsArray, diffContext.vSiblings, diffContext.vSideBuffer, diffContext.isCreationMode);
3480
3664
  }
3481
3665
  diffContext.stack.push(descendVNode);
3482
3666
  if (Array.isArray(children)) {
@@ -3604,12 +3788,20 @@ function expectSlot(diffContext) {
3604
3788
  else if (vProjectedNode === diffContext.vCurrent) ;
3605
3789
  else {
3606
3790
  // move from q:template to the target node
3791
+ const oldParent = vProjectedNode.parent;
3607
3792
  vnode_insertBefore(diffContext.journal, diffContext.vParent, (diffContext.vNewNode = vProjectedNode), diffContext.vCurrent && getInsertBefore(diffContext));
3608
3793
  vnode_setProp(diffContext.vNewNode, QSlot, slotNameKey);
3609
3794
  vHost && vnode_setProp(vHost, slotNameKey, diffContext.vNewNode);
3610
3795
  isDev &&
3611
3796
  vnode_setProp(diffContext.vNewNode, DEBUG_TYPE, "P" /* VirtualType.Projection */);
3612
3797
  isDev && vnode_setProp(diffContext.vNewNode, 'q:code', 'expectSlot' + count++);
3798
+ // If we moved from a q:template and it's now empty, remove it
3799
+ if (oldParent &&
3800
+ vnode_isElementVNode(oldParent) &&
3801
+ !oldParent.firstChild &&
3802
+ vnode_getElementName(oldParent) === QTemplate) {
3803
+ vnode_remove(diffContext.journal, oldParent.parent, oldParent, true);
3804
+ }
3613
3805
  }
3614
3806
  return true;
3615
3807
  }
@@ -3684,7 +3876,8 @@ function expectNoChildren(diffContext, removeDOM = true) {
3684
3876
  }
3685
3877
  /** Expect no more nodes - Any nodes which are still at cursor, need to be removed. */
3686
3878
  function expectNoMore(diffContext) {
3687
- assertFalse(diffContext.vParent === diffContext.vCurrent, "Parent and current can't be the same");
3879
+ isDev &&
3880
+ assertFalse(diffContext.vParent === diffContext.vCurrent, "Parent and current can't be the same");
3688
3881
  if (diffContext.vCurrent !== null) {
3689
3882
  while (diffContext.vCurrent) {
3690
3883
  const toRemove = diffContext.vCurrent;
@@ -3714,20 +3907,6 @@ function expectNoMoreTextNodes(diffContext) {
3714
3907
  */
3715
3908
  function createNewElement(diffContext, jsx, elementName, currentFile) {
3716
3909
  const element = createElementWithNamespace(diffContext, elementName);
3717
- function setAttribute(key, value, vHost) {
3718
- value = serializeAttribute(key, value, diffContext.scopedStyleIdPrefix);
3719
- if (value != null) {
3720
- if (vHost.flags & 128 /* VNodeFlags.NS_svg */) {
3721
- // only svg elements can have namespace attributes
3722
- const namespace = getAttributeNamespace(key);
3723
- if (namespace) {
3724
- element.setAttributeNS(namespace, key, value);
3725
- return;
3726
- }
3727
- }
3728
- element.setAttribute(key, value);
3729
- }
3730
- }
3731
3910
  const { constProps } = jsx;
3732
3911
  let needsQDispatchEventPatch = false;
3733
3912
  if (constProps) {
@@ -3778,7 +3957,7 @@ function createNewElement(diffContext, jsx, elementName, currentFile) {
3778
3957
  }
3779
3958
  if (isPromise(value)) {
3780
3959
  const vHost = diffContext.vNewNode;
3781
- const attributePromise = value.then((resolvedValue) => setAttribute(key, resolvedValue, vHost));
3960
+ const attributePromise = value.then((resolvedValue) => directSetAttribute(element, key, serializeAttribute(key, resolvedValue, diffContext.scopedStyleIdPrefix), (vHost.flags & 128 /* VNodeFlags.NS_svg */) !== 0));
3782
3961
  diffContext.asyncAttributePromises.push(attributePromise);
3783
3962
  continue;
3784
3963
  }
@@ -3799,7 +3978,7 @@ function createNewElement(diffContext, jsx, elementName, currentFile) {
3799
3978
  element.value = escapeHTML(value || '');
3800
3979
  continue;
3801
3980
  }
3802
- setAttribute(key, value, diffContext.vNewNode);
3981
+ directSetAttribute(element, key, serializeAttribute(key, value, diffContext.scopedStyleIdPrefix), (diffContext.vNewNode.flags & 128 /* VNodeFlags.NS_svg */) !== 0);
3803
3982
  }
3804
3983
  }
3805
3984
  const key = jsx.key;
@@ -3808,7 +3987,8 @@ function createNewElement(diffContext, jsx, elementName, currentFile) {
3808
3987
  }
3809
3988
  // append class attribute if styleScopedId exists and there is no class attribute
3810
3989
  if (diffContext.scopedStyleIdPrefix) {
3811
- const classAttributeExists = hasClassAttr(jsx.varProps) || (jsx.constProps && hasClassAttr(jsx.constProps));
3990
+ const classAttributeExists = _hasOwnProperty.call(jsx.varProps, 'class') ||
3991
+ (jsx.constProps && _hasOwnProperty.call(jsx.constProps, 'class'));
3812
3992
  if (!classAttributeExists) {
3813
3993
  element.setAttribute('class', diffContext.scopedStyleIdPrefix);
3814
3994
  }
@@ -3818,28 +3998,35 @@ function createNewElement(diffContext, jsx, elementName, currentFile) {
3818
3998
  }
3819
3999
  function createElementWithNamespace(diffContext, elementName) {
3820
4000
  const domParentVNode = vnode_getDomParentVNode(diffContext.vParent, true);
3821
- const { elementNamespace, elementNamespaceFlag } = getNewElementNamespaceData(domParentVNode, elementName);
3822
- const element = diffContext.container.document.createElementNS(elementNamespace, elementName);
4001
+ const namespaceData = getNewElementNamespaceData(domParentVNode, elementName);
4002
+ const currentDocument = import.meta.env.TEST ? diffContext.container.document : document;
4003
+ const element = namespaceData.elementNamespaceFlag === 0 /* VNodeFlags.NS_html */
4004
+ ? currentDocument.createElement(elementName)
4005
+ : currentDocument.createElementNS(namespaceData.elementNamespace, elementName);
3823
4006
  diffContext.vNewNode = vnode_newElement(element, elementName);
3824
- diffContext.vNewNode.flags |= elementNamespaceFlag;
4007
+ diffContext.vNewNode.flags |= namespaceData.elementNamespaceFlag;
3825
4008
  return element;
3826
4009
  }
3827
4010
  function expectElement(diffContext, jsx, elementName) {
3828
- const isSameElementName = diffContext.vCurrent &&
3829
- vnode_isElementVNode(diffContext.vCurrent) &&
3830
- elementName === vnode_getElementName(diffContext.vCurrent);
3831
- const jsxKey = jsx.key;
3832
4011
  let needsQDispatchEventPatch = false;
3833
- const currentKey = getKey(diffContext.vCurrent);
3834
- if (!isSameElementName || jsxKey !== currentKey) {
3835
- const sideBufferKey = getSideBufferKey(elementName, jsxKey);
3836
- if (moveOrCreateKeyedNode(diffContext, elementName, jsxKey, sideBufferKey, diffContext.vParent)) {
3837
- needsQDispatchEventPatch = createNewElement(diffContext, jsx, elementName, null);
3838
- }
4012
+ if (diffContext.isCreationMode) {
4013
+ needsQDispatchEventPatch = createNewElement(diffContext, jsx, elementName, null);
3839
4014
  }
3840
4015
  else {
3841
- // delete the key from the side buffer if it is the same element
3842
- deleteFromSideBuffer(diffContext, elementName, jsxKey);
4016
+ const isElementVNode = diffContext.vCurrent && vnode_isElementVNode(diffContext.vCurrent);
4017
+ const isSameElementName = isElementVNode && elementName === vnode_getElementName(diffContext.vCurrent);
4018
+ const jsxKey = jsx.key;
4019
+ const currentKey = isElementVNode && diffContext.vCurrent.key;
4020
+ if (!isSameElementName || jsxKey !== currentKey) {
4021
+ const sideBufferKey = getSideBufferKey(elementName, jsxKey);
4022
+ if (moveOrCreateKeyedNode(diffContext, elementName, jsxKey, sideBufferKey, diffContext.vParent)) {
4023
+ needsQDispatchEventPatch = createNewElement(diffContext, jsx, elementName, null);
4024
+ }
4025
+ }
4026
+ else {
4027
+ // delete the key from the side buffer if it is the same element
4028
+ deleteFromSideBuffer(diffContext, elementName, jsxKey);
4029
+ }
3843
4030
  }
3844
4031
  // reconcile attributes
3845
4032
  const jsxProps = jsx.varProps;
@@ -3847,7 +4034,8 @@ function expectElement(diffContext, jsx, elementName) {
3847
4034
  const element = vNode.node;
3848
4035
  if (jsxProps) {
3849
4036
  needsQDispatchEventPatch =
3850
- diffProps(diffContext, vNode, jsxProps, (vNode.props ||= {}), (isDev && getFileLocationFromJsx(jsx.dev)) || null) || needsQDispatchEventPatch;
4037
+ diffProps(diffContext, vNode, jsxProps, (isDev && getFileLocationFromJsx(jsx.dev)) || null) ||
4038
+ needsQDispatchEventPatch;
3851
4039
  }
3852
4040
  if (needsQDispatchEventPatch) {
3853
4041
  // Event handler needs to be patched onto the element.
@@ -3864,7 +4052,7 @@ function expectElement(diffContext, jsx, elementName) {
3864
4052
  ];
3865
4053
  for (const qrl of qrls.flat(2)) {
3866
4054
  if (qrl) {
3867
- catchError(qrl(event, element), (e) => {
4055
+ callQrl(diffContext.container, vNode, qrl, event, vNode.node, false).catch((e) => {
3868
4056
  diffContext.container.handleError(e, vNode);
3869
4057
  });
3870
4058
  }
@@ -3873,92 +4061,127 @@ function expectElement(diffContext, jsx, elementName) {
3873
4061
  }
3874
4062
  }
3875
4063
  }
3876
- function diffProps(diffContext, vnode, newAttrs, oldAttrs, currentFile) {
3877
- vnode_ensureElementInflated(vnode);
4064
+ function diffProps(diffContext, vnode, newAttrs, currentFile) {
4065
+ if (!diffContext.isCreationMode) {
4066
+ // inflate only resumed vnodes
4067
+ vnode_ensureElementInflated(vnode);
4068
+ }
4069
+ const oldAttrs = vnode.props;
3878
4070
  let patchEventDispatch = false;
3879
- const record = (key, value) => {
3880
- if (key.startsWith(':')) {
3881
- // TODO: there is a potential deoptimization here, because we are setting different keys on props.
3882
- // Eager bailout - Insufficient type feedback for generic keyed access
3883
- vnode_setProp(vnode, key, value);
3884
- return;
3885
- }
3886
- if (key === 'ref') {
3887
- const element = vnode.node;
3888
- if (isSignal(value)) {
3889
- value.value = element;
3890
- return;
4071
+ // Actual diffing logic
4072
+ // Apply all new attributes
4073
+ for (const key in newAttrs) {
4074
+ const newValue = newAttrs[key];
4075
+ const isEvent = isHtmlAttributeAnEventName(key);
4076
+ if (oldAttrs && _hasOwnProperty.call(oldAttrs, key)) {
4077
+ const oldValue = oldAttrs[key];
4078
+ if (newValue !== oldValue) {
4079
+ if (newValue instanceof WrappedSignalImpl &&
4080
+ oldValue instanceof WrappedSignalImpl &&
4081
+ areWrappedSignalsEqual(newValue, oldValue)) {
4082
+ continue;
4083
+ }
4084
+ if (isEvent) {
4085
+ const result = recordJsxEvent(diffContext, vnode, key, newValue, currentFile);
4086
+ patchEventDispatch ||= result;
4087
+ }
4088
+ else {
4089
+ patchProperty(diffContext, vnode, key, newValue, currentFile);
4090
+ }
3891
4091
  }
3892
- else if (typeof value === 'function') {
3893
- value(element);
3894
- return;
4092
+ }
4093
+ else if (newValue != null) {
4094
+ if (isEvent) {
4095
+ const result = recordJsxEvent(diffContext, vnode, key, newValue, currentFile);
4096
+ patchEventDispatch ||= result;
3895
4097
  }
3896
4098
  else {
3897
- throw qError(15 /* QError.invalidRefValue */, [currentFile]);
4099
+ patchProperty(diffContext, vnode, key, newValue, currentFile);
3898
4100
  }
3899
4101
  }
3900
- const currentEffect = vnode[_EFFECT_BACK_REF]?.get(key);
3901
- if (isSignal(value)) {
3902
- const unwrappedSignal = value instanceof WrappedSignalImpl ? value.$unwrapIfSignal$() : value;
3903
- if (currentEffect?.[2 /* EffectSubscriptionProp.BACK_REF */]?.has(unwrappedSignal)) {
3904
- return;
3905
- }
3906
- if (currentEffect) {
3907
- clearEffectSubscription(diffContext.container, currentEffect);
4102
+ }
4103
+ if (oldAttrs) {
4104
+ // Remove attributes that no longer exist in new props
4105
+ for (const key in oldAttrs) {
4106
+ if (!_hasOwnProperty.call(newAttrs, key) &&
4107
+ !key.startsWith(HANDLER_PREFIX) &&
4108
+ !isHtmlAttributeAnEventName(key)) {
4109
+ patchProperty(diffContext, vnode, key, null, currentFile);
3908
4110
  }
3909
- const vHost = vnode;
3910
- value = retryOnPromise(() => trackSignalAndAssignHost(unwrappedSignal, vHost, key, diffContext.container, diffContext.subscriptionData.var));
3911
4111
  }
3912
- else {
3913
- if (currentEffect) {
3914
- clearEffectSubscription(diffContext.container, currentEffect);
3915
- }
4112
+ }
4113
+ return patchEventDispatch;
4114
+ }
4115
+ const patchProperty = (diffContext, vnode, key, value, currentFile) => {
4116
+ if (
4117
+ // set only property for iteration item, not an attribute
4118
+ key === ITERATION_ITEM_SINGLE ||
4119
+ key === ITERATION_ITEM_MULTI ||
4120
+ key.charAt(0) === HANDLER_PREFIX) {
4121
+ // TODO: there is a potential deoptimization here, because we are setting different keys on props.
4122
+ // Eager bailout - Insufficient type feedback for generic keyed access
4123
+ vnode_setProp(vnode, key, value);
4124
+ return;
4125
+ }
4126
+ const originalValue = value;
4127
+ if (key === 'ref') {
4128
+ const element = vnode.node;
4129
+ if (isSignal(value)) {
4130
+ value.value = element;
4131
+ return;
3916
4132
  }
3917
- if (isPromise(value)) {
3918
- const vHost = vnode;
3919
- const attributePromise = value.then((resolvedValue) => {
3920
- setAttribute(diffContext.journal, vHost, key, resolvedValue, diffContext.scopedStyleIdPrefix);
3921
- });
3922
- diffContext.asyncAttributePromises.push(attributePromise);
4133
+ else if (typeof value === 'function') {
4134
+ value(element);
3923
4135
  return;
3924
4136
  }
3925
- setAttribute(diffContext.journal, vnode, key, value, diffContext.scopedStyleIdPrefix);
3926
- };
3927
- const recordJsxEvent = (key, value) => {
3928
- const data = getEventDataFromHtmlAttribute(key);
3929
- if (data) {
3930
- const [scope, eventName] = data;
3931
- const scopedEvent = getScopedEventName(scope, eventName);
3932
- const loaderScopedEvent = getLoaderScopedEventName(scope, scopedEvent);
3933
- record(':' + scopedEvent, value);
3934
- registerQwikLoaderEvent(diffContext, loaderScopedEvent);
3935
- patchEventDispatch = true;
4137
+ else {
4138
+ throw qError(15 /* QError.invalidRefValue */, [currentFile]);
3936
4139
  }
3937
- };
3938
- // Actual diffing logic
3939
- // Apply all new attributes
3940
- for (const key of Object.keys(newAttrs)) {
3941
- const newValue = newAttrs[key];
3942
- const isEvent = isHtmlAttributeAnEventName(key);
3943
- if (_hasOwnProperty.call(oldAttrs, key)) {
3944
- if (newValue !== oldAttrs[key]) {
3945
- isEvent ? recordJsxEvent(key, newValue) : record(key, newValue);
3946
- }
4140
+ }
4141
+ const currentEffect = vnode[_EFFECT_BACK_REF]?.get(key);
4142
+ if (isSignal(value)) {
4143
+ const unwrappedSignal = value instanceof WrappedSignalImpl ? value.$unwrapIfSignal$() : value;
4144
+ if (currentEffect?.backRef?.has(unwrappedSignal)) {
4145
+ return;
3947
4146
  }
3948
- else if (newValue != null) {
3949
- isEvent ? recordJsxEvent(key, newValue) : record(key, newValue);
4147
+ if (currentEffect) {
4148
+ clearEffectSubscription(diffContext.container, currentEffect);
4149
+ }
4150
+ const vHost = vnode;
4151
+ value = retryOnPromise(() => trackSignalAndAssignHost(unwrappedSignal, vHost, key, diffContext.container, diffContext.subscriptionData.var));
4152
+ }
4153
+ else {
4154
+ if (currentEffect) {
4155
+ clearEffectSubscription(diffContext.container, currentEffect);
3950
4156
  }
3951
4157
  }
3952
- // Remove attributes that no longer exist in new props
3953
- for (const key of Object.keys(oldAttrs)) {
3954
- if (!_hasOwnProperty.call(newAttrs, key) &&
3955
- !key.startsWith(HANDLER_PREFIX) &&
3956
- !isHtmlAttributeAnEventName(key)) {
3957
- record(key, null);
4158
+ if (isPromise(value)) {
4159
+ const vHost = vnode;
4160
+ const attributePromise = value.then((resolvedValue) => {
4161
+ setAttribute(diffContext.journal, vHost, key, resolvedValue, diffContext.scopedStyleIdPrefix, originalValue);
4162
+ });
4163
+ diffContext.asyncAttributePromises.push(attributePromise);
4164
+ return;
4165
+ }
4166
+ setAttribute(diffContext.journal, vnode, key, value, diffContext.scopedStyleIdPrefix, originalValue);
4167
+ };
4168
+ const recordJsxEvent = (diffContext, vnode, key, value, currentFile) => {
4169
+ const data = getEventDataFromHtmlAttribute(key);
4170
+ if (data) {
4171
+ const props = vnode.props;
4172
+ const [scope, eventName] = data;
4173
+ const scopedEvent = getScopedEventName(scope, eventName);
4174
+ const loaderScopedEvent = getLoaderScopedEventName(scope, scopedEvent);
4175
+ const scopedEventKey = ':' + scopedEvent;
4176
+ if (props && _hasOwnProperty.call(props, scopedEventKey)) {
4177
+ return false;
3958
4178
  }
4179
+ patchProperty(diffContext, vnode, scopedEventKey, value, currentFile);
4180
+ registerQwikLoaderEvent(diffContext, loaderScopedEvent);
4181
+ return true;
3959
4182
  }
3960
- return patchEventDispatch;
3961
- }
4183
+ return false;
4184
+ };
3962
4185
  function registerQwikLoaderEvent(diffContext, eventName) {
3963
4186
  const qWindow = import.meta.env.TEST
3964
4187
  ? diffContext.container.document.defaultView
@@ -3970,6 +4193,16 @@ function registerQwikLoaderEvent(diffContext, eventName) {
3970
4193
  function retrieveChildWithKey(diffContext, nodeName, key) {
3971
4194
  let vNodeWithKey = null;
3972
4195
  if (diffContext.vSiblings === null) {
4196
+ // check if the current node is the one we are looking for
4197
+ const vCurrent = diffContext.vCurrent;
4198
+ if (vCurrent) {
4199
+ const name = vnode_isElementVNode(vCurrent) ? vnode_getElementName(vCurrent) : null;
4200
+ const vKey = getKey(vCurrent) ||
4201
+ getComponentHash(vCurrent, diffContext.container.$getObjectById$);
4202
+ if (vKey === key && name === nodeName) {
4203
+ return vCurrent;
4204
+ }
4205
+ }
3973
4206
  // it is not materialized; so materialize it.
3974
4207
  diffContext.vSiblings = new Map();
3975
4208
  diffContext.vSiblingsArray = [];
@@ -4005,8 +4238,9 @@ function retrieveChildWithKey(diffContext, nodeName, key) {
4005
4238
  }
4006
4239
  else {
4007
4240
  const siblingsKey = getSideBufferKey(nodeName, key);
4008
- if (diffContext.vSiblings.has(siblingsKey)) {
4009
- vNodeWithKey = diffContext.vSiblings.get(siblingsKey);
4241
+ const sibling = diffContext.vSiblings.get(siblingsKey);
4242
+ if (sibling) {
4243
+ vNodeWithKey = sibling;
4010
4244
  diffContext.vSiblings.delete(siblingsKey);
4011
4245
  }
4012
4246
  }
@@ -4075,6 +4309,7 @@ function moveOrCreateKeyedNode(diffContext, nodeName, lookupKey, sideBufferKey,
4075
4309
  // 1) Try to find the node among upcoming siblings
4076
4310
  diffContext.vNewNode = retrieveChildWithKey(diffContext, nodeName, lookupKey);
4077
4311
  if (diffContext.vNewNode) {
4312
+ vnode_insertBefore(diffContext.journal, parentForInsert, diffContext.vNewNode, diffContext.vCurrent);
4078
4313
  diffContext.vCurrent = diffContext.vNewNode;
4079
4314
  diffContext.vNewNode = null;
4080
4315
  return false;
@@ -4118,7 +4353,7 @@ function expectVirtual(diffContext, type, jsxKey) {
4118
4353
  return;
4119
4354
  }
4120
4355
  // For fragments without a key, always create a new virtual node (ensures rerender semantics)
4121
- if (jsxKey === null) {
4356
+ if (jsxKey === null || diffContext.isCreationMode) {
4122
4357
  vnode_insertBefore(diffContext.journal, diffContext.vParent, (diffContext.vNewNode = vnode_newVirtual()), diffContext.vCurrent && getInsertBefore(diffContext));
4123
4358
  diffContext.vNewNode.key = jsxKey;
4124
4359
  isDev && vnode_setProp(diffContext.vNewNode, DEBUG_TYPE, type);
@@ -4247,7 +4482,7 @@ function expectText(diffContext, text) {
4247
4482
  return;
4248
4483
  }
4249
4484
  }
4250
- vnode_insertBefore(diffContext.journal, diffContext.vParent, (diffContext.vNewNode = vnode_newText(diffContext.container.document.createTextNode(text), text)), diffContext.vCurrent);
4485
+ vnode_insertBefore(diffContext.journal, diffContext.vParent, (diffContext.vNewNode = vnode_newText((import.meta.env.TEST ? diffContext.container.document : document).createTextNode(text), text)), diffContext.vCurrent);
4251
4486
  }
4252
4487
  /**
4253
4488
  * Retrieve the key from the VNode.
@@ -4304,13 +4539,10 @@ function Projection() { }
4304
4539
  function handleProps(host, jsxProps, vNodeProps, container) {
4305
4540
  let shouldRender = false;
4306
4541
  if (vNodeProps) {
4307
- const effects = vNodeProps[_PROPS_HANDLER].$effects$;
4308
4542
  const constPropsDifferent = handleChangedProps(jsxProps[_CONST_PROPS], vNodeProps[_CONST_PROPS], vNodeProps[_PROPS_HANDLER], container, false);
4309
4543
  shouldRender ||= constPropsDifferent;
4310
- if (effects && effects.size > 0) {
4311
- handleChangedProps(jsxProps[_VAR_PROPS], vNodeProps[_VAR_PROPS], vNodeProps[_PROPS_HANDLER], container, true);
4312
- // don't mark as should render, effects will take care of it
4313
- }
4544
+ const varPropsDifferent = handleChangedProps(jsxProps[_VAR_PROPS], vNodeProps[_VAR_PROPS], vNodeProps[_PROPS_HANDLER], container, true);
4545
+ shouldRender ||= varPropsDifferent;
4314
4546
  // Update the owner after all props have been synced
4315
4547
  vNodeProps[_OWNER] = jsxProps[_OWNER];
4316
4548
  }
@@ -4335,7 +4567,6 @@ function handleChangedProps(src, dst, propsHandler, container, triggerEffects =
4335
4567
  continue;
4336
4568
  }
4337
4569
  if (!dst || src[key] !== dst[key]) {
4338
- changed = true;
4339
4570
  if (triggerEffects) {
4340
4571
  if (dst) {
4341
4572
  // Update the value in dst BEFORE triggering effects
@@ -4343,7 +4574,11 @@ function handleChangedProps(src, dst, propsHandler, container, triggerEffects =
4343
4574
  // Note: Value is not triggering effects, because we are modyfing direct VAR_PROPS object
4344
4575
  dst[key] = src[key];
4345
4576
  }
4346
- triggerPropsProxyEffect(propsHandler, key);
4577
+ const didTigger = triggerPropsProxyEffect(propsHandler, key);
4578
+ if (!didTigger) {
4579
+ // If the effect was not triggered, then the prop has changed and we should rerender
4580
+ changed = true;
4581
+ }
4347
4582
  }
4348
4583
  else {
4349
4584
  // Early return for const props (no effects)
@@ -4359,10 +4594,13 @@ function handleChangedProps(src, dst, propsHandler, container, triggerEffects =
4359
4594
  continue;
4360
4595
  }
4361
4596
  if (!src || !_hasOwnProperty.call(src, key)) {
4362
- changed = true;
4363
4597
  if (triggerEffects) {
4364
4598
  delete dst[key];
4365
- triggerPropsProxyEffect(propsHandler, key);
4599
+ const didTigger = triggerPropsProxyEffect(propsHandler, key);
4600
+ if (!didTigger) {
4601
+ // If the effect was not triggered, then the prop has changed and we should rerender
4602
+ changed = true;
4603
+ }
4366
4604
  }
4367
4605
  }
4368
4606
  }
@@ -4535,6 +4773,37 @@ function markVNodeAsDeleted(vCursor) {
4535
4773
  */
4536
4774
  vCursor.flags |= 32 /* VNodeFlags.Deleted */;
4537
4775
  }
4776
+ function areWrappedSignalsEqual(oldSignal, newSignal) {
4777
+ if (oldSignal === newSignal) {
4778
+ return true;
4779
+ }
4780
+ return (newSignal.$func$ === oldSignal.$func$ && areArgumentsEqual(newSignal.$args$, oldSignal.$args$));
4781
+ }
4782
+ function areArgumentsEqual(oldArgs, newArgs) {
4783
+ if (oldArgs === newArgs) {
4784
+ return true;
4785
+ }
4786
+ if (!oldArgs || !newArgs || oldArgs.length !== newArgs.length) {
4787
+ return false;
4788
+ }
4789
+ for (let i = 0; i < oldArgs.length; i++) {
4790
+ if (oldArgs[i] !== newArgs[i]) {
4791
+ return false;
4792
+ }
4793
+ }
4794
+ return true;
4795
+ }
4796
+ function containsWrappedSignal(data, signal) {
4797
+ if (!(signal instanceof WrappedSignalImpl)) {
4798
+ return false;
4799
+ }
4800
+ for (const item of data) {
4801
+ if (item instanceof WrappedSignalImpl && areWrappedSignalsEqual(item, signal)) {
4802
+ return true;
4803
+ }
4804
+ }
4805
+ return false;
4806
+ }
4538
4807
  /**
4539
4808
  * This marks the property as immutable. It is needed for the QRLs so that QwikLoader can get a hold
4540
4809
  * of them. This character must be `:` so that the `vnode_getAttr` can ignore them.
@@ -4542,6 +4811,41 @@ function markVNodeAsDeleted(vCursor) {
4542
4811
  const HANDLER_PREFIX = ':';
4543
4812
  let count = 0;
4544
4813
 
4814
+ /**
4815
+ * @internal
4816
+ * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
4817
+ */
4818
+ const useSequentialScope = () => {
4819
+ const iCtx = useInvokeContext();
4820
+ const hostElement = iCtx.$hostElement$;
4821
+ const host = hostElement;
4822
+ let seq = iCtx.$container$.getHostProp(host, ELEMENT_SEQ);
4823
+ if (seq === null) {
4824
+ seq = [];
4825
+ iCtx.$container$.setHostProp(host, ELEMENT_SEQ, seq);
4826
+ }
4827
+ let seqIdx = iCtx.$container$.getHostProp(host, ELEMENT_SEQ_IDX);
4828
+ if (seqIdx === null) {
4829
+ seqIdx = 0;
4830
+ }
4831
+ iCtx.$container$.setHostProp(host, ELEMENT_SEQ_IDX, seqIdx + 1);
4832
+ while (seq.length <= seqIdx) {
4833
+ seq.push(undefined);
4834
+ }
4835
+ const set = (value) => {
4836
+ if (qDev && qSerialize) {
4837
+ verifySerializable(value);
4838
+ }
4839
+ return (seq[seqIdx] = value);
4840
+ };
4841
+ return {
4842
+ val: seq[seqIdx],
4843
+ set,
4844
+ i: seqIdx,
4845
+ iCtx,
4846
+ };
4847
+ };
4848
+
4545
4849
  /** @internal */
4546
4850
  const useResourceQrl = (qrl, opts) => {
4547
4851
  const { val, set, i, iCtx } = useSequentialScope();
@@ -4683,7 +4987,8 @@ const runResource = (task, container, host) => {
4683
4987
  iCtx.$container$ = container;
4684
4988
  const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
4685
4989
  const resource = task.$state$;
4686
- assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
4990
+ isDev &&
4991
+ assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
4687
4992
  const track = trackFn(task, container);
4688
4993
  const [cleanup, cleanups] = cleanupFn(task, (reason) => container.handleError(reason, host));
4689
4994
  const resourceTarget = unwrapStore(resource);
@@ -4776,33 +5081,6 @@ const runResource = (task, container, host) => {
4776
5081
  return promise;
4777
5082
  };
4778
5083
 
4779
- /** Factory functions to create operations with consistent hidden classes. */
4780
- const createDeleteOperation = (target) => ({
4781
- operationType: 1 /* VNodeOperationType.Delete */,
4782
- target,
4783
- });
4784
- const createRemoveAllChildrenOperation = (target) => ({
4785
- operationType: 4 /* VNodeOperationType.RemoveAllChildren */,
4786
- target,
4787
- });
4788
- const createSetTextOperation = (target, text) => ({
4789
- operationType: 16 /* VNodeOperationType.SetText */,
4790
- target,
4791
- text,
4792
- });
4793
- const createInsertOrMoveOperation = (target, parent, beforeTarget) => ({
4794
- operationType: 2 /* VNodeOperationType.InsertOrMove */,
4795
- target,
4796
- parent,
4797
- beforeTarget,
4798
- });
4799
- const createSetAttributeOperation = (target, attrName, attrValue) => ({
4800
- operationType: 8 /* VNodeOperationType.SetAttribute */,
4801
- target,
4802
- attrName,
4803
- attrValue,
4804
- });
4805
-
4806
5084
  /**
4807
5085
  * Executes tasks for a vNode if the TASKS dirty bit is set. Tasks are stored in the ELEMENT_SEQ
4808
5086
  * property and executed in order.
@@ -4947,8 +5225,8 @@ function clearNodePropData(vNode) {
4947
5225
  const props = (vNode.props ||= {});
4948
5226
  delete props[NODE_PROPS_DATA_KEY];
4949
5227
  }
4950
- function setNodeProp(domVNode, journal, property, value, isConst) {
4951
- journal.push(createSetAttributeOperation(domVNode.node, property, value));
5228
+ function setNodeProp(domVNode, journal, property, value, isConst, scopedStyleIdPrefix = null) {
5229
+ journal.push(createSetAttributeOperation(domVNode.node, property, value, scopedStyleIdPrefix, (domVNode.flags & 128 /* VNodeFlags.NS_svg */) !== 0));
4952
5230
  if (!isConst) {
4953
5231
  if (domVNode.props && value == null) {
4954
5232
  delete domVNode.props[property];
@@ -4983,10 +5261,9 @@ function executeNodeProps(vNode, journal) {
4983
5261
  // TODO: Handle async signals (promises) - need to track pending async prop data
4984
5262
  value = value.value;
4985
5263
  }
4986
- // Process synchronously (same logic as scheduler)
4987
- const serializedValue = serializeAttribute(property, value, nodeProp.scopedStyleIdPrefix);
5264
+ // Pass raw value and scopedStyleIdPrefix - serialization happens in flush
4988
5265
  const isConst = nodeProp.isConst;
4989
- setNodeProp(domVNode, journal, property, serializedValue, isConst);
5266
+ setNodeProp(domVNode, journal, property, value, isConst, nodeProp.scopedStyleIdPrefix);
4990
5267
  }
4991
5268
  // Clear pending prop data after processing
4992
5269
  clearNodePropData(vNode);
@@ -5065,59 +5342,129 @@ const fastInsertBefore = (insertBeforeParent, target, insertBefore) => {
5065
5342
  _insertBefore.call(insertBeforeParent, target, insertBefore);
5066
5343
  };
5067
5344
  function _flushJournal(journal) {
5068
- // console.log(vnode_journalToString(journal));
5069
- for (const operation of journal) {
5070
- switch (operation.operationType) {
5071
- case 2 /* VNodeOperationType.InsertOrMove */: {
5072
- const insertBefore = operation.beforeTarget;
5073
- const insertBeforeParent = operation.parent;
5074
- fastInsertBefore(insertBeforeParent, operation.target, insertBefore);
5075
- break;
5076
- }
5077
- case 1 /* VNodeOperationType.Delete */: {
5078
- operation.target.remove();
5079
- break;
5345
+ let batchParent = null;
5346
+ let batchBefore = null;
5347
+ let batchNodes = null;
5348
+ const batchSet = new Set();
5349
+ const flush = () => {
5350
+ if (batchNodes) {
5351
+ if (batchNodes.length === 1) {
5352
+ fastInsertBefore(batchParent, batchNodes[0], batchBefore);
5080
5353
  }
5081
- case 16 /* VNodeOperationType.SetText */: {
5082
- operation.target.nodeValue = operation.text;
5083
- break;
5084
- }
5085
- case 8 /* VNodeOperationType.SetAttribute */: {
5086
- const element = operation.target;
5087
- const attrName = operation.attrName;
5088
- const attrValue = operation.attrValue;
5089
- const shouldRemove = attrValue == null || attrValue === false;
5090
- if (isBooleanAttr(element, attrName)) {
5091
- element[attrName] = parseBoolean(attrValue);
5092
- }
5093
- else if (attrName === dangerouslySetInnerHTML) {
5094
- element.innerHTML = attrValue;
5095
- element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
5354
+ else {
5355
+ const doc = batchParent.ownerDocument || batchParent;
5356
+ const fragment = doc.createDocumentFragment();
5357
+ for (const node of batchNodes) {
5358
+ fragment.appendChild(node);
5096
5359
  }
5097
- else if (shouldRemove) {
5098
- element.removeAttribute(attrName);
5360
+ fastInsertBefore(batchParent, fragment, batchBefore);
5361
+ }
5362
+ batchNodes = null;
5363
+ batchParent = null;
5364
+ batchBefore = null;
5365
+ batchSet.clear();
5366
+ }
5367
+ };
5368
+ for (const operation of journal) {
5369
+ if (operation instanceof InsertOrMoveOperation) {
5370
+ if (batchParent === operation.parent && batchBefore === operation.beforeTarget) {
5371
+ if (!batchNodes) {
5372
+ batchNodes = [];
5099
5373
  }
5100
- else if (attrName === 'value' && attrName in element) {
5101
- element.value = attrValue;
5374
+ batchNodes.push(operation.target);
5375
+ batchSet.add(operation.target);
5376
+ continue;
5377
+ }
5378
+ if (batchNodes) {
5379
+ // If we have an existing batch, we need to check if the new operation conflicts with it.
5380
+ // 1. If we are inserting into the same parent but with a different "before" reference, we must flush.
5381
+ if (batchParent === operation.parent) {
5382
+ flush();
5383
+ batchParent = operation.parent;
5384
+ batchBefore = operation.beforeTarget;
5385
+ batchNodes = [operation.target];
5386
+ batchSet.add(operation.target);
5387
+ continue;
5102
5388
  }
5103
- else {
5104
- element.setAttribute(attrName, attrValue);
5389
+ // 2. If we are moving a node that is currently in the batch, or moving the node that is the reference for the batch.
5390
+ if (batchSet.has(operation.target) ||
5391
+ (batchBefore && operation.target === batchBefore) ||
5392
+ (batchParent && operation.target === batchParent)) {
5393
+ flush();
5394
+ batchParent = operation.parent;
5395
+ batchBefore = operation.beforeTarget;
5396
+ batchNodes = [operation.target];
5397
+ batchSet.add(operation.target);
5398
+ continue;
5105
5399
  }
5106
- break;
5400
+ // 3. Otherwise, we can execute this operation immediately without flushing the current batch.
5401
+ // This is important for "interleaved" inserts, e.g. inserting <tr> into <tbody> (batched)
5402
+ // and then inserting <td> into that <tr> (immediate).
5403
+ // The <tr> is in memory, so inserting <td> into it is fine and doesn't require the <tr> to be in the DOM.
5107
5404
  }
5108
- case 4 /* VNodeOperationType.RemoveAllChildren */: {
5109
- const removeParent = operation.target;
5110
- if (removeParent.replaceChildren) {
5111
- removeParent.replaceChildren();
5112
- }
5113
- else {
5114
- // fallback if replaceChildren is not supported
5115
- removeParent.textContent = '';
5405
+ else {
5406
+ batchParent = operation.parent;
5407
+ batchBefore = operation.beforeTarget;
5408
+ batchNodes = [operation.target];
5409
+ batchSet.add(operation.target);
5410
+ continue;
5411
+ }
5412
+ fastInsertBefore(operation.parent, operation.target, operation.beforeTarget);
5413
+ continue;
5414
+ }
5415
+ if (operation instanceof DeleteOperation) {
5416
+ if (batchSet.has(operation.target) ||
5417
+ (batchBefore && operation.target === batchBefore) ||
5418
+ (batchParent && operation.target === batchParent)) {
5419
+ flush();
5420
+ }
5421
+ operation.target.remove();
5422
+ continue;
5423
+ }
5424
+ if (operation instanceof RemoveAllChildrenOperation) {
5425
+ if (batchSet.has(operation.target) ||
5426
+ (batchBefore && operation.target === batchBefore) ||
5427
+ (batchParent && operation.target === batchParent)) {
5428
+ flush();
5429
+ }
5430
+ // Removing children of a node in the batch is safe (clears detached node)
5431
+ const removeParent = operation.target;
5432
+ removeParent.textContent = '';
5433
+ continue;
5434
+ }
5435
+ if (operation instanceof SetTextOperation) {
5436
+ operation.target.nodeValue = operation.text;
5437
+ }
5438
+ else if (operation instanceof SetAttributeOperation) {
5439
+ const element = operation.target;
5440
+ const attrName = operation.attrName;
5441
+ const rawValue = operation.attrValue;
5442
+ const attrValue = rawValue != null
5443
+ ? serializeAttribute(attrName, rawValue, operation.scopedStyleIdPrefix)
5444
+ : null;
5445
+ const shouldRemove = attrValue == null || attrValue === false;
5446
+ if (isBooleanAttr(element, attrName)) {
5447
+ element[attrName] = parseBoolean(attrValue);
5448
+ }
5449
+ else if (attrName === dangerouslySetInnerHTML) {
5450
+ if (batchParent === element) {
5451
+ flush();
5116
5452
  }
5117
- break;
5453
+ element.innerHTML = attrValue;
5454
+ element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
5455
+ }
5456
+ else if (shouldRemove) {
5457
+ element.removeAttribute(attrName);
5458
+ }
5459
+ else if (attrName === 'value' && attrName in element) {
5460
+ element.value = attrValue;
5461
+ }
5462
+ else {
5463
+ directSetAttribute(element, attrName, attrValue, operation.isSvg);
5118
5464
  }
5119
5465
  }
5120
5466
  }
5467
+ flush();
5121
5468
  }
5122
5469
  function executeAfterFlush(container, cursorData) {
5123
5470
  const visibleTasks = cursorData.afterFlushTasks;
@@ -5253,7 +5600,7 @@ function processCursorQueue(options = {
5253
5600
  */
5254
5601
  function walkCursor(cursor, options) {
5255
5602
  const { timeBudget } = options;
5256
- const isServer = isServerPlatform();
5603
+ const isRunningOnServer = import.meta.env.TEST ? isServerPlatform() : isServer;
5257
5604
  const startTime = performance.now();
5258
5605
  const cursorData = getCursorData(cursor);
5259
5606
  // Check if cursor is blocked by a promise
@@ -5262,10 +5609,10 @@ function walkCursor(cursor, options) {
5262
5609
  return;
5263
5610
  }
5264
5611
  const container = cursorData.container;
5265
- assertDefined(container, 'Cursor container not found');
5612
+ isDev && assertDefined(container, 'Cursor container not found');
5266
5613
  // Check if cursor is already complete
5267
5614
  if (!cursor.dirty) {
5268
- finishWalk(container, cursor, cursorData, isServer);
5615
+ finishWalk(container, cursor, cursorData, isRunningOnServer);
5269
5616
  return;
5270
5617
  }
5271
5618
  const journal = (cursorData.journal ||= []);
@@ -5273,7 +5620,7 @@ function walkCursor(cursor, options) {
5273
5620
  let currentVNode = null;
5274
5621
  while ((currentVNode = cursorData.position)) {
5275
5622
  // Check time budget (only for DOM, not SSR)
5276
- if (!isServer && !import.meta.env.TEST) {
5623
+ if (!isRunningOnServer && !import.meta.env.TEST) {
5277
5624
  const elapsed = performance.now() - startTime;
5278
5625
  if (elapsed >= timeBudget) {
5279
5626
  // Schedule continuation as macrotask to actually yield to browser
@@ -5356,8 +5703,9 @@ function walkCursor(cursor, options) {
5356
5703
  return;
5357
5704
  }
5358
5705
  }
5359
- assertFalse(!!(cursor.dirty & 127 /* ChoreBits.DIRTY_MASK */ && !cursorData.position), 'Cursor is still dirty and position is not set after walking');
5360
- finishWalk(container, cursor, cursorData, isServer);
5706
+ isDev &&
5707
+ assertFalse(!!(cursor.dirty & 127 /* ChoreBits.DIRTY_MASK */ && !cursorData.position), 'Cursor is still dirty and position is not set after walking');
5708
+ finishWalk(container, cursor, cursorData, isRunningOnServer);
5361
5709
  }
5362
5710
  function finishWalk(container, cursor, cursorData, isServer) {
5363
5711
  if (!(cursor.dirty & 127 /* ChoreBits.DIRTY_MASK */)) {
@@ -5377,7 +5725,7 @@ function finishWalk(container, cursor, cursorData, isServer) {
5377
5725
  function resolveCursor(container) {
5378
5726
  // TODO streaming as a cursor? otherwise we need to wait separately for it
5379
5727
  // or just ignore and resolve manually
5380
- if (container.$cursorCount$ === 0) {
5728
+ if (container.$cursorCount$ === 0 && container.$pausedCursorCount$ === 0) {
5381
5729
  container.$resolveRenderPromise$();
5382
5730
  container.$renderPromise$ = null;
5383
5731
  }
@@ -5441,6 +5789,7 @@ function getNextVNode(vNode, cursor) {
5441
5789
  // all array items checked, children are no longer dirty
5442
5790
  parent.dirty &= -33 /* ChoreBits.CHILDREN */;
5443
5791
  parent.dirtyChildren = null;
5792
+ parent.nextDirtyChildIndex = 0;
5444
5793
  return getNextVNode(parent, cursor);
5445
5794
  }
5446
5795
 
@@ -5529,8 +5878,13 @@ function _executeSsrChores(container, ssrNode) {
5529
5878
  promise = result;
5530
5879
  }
5531
5880
  }
5532
- if (ssrNode.dirty & 4 /* ChoreBits.COMPONENT */) ;
5533
- ssrNode.dirty &= -128 /* ChoreBits.DIRTY_MASK */;
5881
+ // In SSR, we don't handle the COMPONENT bit here.
5882
+ // During initial render, if a task completes and marks the component dirty,
5883
+ // we want to leave the COMPONENT bit set so that executeComponent can detect
5884
+ // it after $waitOn$ completes and re-execute the component function.
5885
+ // executeComponent will clear the bit after re-executing.
5886
+ // Clear all dirty bits EXCEPT COMPONENT
5887
+ ssrNode.dirty &= -124;
5534
5888
  if (promise) {
5535
5889
  return promise;
5536
5890
  }
@@ -5649,7 +6003,7 @@ function findAndPropagateToBlockingCursor(vNode) {
5649
6003
  return false;
5650
6004
  }
5651
6005
  function isSsrNodeGuard(_vNode) {
5652
- return isServerPlatform();
6006
+ return import.meta.env.TEST ? isServerPlatform() : isServer;
5653
6007
  }
5654
6008
  /**
5655
6009
  * Marks a vNode as dirty and propagates dirty bits up the tree.
@@ -5855,21 +6209,22 @@ const fastGetter = (prototype, name) => {
5855
6209
  */
5856
6210
  //////////////////////////////////////////////////////////////////////////////////////////////////////
5857
6211
  const vnode_newElement = (element, elementName, key = null) => {
5858
- assertEqual(fastNodeType(element), 1 /* ELEMENT_NODE */, 'Expecting element node.');
6212
+ isDev && assertEqual(fastNodeType(element), 1 /* ELEMENT_NODE */, 'Expecting element node.');
5859
6213
  const vnode = new ElementVNode(key, 1 /* VNodeFlags.Element */ | 8 /* VNodeFlags.Inflated */ | (-1 << 9 /* VNodeFlagsIndex.shift */), // Flag
5860
6214
  null, null, null, null, null, null, element, elementName);
5861
6215
  element.vNode = vnode;
5862
6216
  return vnode;
5863
6217
  };
5864
6218
  const vnode_newUnMaterializedElement = (element) => {
5865
- assertEqual(fastNodeType(element), 1 /* ELEMENT_NODE */, 'Expecting element node.');
6219
+ isDev && assertEqual(fastNodeType(element), 1 /* ELEMENT_NODE */, 'Expecting element node.');
5866
6220
  const vnode = new ElementVNode(null, 1 /* VNodeFlags.Element */ | (-1 << 9 /* VNodeFlagsIndex.shift */), // Flag
5867
6221
  null, null, null, null, undefined, undefined, element, undefined);
5868
6222
  element.vNode = vnode;
5869
6223
  return vnode;
5870
6224
  };
5871
6225
  const vnode_newSharedText = (previousTextNode, sharedTextNode, textContent) => {
5872
- sharedTextNode &&
6226
+ isDev &&
6227
+ sharedTextNode &&
5873
6228
  assertEqual(fastNodeType(sharedTextNode), 3 /* TEXT_NODE */, 'Expecting text node.');
5874
6229
  const vnode = new TextVNode(4 /* VNodeFlags.Text */ | (-1 << 9 /* VNodeFlagsIndex.shift */), // Flag
5875
6230
  null, // Parent
@@ -5886,18 +6241,18 @@ const vnode_newText = (textNode, textContent) => {
5886
6241
  null, textNode, // TextNode
5887
6242
  textContent // Text Content
5888
6243
  );
5889
- assertEqual(fastNodeType(textNode), 3 /* TEXT_NODE */, 'Expecting text node.');
5890
- assertFalse(vnode_isElementVNode(vnode), 'Incorrect format of TextVNode.');
5891
- assertTrue(vnode_isTextVNode(vnode), 'Incorrect format of TextVNode.');
5892
- assertFalse(vnode_isVirtualVNode(vnode), 'Incorrect format of TextVNode.');
6244
+ isDev && assertEqual(fastNodeType(textNode), 3 /* TEXT_NODE */, 'Expecting text node.');
6245
+ isDev && assertFalse(vnode_isElementVNode(vnode), 'Incorrect format of TextVNode.');
6246
+ isDev && assertTrue(vnode_isTextVNode(vnode), 'Incorrect format of TextVNode.');
6247
+ isDev && assertFalse(vnode_isVirtualVNode(vnode), 'Incorrect format of TextVNode.');
5893
6248
  return vnode;
5894
6249
  };
5895
6250
  const vnode_newVirtual = () => {
5896
6251
  const vnode = new VirtualVNode(null, 2 /* VNodeFlags.Virtual */ | (-1 << 9 /* VNodeFlagsIndex.shift */), // Flags
5897
6252
  null, null, null, null, null, null);
5898
- assertFalse(vnode_isElementVNode(vnode), 'Incorrect format of TextVNode.');
5899
- assertFalse(vnode_isTextVNode(vnode), 'Incorrect format of TextVNode.');
5900
- assertTrue(vnode_isVirtualVNode(vnode), 'Incorrect format of TextVNode.');
6253
+ isDev && assertFalse(vnode_isElementVNode(vnode), 'Incorrect format of TextVNode.');
6254
+ isDev && assertFalse(vnode_isTextVNode(vnode), 'Incorrect format of TextVNode.');
6255
+ isDev && assertTrue(vnode_isVirtualVNode(vnode), 'Incorrect format of TextVNode.');
5901
6256
  return vnode;
5902
6257
  };
5903
6258
  //////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -5905,16 +6260,16 @@ const vnode_isVNode = (vNode) => {
5905
6260
  return vNode instanceof VNode;
5906
6261
  };
5907
6262
  const vnode_isElementVNode = (vNode) => {
5908
- return vNode instanceof ElementVNode;
6263
+ return (vNode.flags & 1 /* VNodeFlags.Element */) === 1 /* VNodeFlags.Element */;
5909
6264
  };
5910
6265
  const vnode_isElementOrTextVNode = (vNode) => {
5911
- assertDefined(vNode, 'Missing vNode');
6266
+ isDev && assertDefined(vNode, 'Missing vNode');
5912
6267
  const flag = vNode.flags;
5913
6268
  return (flag & 5 /* VNodeFlags.ELEMENT_OR_TEXT_MASK */) !== 0;
5914
6269
  };
5915
6270
  /** @internal */
5916
6271
  const vnode_isMaterialized = (vNode) => {
5917
- assertDefined(vNode, 'Missing vNode');
6272
+ isDev && assertDefined(vNode, 'Missing vNode');
5918
6273
  const flag = vNode.flags;
5919
6274
  return ((flag & 1 /* VNodeFlags.Element */) === 1 /* VNodeFlags.Element */ &&
5920
6275
  vNode.firstChild !== undefined &&
@@ -5922,27 +6277,30 @@ const vnode_isMaterialized = (vNode) => {
5922
6277
  };
5923
6278
  /** @internal */
5924
6279
  const vnode_isTextVNode = (vNode) => {
5925
- return vNode instanceof TextVNode;
6280
+ return (vNode.flags & 4 /* VNodeFlags.Text */) === 4 /* VNodeFlags.Text */;
5926
6281
  };
5927
6282
  /** @internal */
5928
6283
  const vnode_isVirtualVNode = (vNode) => {
5929
- return vNode instanceof VirtualVNode;
6284
+ return (vNode.flags & 2 /* VNodeFlags.Virtual */) === 2 /* VNodeFlags.Virtual */;
5930
6285
  };
5931
6286
  const vnode_isProjection = (vNode) => {
5932
- assertDefined(vNode, 'Missing vNode');
6287
+ isDev && assertDefined(vNode, 'Missing vNode');
5933
6288
  const flag = vNode.flags;
5934
6289
  return ((flag & 2 /* VNodeFlags.Virtual */) === 2 /* VNodeFlags.Virtual */ && vnode_getProp(vNode, QSlot, null) !== null);
5935
6290
  };
5936
6291
  const ensureTextVNode = (vNode) => {
5937
- assertTrue(vnode_isTextVNode(vNode), 'Expecting TextVNode was: ' + vnode_getNodeTypeName(vNode));
6292
+ isDev &&
6293
+ assertTrue(vnode_isTextVNode(vNode), 'Expecting TextVNode was: ' + vnode_getNodeTypeName(vNode));
5938
6294
  return vNode;
5939
6295
  };
5940
6296
  const ensureElementOrVirtualVNode = (vNode) => {
5941
- assertDefined(vNode, 'Missing vNode');
5942
- assertTrue((vNode.flags & 3 /* VNodeFlags.ELEMENT_OR_VIRTUAL_MASK */) !== 0, 'Expecting ElementVNode or VirtualVNode was: ' + vnode_getNodeTypeName(vNode));
6297
+ isDev && assertDefined(vNode, 'Missing vNode');
6298
+ isDev &&
6299
+ assertTrue((vNode.flags & 3 /* VNodeFlags.ELEMENT_OR_VIRTUAL_MASK */) !== 0, 'Expecting ElementVNode or VirtualVNode was: ' + vnode_getNodeTypeName(vNode));
5943
6300
  };
5944
6301
  const ensureElementVNode = (vNode) => {
5945
- assertTrue(vnode_isElementVNode(vNode), 'Expecting ElementVNode was: ' + vnode_getNodeTypeName(vNode));
6302
+ isDev &&
6303
+ assertTrue(vnode_isElementVNode(vNode), 'Expecting ElementVNode was: ' + vnode_getNodeTypeName(vNode));
5946
6304
  return vNode;
5947
6305
  };
5948
6306
  const vnode_getNodeTypeName = (vNode) => {
@@ -5972,20 +6330,21 @@ const vnode_getProp = (vNode, key, getObject) => {
5972
6330
  return null;
5973
6331
  };
5974
6332
  const vnode_setProp = (vNode, key, value) => {
5975
- if (vnode_isElementVNode(vNode) || vnode_isVirtualVNode(vNode)) {
5976
- if (value == null && vNode.props) {
5977
- delete vNode.props[key];
5978
- }
5979
- else {
5980
- vNode.props ||= {};
5981
- vNode.props[key] = value;
5982
- }
6333
+ if (value == null && vNode.props) {
6334
+ delete vNode.props[key];
6335
+ }
6336
+ else {
6337
+ vNode.props ||= {};
6338
+ vNode.props[key] = value;
5983
6339
  }
5984
6340
  };
5985
- const vnode_setAttr = (journal, vNode, key, value) => {
6341
+ const vnode_setAttr = (journal, vNode, key, value, scopedStyleIdPrefix = null) => {
5986
6342
  if (vnode_isElementVNode(vNode)) {
6343
+ import.meta.env.TEST &&
6344
+ scopedStyleIdPrefix &&
6345
+ vnode_setProp(vNode, debugStyleScopeIdPrefixAttr, scopedStyleIdPrefix);
5987
6346
  vnode_setProp(vNode, key, value);
5988
- addVNodeOperation(journal, createSetAttributeOperation(vNode.node, key, value));
6347
+ addVNodeOperation(journal, createSetAttributeOperation(vNode.node, key, value, scopedStyleIdPrefix, (vNode.flags & 128 /* VNodeFlags.NS_svg */) !== 0));
5989
6348
  }
5990
6349
  };
5991
6350
  const vnode_ensureElementKeyInflated = (vnode) => {
@@ -6218,7 +6577,7 @@ const vnode_ensureTextInflated = (journal, vnode) => {
6218
6577
  const flags = textVNode.flags;
6219
6578
  if ((flags & 8 /* VNodeFlags.Inflated */) === 0) {
6220
6579
  const parentNode = vnode_getDomParent(vnode, true);
6221
- assertDefined(parentNode, 'Missing parent node.');
6580
+ isDev && assertDefined(parentNode, 'Missing parent node.');
6222
6581
  const sharedTextNode = textVNode.node;
6223
6582
  const doc = fastOwnerDocument(parentNode);
6224
6583
  // Walk the previous siblings and inflate them.
@@ -6228,7 +6587,7 @@ const vnode_ensureTextInflated = (journal, vnode) => {
6228
6587
  // case we know that the next node MUST be either NULL or an Element.
6229
6588
  const node = vnode_getDomSibling(vnode, true, true);
6230
6589
  const insertBeforeNode = sharedTextNode ||
6231
- ((node instanceof ElementVNode ? node.node : node?.node) || null);
6590
+ ((node && vnode_isElementVNode(node) ? node.node : node?.node) || null);
6232
6591
  let lastPreviousTextNode = insertBeforeNode;
6233
6592
  while (vCursor && vnode_isTextVNode(vCursor)) {
6234
6593
  if ((vCursor.flags & 8 /* VNodeFlags.Inflated */) === 0) {
@@ -6268,7 +6627,7 @@ const vnode_locate = (rootVNode, id) => {
6268
6627
  let elementOffset = -1;
6269
6628
  let refElement;
6270
6629
  if (typeof id === 'string') {
6271
- assertDefined(qVNodeRefs, 'Missing qVNodeRefs.');
6630
+ isDev && assertDefined(qVNodeRefs, 'Missing qVNodeRefs.');
6272
6631
  elementOffset = parseInt(id);
6273
6632
  refElement = qVNodeRefs.get(elementOffset);
6274
6633
  }
@@ -6279,9 +6638,10 @@ const vnode_locate = (rootVNode, id) => {
6279
6638
  return vNode;
6280
6639
  }
6281
6640
  }
6282
- assertDefined(refElement, 'Missing refElement.');
6641
+ isDev && assertDefined(refElement, 'Missing refElement.');
6283
6642
  if (!vnode_isVNode(refElement)) {
6284
- assertTrue(containerElement.contains(refElement), `Couldn't find the element inside the container while locating the VNode.`);
6643
+ isDev &&
6644
+ assertTrue(containerElement.contains(refElement), `Couldn't find the element inside the container while locating the VNode.`);
6285
6645
  // We need to find the vnode.
6286
6646
  let parent = refElement;
6287
6647
  const elementPath = [refElement];
@@ -6329,10 +6689,10 @@ const vnode_locate = (rootVNode, id) => {
6329
6689
  };
6330
6690
  const vnode_getChildWithIdx = (vNode, childIdx) => {
6331
6691
  let child = vnode_getFirstChild(vNode);
6332
- assertDefined(child, 'Missing child.');
6692
+ isDev && assertDefined(child, 'Missing child.');
6333
6693
  while (child.flags >>> 9 /* VNodeFlagsIndex.shift */ !== childIdx) {
6334
6694
  child = child.nextSibling;
6335
- assertDefined(child, 'Missing child.');
6695
+ isDev && assertDefined(child, 'Missing child.');
6336
6696
  }
6337
6697
  return child;
6338
6698
  };
@@ -6340,7 +6700,7 @@ const vNodeStack = [];
6340
6700
  const vnode_getVNodeForChildNode = (vNode, childElement) => {
6341
6701
  ensureElementVNode(vNode);
6342
6702
  let child = vnode_getFirstChild(vNode);
6343
- assertDefined(child, 'Missing child.');
6703
+ isDev && assertDefined(child, 'Missing child.');
6344
6704
  while (child && (child instanceof ElementVNode ? child.node !== childElement : true)) {
6345
6705
  if (vnode_isVirtualVNode(child)) {
6346
6706
  const next = child.nextSibling;
@@ -6362,13 +6722,13 @@ const vnode_getVNodeForChildNode = (vNode, childElement) => {
6362
6722
  child = next || vNodeStack.pop();
6363
6723
  }
6364
6724
  }
6365
- assertDefined(child, 'Missing child.');
6725
+ isDev && assertDefined(child, 'Missing child.');
6366
6726
  }
6367
6727
  while (vNodeStack.length) {
6368
6728
  vNodeStack.pop();
6369
6729
  }
6370
6730
  ensureElementVNode(child);
6371
- assertEqual(child.node, childElement, 'Child not found.');
6731
+ isDev && assertEqual(child.node, childElement, 'Child not found.');
6372
6732
  // console.log('FOUND', child[VNodeProps.node]?.outerHTML);
6373
6733
  return child;
6374
6734
  };
@@ -6543,7 +6903,7 @@ const vnode_getDomParentVNode = (vnode, includeProjection) => {
6543
6903
  return vnode;
6544
6904
  };
6545
6905
  const vnode_remove = (journal, vParent, vToRemove, removeDOM) => {
6546
- assertEqual(vParent, vToRemove.parent, 'Parent mismatch.');
6906
+ isDev && assertEqual(vParent, vToRemove.parent, 'Parent mismatch.');
6547
6907
  if (vnode_isTextVNode(vToRemove)) {
6548
6908
  vnode_ensureTextInflated(journal, vToRemove);
6549
6909
  }
@@ -6580,7 +6940,7 @@ const vnode_remove = (journal, vParent, vToRemove, removeDOM) => {
6580
6940
  vToRemove.nextSibling = null;
6581
6941
  };
6582
6942
  const vnode_truncate = (journal, vParent, vDelete, removeDOM = true) => {
6583
- assertDefined(vDelete, 'Missing vDelete.');
6943
+ isDev && assertDefined(vDelete, 'Missing vDelete.');
6584
6944
  const parent = vnode_getDomParent(vParent, true);
6585
6945
  if (parent && removeDOM) {
6586
6946
  if (vnode_isElementVNode(vParent)) {
@@ -6699,8 +7059,8 @@ const ensureMaterialized = (vnode) => {
6699
7059
  vFirstChild = vnode_materialize(vParent);
6700
7060
  }
6701
7061
  }
6702
- assertTrue(vParent.firstChild !== undefined, 'Did not materialize.');
6703
- assertTrue(vParent.lastChild !== undefined, 'Did not materialize.');
7062
+ isDev && assertTrue(vParent.firstChild !== undefined, 'Did not materialize.');
7063
+ isDev && assertTrue(vParent.lastChild !== undefined, 'Did not materialize.');
6704
7064
  return vFirstChild;
6705
7065
  };
6706
7066
  let _fastHasAttribute = null;
@@ -7018,8 +7378,11 @@ function vnode_toString(depth = 20, offset = '', materialize = false, siblings =
7018
7378
  else if (vnode_isVirtualVNode(vnode)) {
7019
7379
  const idx = vnode.flags >>> 9 /* VNodeFlagsIndex.shift */;
7020
7380
  const attrs = ['[' + String(idx) + ']'];
7381
+ if (vnode.dirty) {
7382
+ attrs.push(` dirty:${vnode.dirty}`);
7383
+ }
7021
7384
  vnode_getAttrKeys(vnode).forEach((key) => {
7022
- if (key !== DEBUG_TYPE) {
7385
+ if (key !== DEBUG_TYPE && key !== debugStyleScopeIdPrefixAttr) {
7023
7386
  const value = vnode_getProp(vnode, key, null);
7024
7387
  attrs.push(' ' + key + '=' + qwikDebugToString(value));
7025
7388
  }
@@ -7336,8 +7699,8 @@ const useInvokeContext = () => {
7336
7699
  if (!ctx || ctx.$event$ !== RenderEvent) {
7337
7700
  throw qError(10 /* QError.useInvokeContext */);
7338
7701
  }
7339
- assertDefined(ctx.$hostElement$, `invoke: $hostElement$ must be defined`, ctx);
7340
- assertDefined(ctx.$effectSubscriber$, `invoke: $effectSubscriber$ must be defined`, ctx);
7702
+ isDev && assertDefined(ctx.$hostElement$, `invoke: $hostElement$ must be defined`, ctx);
7703
+ isDev && assertDefined(ctx.$effectSubscriber$, `invoke: $effectSubscriber$ must be defined`, ctx);
7341
7704
  return ctx;
7342
7705
  };
7343
7706
  function useBindInvokeContext(fn) {
@@ -7556,7 +7919,8 @@ const _waitUntilRendered = (elm) => {
7556
7919
  */
7557
7920
  // </docs>
7558
7921
  const createContextId = (name) => {
7559
- assertTrue(/^[\w/.-]+$/.test(name), 'Context name must only contain A-Z,a-z,0-9,_,.,-', name);
7922
+ isDev &&
7923
+ assertTrue(/^[\w/.-]+$/.test(name), 'Context name must only contain A-Z,a-z,0-9,_,.,-', name);
7560
7924
  return /*#__PURE__*/ Object.freeze({
7561
7925
  id: fromCamelToKebabCase(name),
7562
7926
  });
@@ -7803,6 +8167,7 @@ const _typeIdNames = [
7803
8167
  'JSXNode',
7804
8168
  'PropsProxy',
7805
8169
  'SubscriptionData',
8170
+ 'EffectSubscription',
7806
8171
  ];
7807
8172
 
7808
8173
  function qrlToString(serializationContext, value, raw) {
@@ -7860,13 +8225,16 @@ function qrlToString(serializationContext, value, raw) {
7860
8225
  return qrlStringInline;
7861
8226
  }
7862
8227
  function createQRLWithBackChannel(chunk, symbol, captureIds) {
7863
- let qrlRef = null;
8228
+ let qrlImporter = null;
7864
8229
  if (isDev && chunk === QRL_RUNTIME_CHUNK) {
7865
8230
  const backChannel = globalThis.__qrl_back_channel__;
7866
- assertDefined(backChannel, 'Missing QRL_RUNTIME_CHUNK');
7867
- qrlRef = backChannel.get(symbol);
8231
+ isDev && assertDefined(backChannel, 'Missing QRL_RUNTIME_CHUNK');
8232
+ const fn = backChannel.get(symbol);
8233
+ if (fn) {
8234
+ qrlImporter = () => Promise.resolve({ [symbol]: fn });
8235
+ }
7868
8236
  }
7869
- return createQRL(chunk, symbol, qrlRef, null, captureIds, null);
8237
+ return createQRL(chunk, symbol, null, qrlImporter, captureIds, null);
7870
8238
  }
7871
8239
  /** Parses "chunk#hash[...rootRef]" */
7872
8240
  function parseQRL(qrl) {
@@ -7978,7 +8346,7 @@ const allocate = (container, typeId, value) => {
7978
8346
  case 30 /* TypeIds.FormData */:
7979
8347
  return new FormData();
7980
8348
  case 31 /* TypeIds.JSXNode */:
7981
- return new JSXNodeImpl(null);
8349
+ return new JSXNodeImpl(null, null, null, null, null);
7982
8350
  case 11 /* TypeIds.BigInt */:
7983
8351
  return BigInt(value);
7984
8352
  case 16 /* TypeIds.Set */:
@@ -8042,6 +8410,8 @@ const allocate = (container, typeId, value) => {
8042
8410
  }
8043
8411
  case 33 /* TypeIds.SubscriptionData */:
8044
8412
  return new SubscriptionData({});
8413
+ case 34 /* TypeIds.EffectSubscription */:
8414
+ return new EffectSubscription(null, null, null, null);
8045
8415
  default:
8046
8416
  throw qError(18 /* QError.serializeErrorCannotAllocate */, [typeId]);
8047
8417
  }
@@ -8164,33 +8534,6 @@ const _regSymbol = (symbol, hash) => {
8164
8534
  return symbol;
8165
8535
  };
8166
8536
 
8167
- /**
8168
- * This is called by qwik-loader to run a QRL. It has to be synchronous.
8169
- *
8170
- * @internal
8171
- */
8172
- const _run = (...args) => {
8173
- // This will already check container
8174
- const [runQrl] = useLexicalScope();
8175
- const context = getInvokeContext();
8176
- const hostElement = context.$hostElement$;
8177
- if (hostElement) {
8178
- return retryOnPromise(() => {
8179
- if (!(hostElement.flags & 32 /* VNodeFlags.Deleted */)) {
8180
- return catchError(runQrl(...args), (err) => {
8181
- const container = (context.$container$ ||= getDomContainer(hostElement.node));
8182
- if (container) {
8183
- container.handleError(err, hostElement);
8184
- }
8185
- else {
8186
- throw err;
8187
- }
8188
- });
8189
- }
8190
- });
8191
- }
8192
- };
8193
-
8194
8537
  /** @file Shared types */
8195
8538
  /** @internal */
8196
8539
  function isStringifiable(value) {
@@ -8469,6 +8812,14 @@ async function serialize(serializationContext) {
8469
8812
  else if (value instanceof SubscriptionData) {
8470
8813
  output(33 /* TypeIds.SubscriptionData */, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
8471
8814
  }
8815
+ else if (value instanceof EffectSubscription) {
8816
+ output(34 /* TypeIds.EffectSubscription */, [
8817
+ value.consumer,
8818
+ value.property,
8819
+ value.backRef,
8820
+ value.data,
8821
+ ]);
8822
+ }
8472
8823
  else if (isStore(value)) {
8473
8824
  if (isResource(value)) {
8474
8825
  // let render know about the resource
@@ -8918,7 +9269,7 @@ function filterEffectBackRefs(effectBackRef) {
8918
9269
  let effectBackRefToSerialize = undefined;
8919
9270
  if (effectBackRef) {
8920
9271
  for (const [effectProp, effect] of effectBackRef) {
8921
- if (effect[2 /* EffectSubscriptionProp.BACK_REF */]) {
9272
+ if (effect.backRef) {
8922
9273
  effectBackRefToSerialize ||= new Map();
8923
9274
  effectBackRefToSerialize.set(effectProp, effect);
8924
9275
  }
@@ -9087,6 +9438,7 @@ class _SharedContainer {
9087
9438
  $renderPromise$ = null;
9088
9439
  $resolveRenderPromise$ = null;
9089
9440
  $cursorCount$ = 0;
9441
+ $pausedCursorCount$ = 0;
9090
9442
  constructor(serverData, locale) {
9091
9443
  this.$serverData$ = serverData;
9092
9444
  this.$locale$ = locale;
@@ -9125,8 +9477,6 @@ const applyQwikComponentBody = (ssr, jsx, component) => {
9125
9477
  host.setProp(ELEMENT_KEY, jsx.key);
9126
9478
  }
9127
9479
  return executeComponent(ssr, host, host, componentQrl, srcProps);
9128
- // const componentChore = ssr.$scheduler$(ChoreType.COMPONENT, host, componentQrl, srcProps);
9129
- // return getChorePromise(componentChore);
9130
9480
  };
9131
9481
 
9132
9482
  class ParentComponentData {
@@ -9276,7 +9626,7 @@ function processJSXNode(ssr, enqueue, value, options) {
9276
9626
  children != null && enqueue(children);
9277
9627
  }
9278
9628
  else if (type === Slot) {
9279
- const componentFrame = options.parentComponentFrame || ssr.unclaimedProjectionComponentFrameQueue.shift();
9629
+ const componentFrame = options.parentComponentFrame;
9280
9630
  if (componentFrame) {
9281
9631
  const compId = componentFrame.componentNode.id || '';
9282
9632
  const projectionAttrs = isDev ? [DEBUG_TYPE, "P" /* VirtualType.Projection */] : [];
@@ -9398,6 +9748,9 @@ function toSsrAttrs(record, options) {
9398
9748
  if (isPreventDefault(key)) {
9399
9749
  addPreventDefaultEventToSerializationContext(options.serializationCtx, key);
9400
9750
  }
9751
+ else if (key === ITERATION_ITEM_SINGLE || key === ITERATION_ITEM_MULTI) {
9752
+ value = options.serializationCtx.$addRoot$(value);
9753
+ }
9401
9754
  value = serializeAttribute(key, value, options.styleScopedId);
9402
9755
  ssrAttrs.push(key, value);
9403
9756
  };
@@ -9714,7 +10067,7 @@ const inflate = (container, target, typeId, data) => {
9714
10067
  const d = data;
9715
10068
  let owner = d[0];
9716
10069
  if (owner === _UNINITIALIZED) {
9717
- owner = new JSXNodeImpl(Fragment, d[1], d[2]);
10070
+ owner = new JSXNodeImpl(Fragment, d[1], d[2], null, null);
9718
10071
  owner._proxy = propsProxy;
9719
10072
  }
9720
10073
  propsProxy[_OWNER] = owner;
@@ -9726,6 +10079,15 @@ const inflate = (container, target, typeId, data) => {
9726
10079
  effectData.data.$isConst$ = data[1];
9727
10080
  break;
9728
10081
  }
10082
+ case 34 /* TypeIds.EffectSubscription */: {
10083
+ const effectSub = target;
10084
+ const d = data;
10085
+ effectSub.consumer = d[0];
10086
+ effectSub.property = d[1];
10087
+ effectSub.backRef = d[2];
10088
+ effectSub.data = d[3];
10089
+ break;
10090
+ }
9729
10091
  default:
9730
10092
  throw qError(16 /* QError.serializeErrorNotImplemented */, [typeId]);
9731
10093
  }
@@ -9770,7 +10132,8 @@ function inflateWrappedSignalValue(signal) {
9770
10132
  let hasAttrValue = false;
9771
10133
  if (effects) {
9772
10134
  // Find string keys (attribute names) in the effect back refs
9773
- for (const [_, key] of effects) {
10135
+ for (const effect of effects) {
10136
+ const key = effect.property;
9774
10137
  if (isString(key)) {
9775
10138
  // This is an attribute name, try to read its value
9776
10139
  const attrValue = vnode_getProp(hostVNode, key, null);
@@ -10399,7 +10762,7 @@ class DomContainer extends _SharedContainer {
10399
10762
  };
10400
10763
  getSyncFn(id) {
10401
10764
  const fn = this.$qFuncs$[id];
10402
- assertTrue(typeof fn === 'function', 'Invalid reference: ' + id);
10765
+ isDev && assertTrue(typeof fn === 'function', 'Invalid reference: ' + id);
10403
10766
  return fn;
10404
10767
  }
10405
10768
  $appendStyle$(content, styleId, host, scoped) {
@@ -10441,6 +10804,81 @@ class DomContainer extends _SharedContainer {
10441
10804
  }
10442
10805
  }
10443
10806
 
10807
+ /** @internal */
10808
+ const useTaskQrl = (qrl, opts) => {
10809
+ const { val, set, iCtx, i } = useSequentialScope();
10810
+ if (val) {
10811
+ return;
10812
+ }
10813
+ assertQrl(qrl);
10814
+ set(1);
10815
+ const taskFlags =
10816
+ // enabled by default
10817
+ opts?.deferUpdates === false ? 0 : 16 /* TaskFlags.RENDER_BLOCKING */;
10818
+ const task = new Task(8 /* TaskFlags.DIRTY */ | 2 /* TaskFlags.TASK */ | taskFlags, i, iCtx.$hostElement$, qrl, undefined, null);
10819
+ // In V2 we add the task to the sequential scope. We need to do this
10820
+ // in order to be able to retrieve it later when the parent element is
10821
+ // deleted and we need to be able to release the task subscriptions.
10822
+ set(task);
10823
+ const container = iCtx.$container$;
10824
+ const { $waitOn$: waitOn } = iCtx;
10825
+ const result = maybeThen(waitOn, () => runTask(task, container, iCtx.$hostElement$));
10826
+ if (isPromise(result)) {
10827
+ iCtx.$waitOn$ = result;
10828
+ }
10829
+ };
10830
+ const runTask = (task, container, host) => {
10831
+ task.$flags$ &= -9 /* TaskFlags.DIRTY */;
10832
+ cleanupDestroyable(task);
10833
+ const iCtx = newInvokeContext(container.$locale$, host, TaskEvent);
10834
+ iCtx.$container$ = container;
10835
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
10836
+ const track = trackFn(task, container);
10837
+ const [cleanup] = cleanupFn(task, (reason) => container.handleError(reason, host));
10838
+ const taskApi = { track, cleanup };
10839
+ return safeCall(() => taskFn(taskApi), cleanup, (err) => {
10840
+ // If a Promise is thrown, that means we need to re-run the task.
10841
+ if (isPromise(err)) {
10842
+ return err.then(() => runTask(task, container, host));
10843
+ }
10844
+ else {
10845
+ container.handleError(err, host);
10846
+ }
10847
+ });
10848
+ };
10849
+ class Task extends BackRef {
10850
+ $flags$;
10851
+ $index$;
10852
+ $el$;
10853
+ $qrl$;
10854
+ $state$;
10855
+ $destroy$;
10856
+ constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
10857
+ super();
10858
+ this.$flags$ = $flags$;
10859
+ this.$index$ = $index$;
10860
+ this.$el$ = $el$;
10861
+ this.$qrl$ = $qrl$;
10862
+ this.$state$ = $state$;
10863
+ this.$destroy$ = $destroy$;
10864
+ }
10865
+ }
10866
+ /** @internal */
10867
+ const isTask = (value) => {
10868
+ return value instanceof Task;
10869
+ };
10870
+ /**
10871
+ * Used internally as a qrl event handler to schedule a task.
10872
+ *
10873
+ * @internal
10874
+ */
10875
+ const scheduleTask = (_event, element) => {
10876
+ const [task] = useLexicalScope();
10877
+ const container = getDomContainer(element);
10878
+ task.$flags$ |= 8 /* TaskFlags.DIRTY */;
10879
+ markVNodeDirty(container, task.$el$, 1 /* ChoreBits.TASKS */);
10880
+ };
10881
+
10444
10882
  const throwIfQRLNotResolved = (qrl) => {
10445
10883
  const resolved = qrl.resolved;
10446
10884
  if (!resolved) {
@@ -10457,18 +10895,16 @@ const isSignal = (value) => {
10457
10895
  return value instanceof SignalImpl;
10458
10896
  };
10459
10897
  const ensureContainsSubscription = (array, effectSubscription) => {
10460
- !array.has(effectSubscription) && array.add(effectSubscription);
10898
+ array.add(effectSubscription);
10461
10899
  };
10462
10900
  /** Ensure the item is in back refs set */
10463
10901
  const ensureContainsBackRef = (array, value) => {
10464
- array[2 /* EffectSubscriptionProp.BACK_REF */] ||= new Set();
10465
- !array[2 /* EffectSubscriptionProp.BACK_REF */].has(value) &&
10466
- array[2 /* EffectSubscriptionProp.BACK_REF */].add(value);
10902
+ (array.backRef ||= new Set()).add(value);
10467
10903
  };
10468
10904
  const addQrlToSerializationCtx = (effectSubscriber, container) => {
10469
- if (!!container && !isDomContainer(container)) {
10470
- const effect = effectSubscriber[0 /* EffectSubscriptionProp.CONSUMER */];
10471
- const property = effectSubscriber[1 /* EffectSubscriptionProp.PROPERTY */];
10905
+ if (container) {
10906
+ const effect = effectSubscriber.consumer;
10907
+ const property = effectSubscriber.property;
10472
10908
  let qrl = null;
10473
10909
  if (isTask(effect)) {
10474
10910
  qrl = effect.$qrl$;
@@ -10485,24 +10921,15 @@ const addQrlToSerializationCtx = (effectSubscriber, container) => {
10485
10921
  }
10486
10922
  };
10487
10923
  const scheduleEffects = (container, signal, effects) => {
10488
- const isBrowser = !isServerPlatform();
10924
+ const isBrowser = import.meta.env.TEST ? !isServerPlatform() : !isServer;
10489
10925
  if (effects) {
10490
- let tasksToTrigger = null;
10491
10926
  const scheduleEffect = (effectSubscription) => {
10492
- const consumer = effectSubscription[0 /* EffectSubscriptionProp.CONSUMER */];
10493
- const property = effectSubscription[1 /* EffectSubscriptionProp.PROPERTY */];
10494
- assertDefined(container, 'Container must be defined.');
10927
+ const consumer = effectSubscription.consumer;
10928
+ const property = effectSubscription.property;
10929
+ isDev && assertDefined(container, 'Container must be defined.');
10495
10930
  if (isTask(consumer)) {
10496
10931
  consumer.$flags$ |= 8 /* TaskFlags.DIRTY */;
10497
- if (isBrowser) {
10498
- markVNodeDirty(container, consumer.$el$, 1 /* ChoreBits.TASKS */);
10499
- }
10500
- else {
10501
- // for server we run tasks sync, so they can change currently running effects
10502
- // in this case we could have infinite loop if we trigger tasks here
10503
- // so instead we collect them and trigger them after the effects are scheduled
10504
- (tasksToTrigger ||= []).push(consumer);
10505
- }
10932
+ markVNodeDirty(container, consumer.$el$, 1 /* ChoreBits.TASKS */);
10506
10933
  }
10507
10934
  else if (consumer instanceof SignalImpl) {
10508
10935
  consumer.invalidate();
@@ -10517,7 +10944,7 @@ const scheduleEffects = (container, signal, effects) => {
10517
10944
  }
10518
10945
  }
10519
10946
  else {
10520
- const effectData = effectSubscription[3 /* EffectSubscriptionProp.DATA */];
10947
+ const effectData = effectSubscription.data;
10521
10948
  if (effectData instanceof SubscriptionData) {
10522
10949
  const data = effectData.data;
10523
10950
  const payload = {
@@ -10541,14 +10968,10 @@ const scheduleEffects = (container, signal, effects) => {
10541
10968
  }
10542
10969
  }
10543
10970
  };
10544
- for (const effect of effects) {
10971
+ const effectsSnapshot = Array.from(effects);
10972
+ for (const effect of effectsSnapshot) {
10545
10973
  scheduleEffect(effect);
10546
10974
  }
10547
- if (!isBrowser && container && tasksToTrigger) {
10548
- for (const task of tasksToTrigger) {
10549
- markVNodeDirty(container, task.$el$, 1 /* ChoreBits.TASKS */);
10550
- }
10551
- }
10552
10975
  }
10553
10976
  };
10554
10977
  /** @internal */
@@ -10779,7 +11202,8 @@ class StoreHandler {
10779
11202
  this.$container$ = ctx.$container$;
10780
11203
  }
10781
11204
  else {
10782
- assertTrue(!ctx.$container$ || ctx.$container$ === this.$container$, 'Do not use signals across containers');
11205
+ isDev &&
11206
+ assertTrue(!ctx.$container$ || ctx.$container$ === this.$container$, 'Do not use signals across containers');
10783
11207
  }
10784
11208
  const effectSubscriber = ctx.$effectSubscriber$;
10785
11209
  if (effectSubscriber) {
@@ -10881,7 +11305,8 @@ function addStoreEffect(target, prop, store, effectSubscription) {
10881
11305
  // to this signal.
10882
11306
  ensureContainsBackRef(effectSubscription, target);
10883
11307
  // TODO is this needed with the preloader?
10884
- addQrlToSerializationCtx(effectSubscription, store.$container$);
11308
+ (import.meta.env.TEST ? !isDomContainer(store.$container$) : isServer) &&
11309
+ addQrlToSerializationCtx(effectSubscription, store.$container$);
10885
11310
  }
10886
11311
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
10887
11312
  target[prop] = value;
@@ -11267,7 +11692,7 @@ function getObjectById(id, stateData) {
11267
11692
  if (typeof id === 'string') {
11268
11693
  id = parseInt(id, 10);
11269
11694
  }
11270
- assertTrue(id < stateData.length, `Invalid reference ${id} >= ${stateData.length}`);
11695
+ isDev && assertTrue(id < stateData.length, `Invalid reference ${id} >= ${stateData.length}`);
11271
11696
  return stateData[id];
11272
11697
  }
11273
11698
  function _createDeserializeContainer(stateData, element) {
@@ -11526,7 +11951,7 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
11526
11951
  }
11527
11952
  if (chunk === '') {
11528
11953
  // Sync QRL
11529
- assertDefined(_containerEl, 'Sync QRL must have container element');
11954
+ isDev && assertDefined(_containerEl, 'Sync QRL must have container element');
11530
11955
  const hash = _containerEl.getAttribute(QInstanceAttr);
11531
11956
  const doc = _containerEl.ownerDocument;
11532
11957
  const qFuncs = getQFuncs(doc, hash);
@@ -11768,8 +12193,8 @@ const _qrlSync = function (fn, serializedFn) {
11768
12193
  const componentQrl = (componentQrl) => {
11769
12194
  // Return a QComponent Factory function.
11770
12195
  function QwikComponent(props, key, flags = 0) {
11771
- assertQrl(componentQrl);
11772
- assertNumber(flags, 'The Qwik Component was not invoked correctly');
12196
+ isDev && assertQrl(componentQrl);
12197
+ isDev && assertNumber(flags, 'The Qwik Component was not invoked correctly');
11773
12198
  const hash = qTest ? 'sX' : componentQrl.$hash$.slice(0, 4);
11774
12199
  const finalKey = hash + ':' + (key ? key : '');
11775
12200
  const InnerCmp = () => { };
@@ -12713,15 +13138,20 @@ const useVisibleTaskQrl = (qrl, opts) => {
12713
13138
  return;
12714
13139
  }
12715
13140
  assertQrl(qrl);
12716
- const task = new Task(1 /* TaskFlags.VISIBLE_TASK */ | 8 /* TaskFlags.DIRTY */, i, iCtx.$hostElement$, qrl, undefined, null);
12717
- set(task);
12718
- useRegisterTaskEvents(task, eagerness);
13141
+ let flags;
12719
13142
  if (!isServerPlatform()) {
12720
- if (!qrl.resolved) {
12721
- qrl.resolve();
12722
- }
13143
+ // In DOM we immediately execute
13144
+ flags = 1 /* TaskFlags.VISIBLE_TASK */ | 8 /* TaskFlags.DIRTY */;
13145
+ qrl.resolve();
12723
13146
  markVNodeDirty(iCtx.$container$, iCtx.$hostElement$, 1 /* ChoreBits.TASKS */);
12724
13147
  }
13148
+ else {
13149
+ // In SSR we defer execution until triggered in DOM
13150
+ flags = 1 /* TaskFlags.VISIBLE_TASK */;
13151
+ }
13152
+ const task = new Task(flags, i, iCtx.$hostElement$, qrl, undefined, null);
13153
+ set(task);
13154
+ useRegisterTaskEvents(task, eagerness);
12725
13155
  };
12726
13156
  const useRegisterTaskEvents = (task, eagerness) => {
12727
13157
  if (eagerness === 'intersection-observer') {