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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/bindings/qwik.darwin-arm64.node +0 -0
  2. package/bindings/qwik.linux-x64-gnu.node +0 -0
  3. package/bindings/qwik.win32-x64-msvc.node +0 -0
  4. package/bindings/qwik_wasm_bg.wasm +0 -0
  5. package/dist/backpatch/package.json +1 -1
  6. package/dist/build/package.json +1 -1
  7. package/dist/cli.mjs +57 -57
  8. package/dist/core-internal.d.ts +32 -32
  9. package/dist/core.min.mjs +1 -1
  10. package/dist/core.mjs +1234 -788
  11. package/dist/core.mjs.map +1 -1
  12. package/dist/core.prod.mjs +815 -553
  13. package/dist/loader/index.mjs +2 -2
  14. package/dist/loader/package.json +1 -1
  15. package/dist/optimizer.mjs +1007 -1000
  16. package/dist/qwikloader.debug.js +1 -1
  17. package/dist/qwikloader.js +1 -1
  18. package/dist/server.mjs +65 -106
  19. package/dist/starters/adapters/cloudflare-workers/README.md +52 -0
  20. package/dist/starters/adapters/cloudflare-workers/adapters/cloudflare-workers/vite.config.ts +15 -0
  21. package/dist/starters/adapters/cloudflare-workers/gitignore +3 -0
  22. package/dist/starters/adapters/cloudflare-workers/package.json +31 -0
  23. package/dist/starters/adapters/cloudflare-workers/public/.assetsignore +4 -0
  24. package/dist/starters/adapters/cloudflare-workers/public/_headers +11 -0
  25. package/dist/starters/adapters/cloudflare-workers/public/_redirects +1 -0
  26. package/dist/starters/adapters/cloudflare-workers/src/entry.cloudflare-pages.tsx +23 -0
  27. package/dist/starters/adapters/cloudflare-workers/worker-configuration.d.ts +5 -0
  28. package/dist/starters/adapters/cloudflare-workers/wrangler.jsonc +41 -0
  29. package/dist/starters/adapters/express/package.json +1 -1
  30. package/dist/starters/features/compiled-i18n/package.json +37 -0
  31. package/dist/starters/features/compiled-i18n/src/components/locale-selector/locale-selector.tsx +30 -0
  32. package/dist/starters/features/{localize → compiled-i18n}/src/entry.ssr.tsx +7 -2
  33. package/dist/starters/features/compiled-i18n/src/routes/plugin@compiled-i18n.ts +28 -0
  34. package/dist/starters/features/csr/package.json +1 -1
  35. package/dist/starters/features/cypress/src/actions/example.action.ts +5 -0
  36. package/dist/starters/features/cypress/src/components/example/example.cy.tsx +50 -8
  37. package/dist/starters/features/cypress/src/components/example/example.tsx +13 -3
  38. package/dist/starters/features/cypress/src/loaders/example.loader.ts +5 -0
  39. package/dist/starters/features/playwright/playwright-report/index.html +0 -19
  40. package/dist/testing/index.d.ts +11 -11
  41. package/dist/testing/index.mjs +2966 -2498
  42. package/dist/testing/package.json +1 -1
  43. package/package.json +14 -11
  44. package/bindings/qwik.darwin-x64.node +0 -0
  45. package/bindings/qwik.wasm.cjs +0 -471
  46. package/dist/starters/features/localize/package.json +0 -37
  47. package/dist/starters/features/localize/src/locales/message.en.json +0 -8
  48. package/dist/starters/features/localize/src/locales/message.it.json +0 -8
  49. package/dist/starters/features/localize/src/routes/[locale]/i18n-utils.ts +0 -94
  50. package/dist/starters/features/localize/src/routes/[locale]/index.tsx +0 -52
  51. package/dist/starters/features/localize/src/routes/[locale]/layout.tsx +0 -12
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.19-dev+0d046fb
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.19-dev+0d046fb";
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)
@@ -476,79 +468,6 @@ function assertNumber(value1, text, ...parts) {
476
468
  }
477
469
  }
478
470
 
479
- let _locale = undefined;
480
- let localAsyncStore;
481
- if (isServer) {
482
- import('node:async_hooks')
483
- .then((module) => {
484
- localAsyncStore = new module.AsyncLocalStorage();
485
- })
486
- .catch(() => {
487
- // ignore if AsyncLocalStorage is not available
488
- });
489
- }
490
- /**
491
- * Retrieve the current locale.
492
- *
493
- * If no current locale and there is no `defaultLocale` the function throws an error.
494
- *
495
- * @returns The locale.
496
- * @public
497
- */
498
- function getLocale(defaultLocale) {
499
- // Prefer per-request locale from local AsyncLocalStorage if available (server-side)
500
- if (localAsyncStore) {
501
- const locale = localAsyncStore.getStore();
502
- if (locale) {
503
- return locale;
504
- }
505
- }
506
- if (_locale === undefined) {
507
- const ctx = tryGetInvokeContext();
508
- if (ctx && ctx.$locale$) {
509
- return ctx.$locale$;
510
- }
511
- if (defaultLocale !== undefined) {
512
- return defaultLocale;
513
- }
514
- throw new Error('Reading `locale` outside of context.');
515
- }
516
- return _locale;
517
- }
518
- /**
519
- * Override the `getLocale` with `lang` within the `fn` execution.
520
- *
521
- * @public
522
- */
523
- function withLocale(locale, fn) {
524
- if (localAsyncStore) {
525
- return localAsyncStore.run(locale, fn);
526
- }
527
- const previousLang = _locale;
528
- try {
529
- _locale = locale;
530
- return fn();
531
- }
532
- finally {
533
- _locale = previousLang;
534
- }
535
- }
536
- /**
537
- * Globally set a lang.
538
- *
539
- * This can be used only in browser. Server execution requires that each request could potentially
540
- * be a different lang, therefore setting a global lang would produce incorrect responses.
541
- *
542
- * @public
543
- */
544
- function setLocale(locale) {
545
- if (localAsyncStore) {
546
- localAsyncStore.enterWith(locale);
547
- return;
548
- }
549
- _locale = locale;
550
- }
551
-
552
471
  /**
553
472
  * A friendly name tag for a VirtualVNode.
554
473
  *
@@ -781,16 +700,11 @@ function vnode_cloneElementWithNamespace(elementVNode, parentVNode, namespace, n
781
700
  }
782
701
  return rootElement;
783
702
  }
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
- }
703
+ // reuse the same object to avoid creating new objects on every call
704
+ const NEW_NAMESPACE_DATA = {
705
+ elementNamespace: HTML_NS,
706
+ elementNamespaceFlag: 0 /* VNodeFlags.NS_html */,
707
+ };
794
708
  function getNewElementNamespaceData(domParentVNode, tagOrVNode) {
795
709
  const parentIsDefaultNamespace = domParentVNode
796
710
  ? !!vnode_getElementName(domParentVNode) && vnode_isDefaultNamespace(domParentVNode)
@@ -815,10 +729,28 @@ function getNewElementNamespaceData(domParentVNode, tagOrVNode) {
815
729
  elementNamespace = isParentSvg ? SVG_NS : isParentMath ? MATH_NS : HTML_NS;
816
730
  elementNamespaceFlag = domParentVNode.flags & 384 /* VNodeFlags.NAMESPACE_MASK */;
817
731
  }
818
- return {
819
- elementNamespace,
820
- elementNamespaceFlag,
821
- };
732
+ NEW_NAMESPACE_DATA.elementNamespace = elementNamespace;
733
+ NEW_NAMESPACE_DATA.elementNamespaceFlag = elementNamespaceFlag;
734
+ return NEW_NAMESPACE_DATA;
735
+ }
736
+ function isSvg(tagOrVNode) {
737
+ if (typeof tagOrVNode === 'string') {
738
+ return isSvgElement(tagOrVNode);
739
+ }
740
+ if (vnode_isElementVNode(tagOrVNode)) {
741
+ return (isSvgElement(vnode_getElementName(tagOrVNode)) || (tagOrVNode.flags & 128 /* VNodeFlags.NS_svg */) !== 0);
742
+ }
743
+ return false;
744
+ }
745
+ function isMath(tagOrVNode) {
746
+ if (typeof tagOrVNode === 'string') {
747
+ return isMathElement(tagOrVNode);
748
+ }
749
+ if (vnode_isElementVNode(tagOrVNode)) {
750
+ return (isMathElement(vnode_getElementName(tagOrVNode)) ||
751
+ (tagOrVNode.flags & 256 /* VNodeFlags.NS_math */) !== 0);
752
+ }
753
+ return false;
822
754
  }
823
755
  function getAttributeNamespace(attributeName) {
824
756
  switch (attributeName) {
@@ -854,18 +786,18 @@ class BackRef {
854
786
  }
855
787
 
856
788
  /** @internal */
857
- class VNode extends BackRef {
789
+ class VNode {
858
790
  flags;
859
791
  parent;
860
792
  previousSibling;
861
793
  nextSibling;
862
794
  props;
795
+ [_EFFECT_BACK_REF] = undefined;
863
796
  slotParent = null;
864
797
  dirty = 0 /* ChoreBits.NONE */;
865
798
  dirtyChildren = null;
866
799
  nextDirtyChildIndex = 0;
867
800
  constructor(flags, parent, previousSibling, nextSibling, props) {
868
- super();
869
801
  this.flags = flags;
870
802
  this.parent = parent;
871
803
  this.previousSibling = previousSibling;
@@ -877,22 +809,29 @@ class VNode extends BackRef {
877
809
  if (isDev) {
878
810
  return vnode_toString.call(this);
879
811
  }
880
- return super.toString();
812
+ return Object.prototype.toString.call(this);
881
813
  }
882
814
  }
883
815
 
884
816
  /** @internal */
885
- class ElementVNode extends VNode {
817
+ class VirtualVNode extends VNode {
886
818
  key;
887
819
  firstChild;
888
820
  lastChild;
889
- node;
890
- elementName;
891
- constructor(key, flags, parent, previousSibling, nextSibling, props, firstChild, lastChild, node, elementName) {
821
+ constructor(key, flags, parent, previousSibling, nextSibling, props, firstChild, lastChild) {
892
822
  super(flags, parent, previousSibling, nextSibling, props);
893
823
  this.key = key;
894
824
  this.firstChild = firstChild;
895
825
  this.lastChild = lastChild;
826
+ }
827
+ }
828
+
829
+ /** @internal */
830
+ class ElementVNode extends VirtualVNode {
831
+ node;
832
+ elementName;
833
+ constructor(key, flags, parent, previousSibling, nextSibling, props, firstChild, lastChild, node, elementName) {
834
+ super(key, flags, parent, previousSibling, nextSibling, props, firstChild, lastChild);
896
835
  this.node = node;
897
836
  this.elementName = elementName;
898
837
  }
@@ -911,19 +850,6 @@ class TextVNode extends VNode {
911
850
  }
912
851
  }
913
852
 
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
853
  /**
928
854
  * @file Cursor queue management for cursor-based scheduling.
929
855
  *
@@ -977,6 +903,7 @@ function getHighestPriorityCursor() {
977
903
  function pauseCursor(cursor, container) {
978
904
  pausedCursorQueue.push(cursor);
979
905
  removeCursorFromQueue(cursor, container, true);
906
+ container.$pausedCursorCount$++;
980
907
  }
981
908
  function resumeCursor(cursor, container) {
982
909
  const index = pausedCursorQueue.indexOf(cursor);
@@ -986,6 +913,7 @@ function resumeCursor(cursor, container) {
986
913
  pausedCursorQueue[index] = pausedCursorQueue[lastIndex];
987
914
  }
988
915
  pausedCursorQueue.pop();
916
+ container.$pausedCursorCount$--;
989
917
  }
990
918
  addCursorToQueue(container, cursor);
991
919
  }
@@ -995,9 +923,6 @@ function resumeCursor(cursor, container) {
995
923
  * @param cursor - The cursor to remove
996
924
  */
997
925
  function removeCursorFromQueue(cursor, container, keepCursorFlag) {
998
- if (container.$cursorCount$ > 0) {
999
- container.$cursorCount$--;
1000
- }
1001
926
  if (!keepCursorFlag) {
1002
927
  cursor.flags &= -65 /* VNodeFlags.Cursor */;
1003
928
  }
@@ -1011,6 +936,7 @@ function removeCursorFromQueue(cursor, container, keepCursorFlag) {
1011
936
  // }
1012
937
  // globalCursorQueue.pop();
1013
938
  globalCursorQueue.splice(index, 1);
939
+ container.$cursorCount$--;
1014
940
  }
1015
941
  }
1016
942
 
@@ -1117,7 +1043,35 @@ class SignalImpl {
1117
1043
  this.$untrackedValue$ = value;
1118
1044
  }
1119
1045
  get value() {
1120
- return setupSignalValueAccess(this, () => (this.$effects$ ||= new Set()), () => this.untrackedValue);
1046
+ const ctx = tryGetInvokeContext();
1047
+ if (!ctx) {
1048
+ return this.untrackedValue;
1049
+ }
1050
+ if (this.$container$ === null) {
1051
+ if (!ctx.$container$) {
1052
+ return this.untrackedValue;
1053
+ }
1054
+ // Grab the container now we have access to it
1055
+ this.$container$ = ctx.$container$;
1056
+ }
1057
+ else {
1058
+ isDev &&
1059
+ assertTrue(!ctx.$container$ || ctx.$container$ === this.$container$, 'Do not use signals across containers');
1060
+ }
1061
+ const effectSubscriber = ctx.$effectSubscriber$;
1062
+ if (effectSubscriber) {
1063
+ // Let's make sure that we have a reference to this effect.
1064
+ // Adding reference is essentially adding a subscription, so if the signal
1065
+ // changes we know who to notify.
1066
+ ensureContainsSubscription((this.$effects$ ||= new Set()), effectSubscriber);
1067
+ // But when effect is scheduled in needs to be able to know which signals
1068
+ // to unsubscribe from. So we need to store the reference from the effect back
1069
+ // to this signal.
1070
+ ensureContainsBackRef(effectSubscriber, this);
1071
+ (import.meta.env.TEST ? !isDomContainer(this.$container$) : isServer) &&
1072
+ addQrlToSerializationCtx(effectSubscriber, this.$container$);
1073
+ }
1074
+ return this.untrackedValue;
1121
1075
  }
1122
1076
  set value(value) {
1123
1077
  if (value !== this.$untrackedValue$) {
@@ -1135,7 +1089,7 @@ class SignalImpl {
1135
1089
  if (isDev) {
1136
1090
  return (`[${this.constructor.name}${this.$flags$ & 1 /* SignalFlags.INVALID */ ? ' INVALID' : ''} ${String(this.$untrackedValue$)}]` +
1137
1091
  (Array.from(this.$effects$ || [])
1138
- .map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' '))
1092
+ .map((e) => '\n -> ' + pad(qwikDebugToString(e.consumer), ' '))
1139
1093
  .join('\n') || ''));
1140
1094
  }
1141
1095
  else {
@@ -1146,34 +1100,33 @@ class SignalImpl {
1146
1100
  return { value: this.$untrackedValue$ };
1147
1101
  }
1148
1102
  }
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
1103
  const setupSignalValueAccess = (target, effectsFn, returnValueFn) => {
1161
1104
  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$;
1105
+ if (!ctx) {
1106
+ return returnValueFn();
1107
+ }
1108
+ if (target.$container$ === null) {
1109
+ if (!ctx.$container$) {
1110
+ return returnValueFn();
1169
1111
  }
1170
- else {
1112
+ // Grab the container now we have access to it
1113
+ target.$container$ = ctx.$container$;
1114
+ }
1115
+ else {
1116
+ isDev &&
1171
1117
  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
- }
1118
+ }
1119
+ const effectSubscriber = ctx.$effectSubscriber$;
1120
+ if (effectSubscriber) {
1121
+ // Let's make sure that we have a reference to this effect.
1122
+ // Adding reference is essentially adding a subscription, so if the signal
1123
+ // changes we know who to notify.
1124
+ ensureContainsSubscription(effectsFn(), effectSubscriber);
1125
+ // But when effect is scheduled in needs to be able to know which signals
1126
+ // to unsubscribe from. So we need to store the reference from the effect back
1127
+ // to this signal.
1128
+ ensureContainsBackRef(effectSubscriber, target);
1129
+ addQrlToSerializationCtx(effectSubscriber, target.$container$);
1177
1130
  }
1178
1131
  return returnValueFn();
1179
1132
  };
@@ -1203,9 +1156,13 @@ const _UNINITIALIZED = Symbol('UNINITIALIZED');
1203
1156
  */
1204
1157
  const EVENT_SUFFIX = '$';
1205
1158
  const isHtmlAttributeAnEventName = (name) => {
1206
- return (name.startsWith("on:" /* EventNameHtmlScope.on */) ||
1207
- name.startsWith("on-window:" /* EventNameHtmlScope.window */) ||
1208
- name.startsWith("on-document:" /* EventNameHtmlScope.document */));
1159
+ if (name.charCodeAt(0) !== 111 /* o */ || name.charCodeAt(1) !== 110 /* n */) {
1160
+ return false;
1161
+ }
1162
+ if (name.charCodeAt(2) === 58 /* : */) {
1163
+ return true; // on:
1164
+ }
1165
+ return name.startsWith("on-window:" /* EventNameHtmlScope.window */) || name.startsWith("on-document:" /* EventNameHtmlScope.document */);
1209
1166
  };
1210
1167
  function jsxEventToHtmlAttribute(jsxEvent) {
1211
1168
  if (jsxEvent.endsWith(EVENT_SUFFIX)) {
@@ -1431,7 +1388,8 @@ const addPropsProxyEffect = (propsProxy, prop) => {
1431
1388
  }
1432
1389
  }
1433
1390
  else {
1434
- assertTrue(!ctx.$container$ || ctx.$container$ === propsProxy.$container$, 'Do not use props across containers');
1391
+ isDev &&
1392
+ assertTrue(!ctx.$container$ || ctx.$container$ === propsProxy.$container$, 'Do not use props across containers');
1435
1393
  }
1436
1394
  }
1437
1395
  const effectSubscriber = ctx?.$effectSubscriber$;
@@ -1444,6 +1402,7 @@ const triggerPropsProxyEffect = (propsProxy, prop) => {
1444
1402
  if (effects) {
1445
1403
  scheduleEffects(propsProxy.$container$, propsProxy, effects);
1446
1404
  }
1405
+ return !!effects;
1447
1406
  };
1448
1407
  function getEffects$1(effects, prop) {
1449
1408
  // TODO: Handle STORE_ALL_PROPS
@@ -1493,30 +1452,6 @@ const cleanupDestroyable = (destroyable) => {
1493
1452
  }
1494
1453
  };
1495
1454
 
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
1455
  /**
1521
1456
  * # ================================
1522
1457
  *
@@ -1529,6 +1464,54 @@ function isSsrNode(value) {
1529
1464
  * "marked as dirty" flag.
1530
1465
  */
1531
1466
  const NEEDS_COMPUTATION = Symbol('invalid');
1467
+ /**
1468
+ * An effect consumer plus type of effect, back references to producers and additional data
1469
+ *
1470
+ * An effect can be trigger by one or more of signal inputs. The first step of re-running an effect
1471
+ * is to clear its subscriptions so that the effect can re add new set of subscriptions. In order to
1472
+ * clear the subscriptions we need to store them here.
1473
+ *
1474
+ * Imagine you have effect such as:
1475
+ *
1476
+ * ```
1477
+ * function effect1() {
1478
+ * console.log(signalA.value ? signalB.value : 'default');
1479
+ * }
1480
+ * ```
1481
+ *
1482
+ * In the above case the `signalB` needs to be unsubscribed when `signalA` is falsy. We do this by
1483
+ * always clearing all of the subscriptions
1484
+ *
1485
+ * The `EffectSubscription` stores
1486
+ *
1487
+ * ```
1488
+ * subscription1 = [effectConsumer1, EffectProperty.COMPONENT, Set[(signalA, signalB)]];
1489
+ * ```
1490
+ *
1491
+ * The `signal1` and `signal2` back references are needed to "clear" existing subscriptions.
1492
+ *
1493
+ * Both `signalA` as well as `signalB` will have a reference to `subscription` to the so that the
1494
+ * effect can be scheduled if either `signalA` or `signalB` triggers. The `subscription1` is shared
1495
+ * between the signals.
1496
+ *
1497
+ * The second position `EffectProperty|string` store the property name of the effect.
1498
+ *
1499
+ * - Property name of the VNode
1500
+ * - `EffectProperty.COMPONENT` if component
1501
+ * - `EffectProperty.VNODE` if VNode
1502
+ */
1503
+ class EffectSubscription {
1504
+ consumer;
1505
+ property;
1506
+ backRef;
1507
+ data;
1508
+ constructor(consumer, property, backRef = null, data = null) {
1509
+ this.consumer = consumer;
1510
+ this.property = property;
1511
+ this.backRef = backRef;
1512
+ this.data = data;
1513
+ }
1514
+ }
1532
1515
  /**
1533
1516
  * # ================================
1534
1517
  *
@@ -1540,6 +1523,30 @@ const STORE_TARGET = Symbol('store.target');
1540
1523
  const STORE_HANDLER = Symbol('store.handler');
1541
1524
  const STORE_ALL_PROPS = Symbol('store.all');
1542
1525
 
1526
+ function getSubscriber(effect, prop, data) {
1527
+ if (!effect[_EFFECT_BACK_REF]) {
1528
+ if (isServer && isSsrNode(effect)) {
1529
+ effect.setProp(QBackRefs, new Map());
1530
+ }
1531
+ else {
1532
+ effect[_EFFECT_BACK_REF] = new Map();
1533
+ }
1534
+ }
1535
+ const subMap = effect[_EFFECT_BACK_REF];
1536
+ let sub = subMap.get(prop);
1537
+ if (!sub) {
1538
+ sub = new EffectSubscription(effect, prop);
1539
+ subMap.set(prop, sub);
1540
+ }
1541
+ if (data) {
1542
+ sub.data = data;
1543
+ }
1544
+ return sub;
1545
+ }
1546
+ function isSsrNode(value) {
1547
+ return '__brand__' in value && value.__brand__ === 'SsrNode';
1548
+ }
1549
+
1543
1550
  const trackFn = (target, container) => (obj, prop) => {
1544
1551
  const ctx = newInvokeContext();
1545
1552
  ctx.$effectSubscriber$ = getSubscriber(target, ":" /* EffectProperty.COMPONENT */);
@@ -1639,7 +1646,7 @@ class ComputedSignalImpl extends SignalImpl {
1639
1646
  }
1640
1647
  get untrackedValue() {
1641
1648
  this.$computeIfNeeded$();
1642
- assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
1649
+ isDev && assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
1643
1650
  return this.$untrackedValue$;
1644
1651
  }
1645
1652
  $computeIfNeeded$() {
@@ -1650,7 +1657,11 @@ class ComputedSignalImpl extends SignalImpl {
1650
1657
  throwIfQRLNotResolved(computeQrl);
1651
1658
  const ctx = tryGetInvokeContext();
1652
1659
  const previousEffectSubscription = ctx?.$effectSubscriber$;
1653
- ctx && (ctx.$effectSubscriber$ = getSubscriber(this, "." /* EffectProperty.VNODE */));
1660
+ if (ctx) {
1661
+ const effectSubscriber = getSubscriber(this, "." /* EffectProperty.VNODE */);
1662
+ clearEffectSubscription(this.$container$, effectSubscriber);
1663
+ ctx.$effectSubscriber$ = effectSubscriber;
1664
+ }
1654
1665
  try {
1655
1666
  const untrackedValue = computeQrl.getFn(ctx)();
1656
1667
  if (isPromise(untrackedValue)) {
@@ -1993,7 +2004,7 @@ const _wrapProp = (...args) => {
1993
2004
  }
1994
2005
  if (isSignal(obj)) {
1995
2006
  if (!(obj instanceof AsyncComputedSignalImpl)) {
1996
- assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
2007
+ isDev && assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
1997
2008
  }
1998
2009
  if (obj instanceof WrappedSignalImpl && obj.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */) {
1999
2010
  return obj;
@@ -2003,11 +2014,11 @@ const _wrapProp = (...args) => {
2003
2014
  if (isPropsProxy(obj)) {
2004
2015
  const constProps = obj[_CONST_PROPS];
2005
2016
  const varProps = obj[_VAR_PROPS];
2006
- if (constProps && prop in constProps) {
2017
+ if (constProps && Object.prototype.hasOwnProperty.call(constProps, prop)) {
2007
2018
  // Const props don't need wrapping
2008
2019
  return constProps[prop];
2009
2020
  }
2010
- else if (prop in varProps) {
2021
+ else if (Object.prototype.hasOwnProperty.call(varProps, prop)) {
2011
2022
  const value = varProps[prop];
2012
2023
  return wrapIfNotSignal(value, args);
2013
2024
  }
@@ -2086,7 +2097,7 @@ class WrappedSignalImpl extends SignalImpl {
2086
2097
  }
2087
2098
  get untrackedValue() {
2088
2099
  this.$computeIfNeeded$();
2089
- assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
2100
+ isDev && assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
2090
2101
  return this.$untrackedValue$;
2091
2102
  }
2092
2103
  $computeIfNeeded$() {
@@ -2137,17 +2148,18 @@ function clearAllEffects(container, consumer) {
2137
2148
  effects.clear();
2138
2149
  }
2139
2150
  function clearEffectSubscription(container, effect) {
2140
- const backRefs = effect[2 /* EffectSubscriptionProp.BACK_REF */];
2151
+ const backRefs = effect.backRef;
2141
2152
  if (!backRefs) {
2142
2153
  return;
2143
2154
  }
2144
2155
  for (const producer of backRefs) {
2145
- if (producer instanceof SignalImpl) {
2146
- clearSignal(container, producer, effect);
2147
- }
2148
- else if (producer instanceof AsyncComputedSignalImpl) {
2156
+ // Check AsyncComputedSignalImpl before SignalImpl since it extends SignalImpl
2157
+ if (producer instanceof AsyncComputedSignalImpl) {
2149
2158
  clearAsyncComputedSignal(producer, effect);
2150
2159
  }
2160
+ else if (producer instanceof SignalImpl) {
2161
+ clearSignal(container, producer, effect);
2162
+ }
2151
2163
  else if (isPropsProxy(producer)) {
2152
2164
  const propsHandler = producer[_PROPS_HANDLER];
2153
2165
  clearStoreOrProps(propsHandler, effect);
@@ -2165,7 +2177,8 @@ function clearSignal(container, producer, effect) {
2165
2177
  if (effects && effects.has(effect)) {
2166
2178
  effects.delete(effect);
2167
2179
  }
2168
- if (producer instanceof WrappedSignalImpl) {
2180
+ if (producer instanceof WrappedSignalImpl && !effects?.size) {
2181
+ // Only clear if there are no more subscribers
2169
2182
  producer.$hostElement$ = undefined;
2170
2183
  clearAllEffects(container, producer);
2171
2184
  }
@@ -2221,15 +2234,17 @@ const useLexicalScope = () => {
2221
2234
  let qrl = context.$qrl$;
2222
2235
  if (!qrl) {
2223
2236
  const el = context.$hostElement$ instanceof ElementVNode ? context.$hostElement$.node : undefined;
2224
- assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
2237
+ isDev && assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
2225
2238
  const containerElement = _getQContainerElement(el);
2226
- assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
2239
+ isDev && assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
2227
2240
  const container = getDomContainer(containerElement);
2241
+ context.$container$ ||= container;
2228
2242
  qrl = container.parseQRL(decodeURIComponent(String(context.$url$)));
2229
2243
  }
2230
2244
  else {
2231
- assertQrl(qrl);
2232
- assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
2245
+ isDev && assertQrl(qrl);
2246
+ isDev &&
2247
+ assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
2233
2248
  }
2234
2249
  return qrl.$captureRef$;
2235
2250
  };
@@ -2253,85 +2268,30 @@ const _chk = (_, element) => {
2253
2268
  signal.value = element.checked;
2254
2269
  };
2255
2270
 
2256
- const _hasOwnProperty$1 = Object.prototype.hasOwnProperty;
2257
- const BIND_VALUE = 'bind:value';
2258
- const BIND_CHECKED = 'bind:checked';
2271
+ const _hasOwnProperty$2 = Object.prototype.hasOwnProperty;
2259
2272
  // TODO store props as the arrays the vnodes also use?
2260
2273
  class JSXNodeImpl {
2261
2274
  type;
2275
+ children;
2262
2276
  toSort;
2263
2277
  key;
2264
2278
  varProps;
2265
2279
  constProps;
2266
- children;
2267
2280
  dev;
2268
2281
  _proxy = null;
2269
2282
  constructor(type, varProps, constProps, children, key, toSort, dev) {
2270
2283
  this.type = type;
2284
+ this.children = children;
2271
2285
  this.toSort = !!toSort;
2272
- this.key = key == null ? null : String(key);
2286
+ this.key = key === null || key === undefined ? null : typeof key === 'string' ? key : '' + key;
2273
2287
  this.varProps = !varProps || isEmpty(varProps) ? EMPTY_OBJ : varProps;
2274
2288
  this.constProps = !constProps || isEmpty(constProps) ? null : constProps;
2275
- this.children = children;
2276
2289
  if (qDev && dev) {
2277
2290
  this.dev = {
2278
2291
  ...dev,
2279
2292
  stack: new Error().stack?.split('\n').slice(2).join('\n'),
2280
2293
  };
2281
2294
  }
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
2295
  seal(this);
2336
2296
  }
2337
2297
  get props() {
@@ -2362,9 +2322,9 @@ const isJSXNode = (n) => {
2362
2322
  return true;
2363
2323
  }
2364
2324
  if (isObject(n) &&
2365
- _hasOwnProperty$1.call(n, 'key') &&
2366
- _hasOwnProperty$1.call(n, 'props') &&
2367
- _hasOwnProperty$1.call(n, 'type')) {
2325
+ _hasOwnProperty$2.call(n, 'key') &&
2326
+ _hasOwnProperty$2.call(n, 'props') &&
2327
+ _hasOwnProperty$2.call(n, 'type')) {
2368
2328
  logWarn(`Duplicate implementations of "JSXNode" found`);
2369
2329
  return true;
2370
2330
  }
@@ -2375,29 +2335,12 @@ const isJSXNode = (n) => {
2375
2335
  }
2376
2336
  };
2377
2337
  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
- }
2338
+ return Object.keys(obj).length === 0;
2399
2339
  };
2400
2340
 
2341
+ const BIND_VALUE = 'bind:value';
2342
+ const BIND_CHECKED = 'bind:checked';
2343
+ const _hasOwnProperty$1 = Object.prototype.hasOwnProperty;
2401
2344
  /**
2402
2345
  * Create a JSXNode with the properties fully split into variable and constant parts, and children
2403
2346
  * separated out. Furthermore, the varProps must be a sorted object, that is, the keys must be
@@ -2415,7 +2358,7 @@ const handleBindProp = (props, prop) => {
2415
2358
  * @internal
2416
2359
  */
2417
2360
  const _jsxSorted = (type, varProps, constProps, children, flags, key, dev) => {
2418
- return untrack(() => new JSXNodeImpl(type, varProps, constProps, children, key, false, dev));
2361
+ return new JSXNodeImpl(type, varProps, constProps, children, key, false, dev);
2419
2362
  };
2420
2363
  /**
2421
2364
  * Create a JSXNode, with the properties split into variable and constant parts, but the variable
@@ -2434,24 +2377,158 @@ const _jsxSorted = (type, varProps, constProps, children, flags, key, dev) => {
2434
2377
  * @internal
2435
2378
  */
2436
2379
  const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
2437
- return untrack(() => {
2380
+ let toSort = false;
2381
+ let constPropsCopied = false;
2382
+ let varPropsCopied = false;
2383
+ let bindValueSignal = null;
2384
+ let bindCheckedSignal = null;
2385
+ // Apply transformations for native HTML elements only
2386
+ if (typeof type === 'string') {
2387
+ // Transform event names (onClick$ -> on:click)
2388
+ if (constProps) {
2389
+ const processedKeys = new Set();
2390
+ for (const k in constProps) {
2391
+ const attr = jsxEventToHtmlAttribute(k);
2392
+ if (attr) {
2393
+ if (!constPropsCopied) {
2394
+ constProps = { ...constProps };
2395
+ constPropsCopied = true;
2396
+ }
2397
+ if (!_hasOwnProperty$1.call(constProps, attr) || processedKeys.has(attr)) {
2398
+ constProps[attr] = constProps[k];
2399
+ }
2400
+ delete constProps[k];
2401
+ }
2402
+ processedKeys.add(k);
2403
+ }
2404
+ }
2438
2405
  if (varProps) {
2406
+ const processedKeys = new Set();
2439
2407
  for (const k in varProps) {
2440
- if (k === 'children') {
2441
- children ||= varProps.children;
2442
- delete varProps.children;
2408
+ const attr = jsxEventToHtmlAttribute(k);
2409
+ if (attr) {
2410
+ if (!varPropsCopied) {
2411
+ varProps = { ...varProps };
2412
+ varPropsCopied = true;
2413
+ }
2414
+ // Transform event name in place
2415
+ if (!_hasOwnProperty$1.call(varProps, attr) || processedKeys.has(attr)) {
2416
+ varProps[attr] = varProps[k];
2417
+ }
2418
+ delete varProps[k];
2419
+ toSort = true;
2420
+ }
2421
+ else if (k === BIND_CHECKED) {
2422
+ // Set flag, will process after walk
2423
+ bindCheckedSignal = varProps[k];
2424
+ }
2425
+ else if (k === BIND_VALUE) {
2426
+ // Set flag, will process after walk
2427
+ bindValueSignal = varProps[k];
2428
+ }
2429
+ processedKeys.add(k);
2430
+ }
2431
+ // Handle bind:* - only in varProps, bind:* should be moved to varProps
2432
+ if (bindCheckedSignal || bindValueSignal) {
2433
+ if (!varPropsCopied) {
2434
+ varProps = { ...varProps };
2435
+ varPropsCopied = true;
2436
+ }
2437
+ if (bindCheckedSignal) {
2438
+ delete varProps[BIND_CHECKED];
2439
+ varProps.checked = bindCheckedSignal;
2440
+ const handler = createQRL(null, '_chk', _chk, null, null, [bindCheckedSignal]);
2441
+ // Move on:input from constProps if it exists
2442
+ if (constProps && _hasOwnProperty$1.call(constProps, 'on:input')) {
2443
+ if (!constPropsCopied) {
2444
+ constProps = { ...constProps };
2445
+ constPropsCopied = true;
2446
+ }
2447
+ const existingHandler = constProps['on:input'];
2448
+ delete constProps['on:input'];
2449
+ toSort = mergeHandlers(varProps, 'on:input', existingHandler) || toSort;
2450
+ }
2451
+ toSort = mergeHandlers(varProps, 'on:input', handler) || toSort;
2452
+ }
2453
+ else if (bindValueSignal) {
2454
+ delete varProps[BIND_VALUE];
2455
+ varProps.value = bindValueSignal;
2456
+ const handler = createQRL(null, '_val', _val, null, null, [bindValueSignal]);
2457
+ // Move on:input from constProps if it exists
2458
+ if (constProps && _hasOwnProperty$1.call(constProps, 'on:input')) {
2459
+ if (!constPropsCopied) {
2460
+ constProps = { ...constProps };
2461
+ constPropsCopied = true;
2462
+ }
2463
+ const existingHandler = constProps['on:input'];
2464
+ delete constProps['on:input'];
2465
+ toSort = mergeHandlers(varProps, 'on:input', existingHandler) || toSort;
2466
+ }
2467
+ toSort = mergeHandlers(varProps, 'on:input', handler) || toSort;
2468
+ }
2469
+ }
2470
+ }
2471
+ // Transform className -> class
2472
+ if (varProps && _hasOwnProperty$1.call(varProps, 'className')) {
2473
+ if (!varPropsCopied) {
2474
+ varProps = { ...varProps };
2475
+ varPropsCopied = true;
2476
+ }
2477
+ varProps.class = varProps.className;
2478
+ varProps.className = undefined;
2479
+ toSort = true;
2480
+ if (qDev) {
2481
+ logOnceWarn(`jsx${dev ? ` ${dev.fileName}${dev?.lineNumber ? `:${dev.lineNumber}` : ''}` : ''}: \`className\` is deprecated. Use \`class\` instead.`);
2482
+ }
2483
+ }
2484
+ if (constProps && _hasOwnProperty$1.call(constProps, 'className')) {
2485
+ if (!constPropsCopied) {
2486
+ constProps = { ...constProps };
2487
+ constPropsCopied = true;
2488
+ }
2489
+ constProps.class = constProps.className;
2490
+ constProps.className = undefined;
2491
+ if (qDev) {
2492
+ logOnceWarn(`jsx${dev ? ` ${dev.fileName}${dev?.lineNumber ? `:${dev.lineNumber}` : ''}` : ''}: \`className\` is deprecated. Use \`class\` instead.`);
2493
+ }
2494
+ }
2495
+ }
2496
+ if (varProps) {
2497
+ for (const k in varProps) {
2498
+ if (k === 'children') {
2499
+ if (!varPropsCopied) {
2500
+ varProps = { ...varProps };
2501
+ varPropsCopied = true;
2443
2502
  }
2444
- else if (k === 'key') {
2445
- key ||= varProps.key;
2446
- delete varProps.key;
2503
+ children ||= varProps.children;
2504
+ delete varProps.children;
2505
+ }
2506
+ else if (k === 'key') {
2507
+ if (!varPropsCopied) {
2508
+ varProps = { ...varProps };
2509
+ varPropsCopied = true;
2447
2510
  }
2448
- else if (constProps && k in constProps) {
2449
- delete varProps[k];
2511
+ key ||= varProps.key;
2512
+ delete varProps.key;
2513
+ }
2514
+ else if (constProps && k in constProps) {
2515
+ if (!varPropsCopied) {
2516
+ varProps = { ...varProps };
2517
+ varPropsCopied = true;
2518
+ }
2519
+ delete varProps[k];
2520
+ }
2521
+ else if (varProps[k] === null) {
2522
+ if (!varPropsCopied) {
2523
+ varProps = { ...varProps };
2524
+ varPropsCopied = true;
2450
2525
  }
2526
+ // Clean up null markers (from event conversions)
2527
+ delete varProps[k];
2451
2528
  }
2452
2529
  }
2453
- return new JSXNodeImpl(type, varProps, constProps, children, key, true, dev);
2454
- });
2530
+ }
2531
+ return new JSXNodeImpl(type, varProps, constProps, children, key, toSort || true, dev);
2455
2532
  };
2456
2533
  /** @internal @deprecated v1 compat */
2457
2534
  const _jsxC = (type, mutable, _flags, key) => jsx(type, mutable, key);
@@ -2547,7 +2624,7 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2547
2624
  let isInlineComponent = false;
2548
2625
  if (componentQRL === null) {
2549
2626
  componentQRL = container.getHostProp(renderHost, OnRenderProp);
2550
- assertDefined(componentQRL, 'No Component found at this location');
2627
+ isDev && assertDefined(componentQRL, 'No Component found at this location');
2551
2628
  }
2552
2629
  if (isQrl(componentQRL)) {
2553
2630
  props = props || container.getHostProp(renderHost, ELEMENT_PROPS) || EMPTY_OBJ;
@@ -2566,6 +2643,7 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2566
2643
  const inlineComponent = componentQRL;
2567
2644
  componentFn = () => invokeApply(iCtx, inlineComponent, [props || EMPTY_OBJ]);
2568
2645
  }
2646
+ const isSsr = import.meta.env.TEST ? isServerPlatform() : isServer;
2569
2647
  const executeComponentWithPromiseExceptionRetry = (retryCount = 0) => safeCall(() => {
2570
2648
  if (!isInlineComponent) {
2571
2649
  container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
@@ -2576,6 +2654,19 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2576
2654
  }
2577
2655
  return maybeThen(componentFn(props), (jsx) => maybeThen(iCtx.$waitOn$, () => jsx));
2578
2656
  }, (jsx) => {
2657
+ // In SSR, check if the component was marked dirty (COMPONENT bit) during execution.
2658
+ // This happens when something completes and updates reactive primitives
2659
+ // while we're waiting on $waitOn$. If so, we need to re-execute the component
2660
+ // to get fresh JSX with updated values.
2661
+ if (isSsr && !isInlineComponent) {
2662
+ const ssrNode = renderHost;
2663
+ if (ssrNode.dirty & 4 /* ChoreBits.COMPONENT */) {
2664
+ ssrNode.dirty &= ~4 /* ChoreBits.COMPONENT */;
2665
+ if (retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
2666
+ return executeComponentWithPromiseExceptionRetry(retryCount + 1);
2667
+ }
2668
+ }
2669
+ }
2579
2670
  const useOnEvents = container.getHostProp(renderHost, USE_ON_LOCAL);
2580
2671
  if (useOnEvents) {
2581
2672
  return addUseOnEvents(jsx, useOnEvents);
@@ -2744,7 +2835,7 @@ function injectPlaceholderElement(jsx) {
2744
2835
  return [placeholder, jsx];
2745
2836
  }
2746
2837
  // For primitives, we can't add children, so we wrap them in a fragment.
2747
- if (isPrimitive(jsx)) {
2838
+ if (isPrimitiveOrNullUndefined(jsx)) {
2748
2839
  const placeholder = createPlaceholderScriptNode();
2749
2840
  return [placeholder, _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null)];
2750
2841
  }
@@ -2758,7 +2849,7 @@ function injectPlaceholderElement(jsx) {
2758
2849
  }
2759
2850
  /** @returns An empty <script> element for adding qwik metadata attributes to */
2760
2851
  function createPlaceholderScriptNode() {
2761
- return new JSXNodeImpl('script', null, { hidden: '' });
2852
+ return new JSXNodeImpl('script', null, { hidden: '' }, null, null);
2762
2853
  }
2763
2854
 
2764
2855
  /**
@@ -2874,15 +2965,12 @@ const _restProps = (props, omit = [], target = {}) => {
2874
2965
  varPropsTarget[key] = varProps[key];
2875
2966
  }
2876
2967
  }
2877
- return createPropsProxy(new JSXNodeImpl(null, varPropsTarget, constPropsTarget));
2968
+ return createPropsProxy(new JSXNodeImpl(null, varPropsTarget, constPropsTarget, null, null));
2878
2969
  };
2879
2970
 
2880
2971
  const styleContent = (styleId) => {
2881
2972
  return ComponentStylesPrefixContent + styleId;
2882
2973
  };
2883
- function hasClassAttr(props) {
2884
- return 'class' in props;
2885
- }
2886
2974
  function isClassAttr(key) {
2887
2975
  return key === 'class';
2888
2976
  }
@@ -3048,132 +3136,22 @@ function isEnumeratedBooleanAttribute(key) {
3048
3136
  return isAriaAttribute(key) || ['spellcheck', 'draggable', 'contenteditable'].includes(key);
3049
3137
  }
3050
3138
  const setValueForStyle = (styleName, value) => {
3051
- if (typeof value === 'number' && value !== 0 && !isUnitlessNumber(styleName)) {
3052
- return value + 'px';
3053
- }
3054
- return value;
3055
- };
3056
- function isAriaAttribute(prop) {
3057
- return prop.startsWith('aria-');
3058
- }
3059
- const styleKey = (qStyles, index) => {
3060
- assertQrl(qStyles);
3061
- return `${hashCode(qStyles.$hash$)}-${index}`;
3062
- };
3063
-
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$;
3139
+ if (typeof value === 'number' && value !== 0 && !isUnitlessNumber(styleName)) {
3140
+ return value + 'px';
3156
3141
  }
3157
- }
3158
- /** @internal */
3159
- const isTask = (value) => {
3160
- return value instanceof Task;
3142
+ return value;
3161
3143
  };
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 */);
3144
+ function isAriaAttribute(prop) {
3145
+ return prop.startsWith('aria-');
3146
+ }
3147
+ const styleKey = (qStyles, index) => {
3148
+ assertQrl(qStyles);
3149
+ return `${hashCode(qStyles.$hash$)}-${index}`;
3172
3150
  };
3173
3151
 
3174
3152
  /** @internal */
3175
3153
  const mapApp_findIndx = (array, key, start) => {
3176
- assertTrue(start % 2 === 0, 'Expecting even number.');
3154
+ isDev && assertTrue(start % 2 === 0, 'Expecting even number.');
3177
3155
  let bottom = start >> 1;
3178
3156
  let top = (array.length - 2) >> 1;
3179
3157
  while (bottom <= top) {
@@ -3220,6 +3198,127 @@ const mapArray_has = (array, key, start) => {
3220
3198
  return mapApp_findIndx(array, key, start) >= 0;
3221
3199
  };
3222
3200
 
3201
+ class DeleteOperation {
3202
+ target;
3203
+ constructor(target) {
3204
+ this.target = target;
3205
+ }
3206
+ }
3207
+ class RemoveAllChildrenOperation {
3208
+ target;
3209
+ constructor(target) {
3210
+ this.target = target;
3211
+ }
3212
+ }
3213
+ class SetTextOperation {
3214
+ target;
3215
+ text;
3216
+ operationType = 16 /* VNodeOperationType.SetText */;
3217
+ constructor(target, text) {
3218
+ this.target = target;
3219
+ this.text = text;
3220
+ }
3221
+ }
3222
+ class InsertOrMoveOperation {
3223
+ target;
3224
+ parent;
3225
+ beforeTarget;
3226
+ constructor(target, parent, beforeTarget) {
3227
+ this.target = target;
3228
+ this.parent = parent;
3229
+ this.beforeTarget = beforeTarget;
3230
+ }
3231
+ }
3232
+ class SetAttributeOperation {
3233
+ target;
3234
+ attrName;
3235
+ attrValue;
3236
+ scopedStyleIdPrefix;
3237
+ isSvg;
3238
+ constructor(target, attrName, attrValue, scopedStyleIdPrefix, isSvg) {
3239
+ this.target = target;
3240
+ this.attrName = attrName;
3241
+ this.attrValue = attrValue;
3242
+ this.scopedStyleIdPrefix = scopedStyleIdPrefix;
3243
+ this.isSvg = isSvg;
3244
+ }
3245
+ }
3246
+ /** Factory functions to create operations with consistent hidden classes. */
3247
+ const createDeleteOperation = (target) => new DeleteOperation(target);
3248
+ const createRemoveAllChildrenOperation = (target) => new RemoveAllChildrenOperation(target);
3249
+ const createSetTextOperation = (target, text) => new SetTextOperation(target, text);
3250
+ const createInsertOrMoveOperation = (target, parent, beforeTarget) => new InsertOrMoveOperation(target, parent, beforeTarget);
3251
+ const createSetAttributeOperation = (target, attrName, attrValue, scopedStyleIdPrefix = null, isSvg = false) => new SetAttributeOperation(target, attrName, attrValue, scopedStyleIdPrefix, isSvg);
3252
+
3253
+ function callQrl(container, host, qrl, event, element, useGetObjectById) {
3254
+ const getObjectById = useGetObjectById ? container?.$getObjectById$ || null : null;
3255
+ const singleItem = vnode_getProp(host, ITERATION_ITEM_SINGLE, getObjectById);
3256
+ if (singleItem !== null) {
3257
+ return qrl(event, element, singleItem);
3258
+ }
3259
+ const multiItems = vnode_getProp(host, ITERATION_ITEM_MULTI, getObjectById);
3260
+ if (multiItems !== null) {
3261
+ return qrl(event, element, ...multiItems);
3262
+ }
3263
+ return qrl(event, element);
3264
+ }
3265
+ /**
3266
+ * This is called by qwik-loader to run a QRL. It has to be synchronous.
3267
+ *
3268
+ * @internal
3269
+ */
3270
+ const _run = (...args) => {
3271
+ // This will already check container
3272
+ const [qrl] = useLexicalScope();
3273
+ const context = getInvokeContext();
3274
+ const hostElement = context.$hostElement$;
3275
+ if (hostElement) {
3276
+ context.$container$ ||= getDomContainer(hostElement.node);
3277
+ vnode_ensureElementInflated(hostElement);
3278
+ return retryOnPromise(() => {
3279
+ if (!(hostElement.flags & 32 /* VNodeFlags.Deleted */)) {
3280
+ return callQrl(context.$container$, hostElement, qrl, args[0], args[1], true).catch((err) => {
3281
+ const container = context.$container$;
3282
+ if (container) {
3283
+ container.handleError(err, hostElement);
3284
+ }
3285
+ else {
3286
+ throw err;
3287
+ }
3288
+ });
3289
+ }
3290
+ });
3291
+ }
3292
+ };
3293
+
3294
+ let _setAttribute = null;
3295
+ const fastSetAttribute = (target, name, value) => {
3296
+ if (!_setAttribute) {
3297
+ _setAttribute = target.setAttribute;
3298
+ }
3299
+ _setAttribute.call(target, name, value);
3300
+ };
3301
+ let _setAttributeNS = null;
3302
+ const fastSetAttributeNS = (target, namespace, name, value) => {
3303
+ if (!_setAttributeNS) {
3304
+ _setAttributeNS = target.setAttributeNS;
3305
+ }
3306
+ _setAttributeNS.call(target, namespace, name, value);
3307
+ };
3308
+ function directSetAttribute(element, attrName, attrValue, isSvg) {
3309
+ if (attrValue != null) {
3310
+ if (isSvg) {
3311
+ // only svg elements can have namespace attributes
3312
+ const namespace = getAttributeNamespace(attrName);
3313
+ if (namespace) {
3314
+ fastSetAttributeNS(element, namespace, attrName, attrValue);
3315
+ return;
3316
+ }
3317
+ }
3318
+ fastSetAttribute(element, attrName, attrValue);
3319
+ }
3320
+ }
3321
+
3223
3322
  /**
3224
3323
  * Helper to get the next sibling of a VNode. Extracted to module scope to help V8 inline it
3225
3324
  * reliably.
@@ -3229,9 +3328,12 @@ function peekNextSibling(vCurrent) {
3229
3328
  }
3230
3329
  const _hasOwnProperty = Object.prototype.hasOwnProperty;
3231
3330
  /** 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);
3331
+ function setAttribute(journal, vnode, key, value, scopedStyleIdPrefix, originalValue) {
3332
+ import.meta.env.TEST &&
3333
+ scopedStyleIdPrefix &&
3334
+ vnode_setProp(vnode, debugStyleScopeIdPrefixAttr, scopedStyleIdPrefix);
3335
+ vnode_setProp(vnode, key, originalValue);
3336
+ addVNodeOperation(journal, createSetAttributeOperation(vnode.node, key, value, scopedStyleIdPrefix, (vnode.flags & 128 /* VNodeFlags.NS_svg */) !== 0));
3235
3337
  }
3236
3338
  const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyleIdPrefix) => {
3237
3339
  const diffContext = {
@@ -3253,6 +3355,7 @@ const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyle
3253
3355
  jsxIdx: 0,
3254
3356
  jsxCount: 0,
3255
3357
  shouldAdvance: true,
3358
+ isCreationMode: false,
3256
3359
  subscriptionData: {
3257
3360
  const: new SubscriptionData({
3258
3361
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
@@ -3281,8 +3384,8 @@ const vnode_diff = (container, journal, jsxNode, vStartNode, cursor, scopedStyle
3281
3384
  //////////////////////////////////////////////
3282
3385
  //////////////////////////////////////////////
3283
3386
  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');
3387
+ isDev && assertFalse(vnode_isVNode(jsxNode), 'JSXNode should not be a VNode');
3388
+ isDev && assertTrue(vnode_isVNode(vStartNode), 'vStartNode should be a VNode');
3286
3389
  diffContext.vParent = vStartNode;
3287
3390
  diffContext.vNewNode = null;
3288
3391
  diffContext.vCurrent = vnode_getFirstChild(vStartNode);
@@ -3293,7 +3396,8 @@ function diff(diffContext, jsxNode, vStartNode) {
3293
3396
  }
3294
3397
  while (diffContext.stack.length) {
3295
3398
  while (diffContext.jsxIdx < diffContext.jsxCount) {
3296
- assertFalse(diffContext.vParent === diffContext.vCurrent, "Parent and current can't be the same");
3399
+ isDev &&
3400
+ assertFalse(diffContext.vParent === diffContext.vCurrent, "Parent and current can't be the same");
3297
3401
  if (typeof diffContext.jsxValue === 'string') {
3298
3402
  expectText(diffContext, diffContext.jsxValue);
3299
3403
  }
@@ -3301,26 +3405,7 @@ function diff(diffContext, jsxNode, vStartNode) {
3301
3405
  expectText(diffContext, String(diffContext.jsxValue));
3302
3406
  }
3303
3407
  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)) {
3408
+ if (isJSXNode(diffContext.jsxValue)) {
3324
3409
  const type = diffContext.jsxValue.type;
3325
3410
  if (typeof type === 'string') {
3326
3411
  expectNoMoreTextNodes(diffContext);
@@ -3368,6 +3453,28 @@ function diff(diffContext, jsxNode, vStartNode) {
3368
3453
  }
3369
3454
  }
3370
3455
  }
3456
+ else if (Array.isArray(diffContext.jsxValue)) {
3457
+ descend(diffContext, diffContext.jsxValue, false);
3458
+ }
3459
+ else if (isSignal(diffContext.jsxValue)) {
3460
+ expectVirtual(diffContext, "S" /* VirtualType.WrappedSignal */, null);
3461
+ const unwrappedSignal = diffContext.jsxValue instanceof WrappedSignalImpl
3462
+ ? diffContext.jsxValue.$unwrapIfSignal$()
3463
+ : diffContext.jsxValue;
3464
+ const signals = diffContext.vCurrent?.[_EFFECT_BACK_REF]?.get("." /* EffectProperty.VNODE */)?.backRef;
3465
+ let hasUnwrappedSignal = signals?.has(unwrappedSignal);
3466
+ if (signals && unwrappedSignal instanceof WrappedSignalImpl) {
3467
+ hasUnwrappedSignal = containsWrappedSignal(signals, unwrappedSignal);
3468
+ }
3469
+ if (!hasUnwrappedSignal) {
3470
+ const vHost = (diffContext.vNewNode || diffContext.vCurrent);
3471
+ descend(diffContext, resolveSignalAndDescend(diffContext, () => trackSignalAndAssignHost(unwrappedSignal, vHost, "." /* EffectProperty.VNODE */, diffContext.container)), true);
3472
+ }
3473
+ }
3474
+ else if (isPromise(diffContext.jsxValue)) {
3475
+ expectVirtual(diffContext, "A" /* VirtualType.Awaited */, null);
3476
+ diffContext.asyncQueue.push(diffContext.jsxValue, diffContext.vNewNode || diffContext.vCurrent);
3477
+ }
3371
3478
  }
3372
3479
  else if (diffContext.jsxValue === SkipRender) ;
3373
3480
  else {
@@ -3447,7 +3554,12 @@ function descend(diffContext, children, descendVNode, shouldExpectNoChildren = t
3447
3554
  }
3448
3555
  stackPush(diffContext, children, descendVNode);
3449
3556
  if (descendVNode) {
3450
- assertDefined(diffContext.vCurrent || diffContext.vNewNode, 'Expecting vCurrent to be defined.');
3557
+ isDev &&
3558
+ assertDefined(diffContext.vCurrent || diffContext.vNewNode, 'Expecting vCurrent to be defined.');
3559
+ const creationMode = diffContext.isCreationMode ||
3560
+ !!diffContext.vNewNode ||
3561
+ !vnode_getFirstChild(diffContext.vCurrent);
3562
+ diffContext.isCreationMode = creationMode;
3451
3563
  diffContext.vSideBuffer = null;
3452
3564
  diffContext.vSiblings = null;
3453
3565
  diffContext.vSiblingsArray = null;
@@ -3460,6 +3572,7 @@ function descend(diffContext, children, descendVNode, shouldExpectNoChildren = t
3460
3572
  function ascend(diffContext) {
3461
3573
  const descendVNode = diffContext.stack.pop(); // boolean: descendVNode
3462
3574
  if (descendVNode) {
3575
+ diffContext.isCreationMode = diffContext.stack.pop();
3463
3576
  diffContext.vSideBuffer = diffContext.stack.pop();
3464
3577
  diffContext.vSiblings = diffContext.stack.pop();
3465
3578
  diffContext.vSiblingsArray = diffContext.stack.pop();
@@ -3476,7 +3589,7 @@ function ascend(diffContext) {
3476
3589
  function stackPush(diffContext, children, descendVNode) {
3477
3590
  diffContext.stack.push(diffContext.jsxChildren, diffContext.jsxIdx, diffContext.jsxCount, diffContext.jsxValue);
3478
3591
  if (descendVNode) {
3479
- diffContext.stack.push(diffContext.vParent, diffContext.vCurrent, diffContext.vNewNode, diffContext.vSiblingsArray, diffContext.vSiblings, diffContext.vSideBuffer);
3592
+ diffContext.stack.push(diffContext.vParent, diffContext.vCurrent, diffContext.vNewNode, diffContext.vSiblingsArray, diffContext.vSiblings, diffContext.vSideBuffer, diffContext.isCreationMode);
3480
3593
  }
3481
3594
  diffContext.stack.push(descendVNode);
3482
3595
  if (Array.isArray(children)) {
@@ -3604,12 +3717,20 @@ function expectSlot(diffContext) {
3604
3717
  else if (vProjectedNode === diffContext.vCurrent) ;
3605
3718
  else {
3606
3719
  // move from q:template to the target node
3720
+ const oldParent = vProjectedNode.parent;
3607
3721
  vnode_insertBefore(diffContext.journal, diffContext.vParent, (diffContext.vNewNode = vProjectedNode), diffContext.vCurrent && getInsertBefore(diffContext));
3608
3722
  vnode_setProp(diffContext.vNewNode, QSlot, slotNameKey);
3609
3723
  vHost && vnode_setProp(vHost, slotNameKey, diffContext.vNewNode);
3610
3724
  isDev &&
3611
3725
  vnode_setProp(diffContext.vNewNode, DEBUG_TYPE, "P" /* VirtualType.Projection */);
3612
3726
  isDev && vnode_setProp(diffContext.vNewNode, 'q:code', 'expectSlot' + count++);
3727
+ // If we moved from a q:template and it's now empty, remove it
3728
+ if (oldParent &&
3729
+ vnode_isElementVNode(oldParent) &&
3730
+ !oldParent.firstChild &&
3731
+ vnode_getElementName(oldParent) === QTemplate) {
3732
+ vnode_remove(diffContext.journal, oldParent.parent, oldParent, true);
3733
+ }
3613
3734
  }
3614
3735
  return true;
3615
3736
  }
@@ -3684,7 +3805,8 @@ function expectNoChildren(diffContext, removeDOM = true) {
3684
3805
  }
3685
3806
  /** Expect no more nodes - Any nodes which are still at cursor, need to be removed. */
3686
3807
  function expectNoMore(diffContext) {
3687
- assertFalse(diffContext.vParent === diffContext.vCurrent, "Parent and current can't be the same");
3808
+ isDev &&
3809
+ assertFalse(diffContext.vParent === diffContext.vCurrent, "Parent and current can't be the same");
3688
3810
  if (diffContext.vCurrent !== null) {
3689
3811
  while (diffContext.vCurrent) {
3690
3812
  const toRemove = diffContext.vCurrent;
@@ -3714,20 +3836,6 @@ function expectNoMoreTextNodes(diffContext) {
3714
3836
  */
3715
3837
  function createNewElement(diffContext, jsx, elementName, currentFile) {
3716
3838
  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
3839
  const { constProps } = jsx;
3732
3840
  let needsQDispatchEventPatch = false;
3733
3841
  if (constProps) {
@@ -3778,7 +3886,7 @@ function createNewElement(diffContext, jsx, elementName, currentFile) {
3778
3886
  }
3779
3887
  if (isPromise(value)) {
3780
3888
  const vHost = diffContext.vNewNode;
3781
- const attributePromise = value.then((resolvedValue) => setAttribute(key, resolvedValue, vHost));
3889
+ const attributePromise = value.then((resolvedValue) => directSetAttribute(element, key, serializeAttribute(key, resolvedValue, diffContext.scopedStyleIdPrefix), (vHost.flags & 128 /* VNodeFlags.NS_svg */) !== 0));
3782
3890
  diffContext.asyncAttributePromises.push(attributePromise);
3783
3891
  continue;
3784
3892
  }
@@ -3799,7 +3907,7 @@ function createNewElement(diffContext, jsx, elementName, currentFile) {
3799
3907
  element.value = escapeHTML(value || '');
3800
3908
  continue;
3801
3909
  }
3802
- setAttribute(key, value, diffContext.vNewNode);
3910
+ directSetAttribute(element, key, serializeAttribute(key, value, diffContext.scopedStyleIdPrefix), (diffContext.vNewNode.flags & 128 /* VNodeFlags.NS_svg */) !== 0);
3803
3911
  }
3804
3912
  }
3805
3913
  const key = jsx.key;
@@ -3808,7 +3916,8 @@ function createNewElement(diffContext, jsx, elementName, currentFile) {
3808
3916
  }
3809
3917
  // append class attribute if styleScopedId exists and there is no class attribute
3810
3918
  if (diffContext.scopedStyleIdPrefix) {
3811
- const classAttributeExists = hasClassAttr(jsx.varProps) || (jsx.constProps && hasClassAttr(jsx.constProps));
3919
+ const classAttributeExists = _hasOwnProperty.call(jsx.varProps, 'class') ||
3920
+ (jsx.constProps && _hasOwnProperty.call(jsx.constProps, 'class'));
3812
3921
  if (!classAttributeExists) {
3813
3922
  element.setAttribute('class', diffContext.scopedStyleIdPrefix);
3814
3923
  }
@@ -3818,28 +3927,35 @@ function createNewElement(diffContext, jsx, elementName, currentFile) {
3818
3927
  }
3819
3928
  function createElementWithNamespace(diffContext, elementName) {
3820
3929
  const domParentVNode = vnode_getDomParentVNode(diffContext.vParent, true);
3821
- const { elementNamespace, elementNamespaceFlag } = getNewElementNamespaceData(domParentVNode, elementName);
3822
- const element = diffContext.container.document.createElementNS(elementNamespace, elementName);
3930
+ const namespaceData = getNewElementNamespaceData(domParentVNode, elementName);
3931
+ const currentDocument = import.meta.env.TEST ? diffContext.container.document : document;
3932
+ const element = namespaceData.elementNamespaceFlag === 0 /* VNodeFlags.NS_html */
3933
+ ? currentDocument.createElement(elementName)
3934
+ : currentDocument.createElementNS(namespaceData.elementNamespace, elementName);
3823
3935
  diffContext.vNewNode = vnode_newElement(element, elementName);
3824
- diffContext.vNewNode.flags |= elementNamespaceFlag;
3936
+ diffContext.vNewNode.flags |= namespaceData.elementNamespaceFlag;
3825
3937
  return element;
3826
3938
  }
3827
3939
  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
3940
  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
- }
3941
+ if (diffContext.isCreationMode) {
3942
+ needsQDispatchEventPatch = createNewElement(diffContext, jsx, elementName, null);
3839
3943
  }
3840
3944
  else {
3841
- // delete the key from the side buffer if it is the same element
3842
- deleteFromSideBuffer(diffContext, elementName, jsxKey);
3945
+ const isElementVNode = diffContext.vCurrent && vnode_isElementVNode(diffContext.vCurrent);
3946
+ const isSameElementName = isElementVNode && elementName === vnode_getElementName(diffContext.vCurrent);
3947
+ const jsxKey = jsx.key;
3948
+ const currentKey = isElementVNode && diffContext.vCurrent.key;
3949
+ if (!isSameElementName || jsxKey !== currentKey) {
3950
+ const sideBufferKey = getSideBufferKey(elementName, jsxKey);
3951
+ if (moveOrCreateKeyedNode(diffContext, elementName, jsxKey, sideBufferKey, diffContext.vParent)) {
3952
+ needsQDispatchEventPatch = createNewElement(diffContext, jsx, elementName, null);
3953
+ }
3954
+ }
3955
+ else {
3956
+ // delete the key from the side buffer if it is the same element
3957
+ deleteFromSideBuffer(diffContext, elementName, jsxKey);
3958
+ }
3843
3959
  }
3844
3960
  // reconcile attributes
3845
3961
  const jsxProps = jsx.varProps;
@@ -3847,7 +3963,8 @@ function expectElement(diffContext, jsx, elementName) {
3847
3963
  const element = vNode.node;
3848
3964
  if (jsxProps) {
3849
3965
  needsQDispatchEventPatch =
3850
- diffProps(diffContext, vNode, jsxProps, (vNode.props ||= {}), (isDev && getFileLocationFromJsx(jsx.dev)) || null) || needsQDispatchEventPatch;
3966
+ diffProps(diffContext, vNode, jsxProps, (isDev && getFileLocationFromJsx(jsx.dev)) || null) ||
3967
+ needsQDispatchEventPatch;
3851
3968
  }
3852
3969
  if (needsQDispatchEventPatch) {
3853
3970
  // Event handler needs to be patched onto the element.
@@ -3864,7 +3981,7 @@ function expectElement(diffContext, jsx, elementName) {
3864
3981
  ];
3865
3982
  for (const qrl of qrls.flat(2)) {
3866
3983
  if (qrl) {
3867
- catchError(qrl(event, element), (e) => {
3984
+ callQrl(diffContext.container, vNode, qrl, event, vNode.node, false).catch((e) => {
3868
3985
  diffContext.container.handleError(e, vNode);
3869
3986
  });
3870
3987
  }
@@ -3873,92 +3990,127 @@ function expectElement(diffContext, jsx, elementName) {
3873
3990
  }
3874
3991
  }
3875
3992
  }
3876
- function diffProps(diffContext, vnode, newAttrs, oldAttrs, currentFile) {
3877
- vnode_ensureElementInflated(vnode);
3993
+ function diffProps(diffContext, vnode, newAttrs, currentFile) {
3994
+ if (!diffContext.isCreationMode) {
3995
+ // inflate only resumed vnodes
3996
+ vnode_ensureElementInflated(vnode);
3997
+ }
3998
+ const oldAttrs = vnode.props;
3878
3999
  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;
4000
+ // Actual diffing logic
4001
+ // Apply all new attributes
4002
+ for (const key in newAttrs) {
4003
+ const newValue = newAttrs[key];
4004
+ const isEvent = isHtmlAttributeAnEventName(key);
4005
+ if (oldAttrs && _hasOwnProperty.call(oldAttrs, key)) {
4006
+ const oldValue = oldAttrs[key];
4007
+ if (newValue !== oldValue) {
4008
+ if (newValue instanceof WrappedSignalImpl &&
4009
+ oldValue instanceof WrappedSignalImpl &&
4010
+ areWrappedSignalsEqual(newValue, oldValue)) {
4011
+ continue;
4012
+ }
4013
+ if (isEvent) {
4014
+ const result = recordJsxEvent(diffContext, vnode, key, newValue, currentFile);
4015
+ patchEventDispatch ||= result;
4016
+ }
4017
+ else {
4018
+ patchProperty(diffContext, vnode, key, newValue, currentFile);
4019
+ }
3891
4020
  }
3892
- else if (typeof value === 'function') {
3893
- value(element);
3894
- return;
4021
+ }
4022
+ else if (newValue != null) {
4023
+ if (isEvent) {
4024
+ const result = recordJsxEvent(diffContext, vnode, key, newValue, currentFile);
4025
+ patchEventDispatch ||= result;
3895
4026
  }
3896
4027
  else {
3897
- throw qError(15 /* QError.invalidRefValue */, [currentFile]);
4028
+ patchProperty(diffContext, vnode, key, newValue, currentFile);
3898
4029
  }
3899
4030
  }
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);
4031
+ }
4032
+ if (oldAttrs) {
4033
+ // Remove attributes that no longer exist in new props
4034
+ for (const key in oldAttrs) {
4035
+ if (!_hasOwnProperty.call(newAttrs, key) &&
4036
+ !key.startsWith(HANDLER_PREFIX) &&
4037
+ !isHtmlAttributeAnEventName(key)) {
4038
+ patchProperty(diffContext, vnode, key, null, currentFile);
3908
4039
  }
3909
- const vHost = vnode;
3910
- value = retryOnPromise(() => trackSignalAndAssignHost(unwrappedSignal, vHost, key, diffContext.container, diffContext.subscriptionData.var));
3911
4040
  }
3912
- else {
3913
- if (currentEffect) {
3914
- clearEffectSubscription(diffContext.container, currentEffect);
3915
- }
4041
+ }
4042
+ return patchEventDispatch;
4043
+ }
4044
+ const patchProperty = (diffContext, vnode, key, value, currentFile) => {
4045
+ if (
4046
+ // set only property for iteration item, not an attribute
4047
+ key === ITERATION_ITEM_SINGLE ||
4048
+ key === ITERATION_ITEM_MULTI ||
4049
+ key.charAt(0) === HANDLER_PREFIX) {
4050
+ // TODO: there is a potential deoptimization here, because we are setting different keys on props.
4051
+ // Eager bailout - Insufficient type feedback for generic keyed access
4052
+ vnode_setProp(vnode, key, value);
4053
+ return;
4054
+ }
4055
+ const originalValue = value;
4056
+ if (key === 'ref') {
4057
+ const element = vnode.node;
4058
+ if (isSignal(value)) {
4059
+ value.value = element;
4060
+ return;
3916
4061
  }
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);
4062
+ else if (typeof value === 'function') {
4063
+ value(element);
3923
4064
  return;
3924
4065
  }
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;
4066
+ else {
4067
+ throw qError(15 /* QError.invalidRefValue */, [currentFile]);
3936
4068
  }
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
- }
4069
+ }
4070
+ const currentEffect = vnode[_EFFECT_BACK_REF]?.get(key);
4071
+ if (isSignal(value)) {
4072
+ const unwrappedSignal = value instanceof WrappedSignalImpl ? value.$unwrapIfSignal$() : value;
4073
+ if (currentEffect?.backRef?.has(unwrappedSignal)) {
4074
+ return;
3947
4075
  }
3948
- else if (newValue != null) {
3949
- isEvent ? recordJsxEvent(key, newValue) : record(key, newValue);
4076
+ if (currentEffect) {
4077
+ clearEffectSubscription(diffContext.container, currentEffect);
3950
4078
  }
4079
+ const vHost = vnode;
4080
+ value = retryOnPromise(() => trackSignalAndAssignHost(unwrappedSignal, vHost, key, diffContext.container, diffContext.subscriptionData.var));
3951
4081
  }
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);
4082
+ else {
4083
+ if (currentEffect) {
4084
+ clearEffectSubscription(diffContext.container, currentEffect);
3958
4085
  }
3959
4086
  }
3960
- return patchEventDispatch;
3961
- }
4087
+ if (isPromise(value)) {
4088
+ const vHost = vnode;
4089
+ const attributePromise = value.then((resolvedValue) => {
4090
+ setAttribute(diffContext.journal, vHost, key, resolvedValue, diffContext.scopedStyleIdPrefix, originalValue);
4091
+ });
4092
+ diffContext.asyncAttributePromises.push(attributePromise);
4093
+ return;
4094
+ }
4095
+ setAttribute(diffContext.journal, vnode, key, value, diffContext.scopedStyleIdPrefix, originalValue);
4096
+ };
4097
+ const recordJsxEvent = (diffContext, vnode, key, value, currentFile) => {
4098
+ const data = getEventDataFromHtmlAttribute(key);
4099
+ if (data) {
4100
+ const props = vnode.props;
4101
+ const [scope, eventName] = data;
4102
+ const scopedEvent = getScopedEventName(scope, eventName);
4103
+ const loaderScopedEvent = getLoaderScopedEventName(scope, scopedEvent);
4104
+ const scopedEventKey = ':' + scopedEvent;
4105
+ if (props && _hasOwnProperty.call(props, scopedEventKey)) {
4106
+ return false;
4107
+ }
4108
+ patchProperty(diffContext, vnode, scopedEventKey, value, currentFile);
4109
+ registerQwikLoaderEvent(diffContext, loaderScopedEvent);
4110
+ return true;
4111
+ }
4112
+ return false;
4113
+ };
3962
4114
  function registerQwikLoaderEvent(diffContext, eventName) {
3963
4115
  const qWindow = import.meta.env.TEST
3964
4116
  ? diffContext.container.document.defaultView
@@ -3970,6 +4122,16 @@ function registerQwikLoaderEvent(diffContext, eventName) {
3970
4122
  function retrieveChildWithKey(diffContext, nodeName, key) {
3971
4123
  let vNodeWithKey = null;
3972
4124
  if (diffContext.vSiblings === null) {
4125
+ // check if the current node is the one we are looking for
4126
+ const vCurrent = diffContext.vCurrent;
4127
+ if (vCurrent) {
4128
+ const name = vnode_isElementVNode(vCurrent) ? vnode_getElementName(vCurrent) : null;
4129
+ const vKey = getKey(vCurrent) ||
4130
+ getComponentHash(vCurrent, diffContext.container.$getObjectById$);
4131
+ if (vKey === key && name === nodeName) {
4132
+ return vCurrent;
4133
+ }
4134
+ }
3973
4135
  // it is not materialized; so materialize it.
3974
4136
  diffContext.vSiblings = new Map();
3975
4137
  diffContext.vSiblingsArray = [];
@@ -4005,8 +4167,9 @@ function retrieveChildWithKey(diffContext, nodeName, key) {
4005
4167
  }
4006
4168
  else {
4007
4169
  const siblingsKey = getSideBufferKey(nodeName, key);
4008
- if (diffContext.vSiblings.has(siblingsKey)) {
4009
- vNodeWithKey = diffContext.vSiblings.get(siblingsKey);
4170
+ const sibling = diffContext.vSiblings.get(siblingsKey);
4171
+ if (sibling) {
4172
+ vNodeWithKey = sibling;
4010
4173
  diffContext.vSiblings.delete(siblingsKey);
4011
4174
  }
4012
4175
  }
@@ -4075,6 +4238,7 @@ function moveOrCreateKeyedNode(diffContext, nodeName, lookupKey, sideBufferKey,
4075
4238
  // 1) Try to find the node among upcoming siblings
4076
4239
  diffContext.vNewNode = retrieveChildWithKey(diffContext, nodeName, lookupKey);
4077
4240
  if (diffContext.vNewNode) {
4241
+ vnode_insertBefore(diffContext.journal, parentForInsert, diffContext.vNewNode, diffContext.vCurrent);
4078
4242
  diffContext.vCurrent = diffContext.vNewNode;
4079
4243
  diffContext.vNewNode = null;
4080
4244
  return false;
@@ -4118,7 +4282,7 @@ function expectVirtual(diffContext, type, jsxKey) {
4118
4282
  return;
4119
4283
  }
4120
4284
  // For fragments without a key, always create a new virtual node (ensures rerender semantics)
4121
- if (jsxKey === null) {
4285
+ if (jsxKey === null || diffContext.isCreationMode) {
4122
4286
  vnode_insertBefore(diffContext.journal, diffContext.vParent, (diffContext.vNewNode = vnode_newVirtual()), diffContext.vCurrent && getInsertBefore(diffContext));
4123
4287
  diffContext.vNewNode.key = jsxKey;
4124
4288
  isDev && vnode_setProp(diffContext.vNewNode, DEBUG_TYPE, type);
@@ -4247,7 +4411,7 @@ function expectText(diffContext, text) {
4247
4411
  return;
4248
4412
  }
4249
4413
  }
4250
- vnode_insertBefore(diffContext.journal, diffContext.vParent, (diffContext.vNewNode = vnode_newText(diffContext.container.document.createTextNode(text), text)), diffContext.vCurrent);
4414
+ vnode_insertBefore(diffContext.journal, diffContext.vParent, (diffContext.vNewNode = vnode_newText((import.meta.env.TEST ? diffContext.container.document : document).createTextNode(text), text)), diffContext.vCurrent);
4251
4415
  }
4252
4416
  /**
4253
4417
  * Retrieve the key from the VNode.
@@ -4304,13 +4468,10 @@ function Projection() { }
4304
4468
  function handleProps(host, jsxProps, vNodeProps, container) {
4305
4469
  let shouldRender = false;
4306
4470
  if (vNodeProps) {
4307
- const effects = vNodeProps[_PROPS_HANDLER].$effects$;
4308
4471
  const constPropsDifferent = handleChangedProps(jsxProps[_CONST_PROPS], vNodeProps[_CONST_PROPS], vNodeProps[_PROPS_HANDLER], container, false);
4309
4472
  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
- }
4473
+ const varPropsDifferent = handleChangedProps(jsxProps[_VAR_PROPS], vNodeProps[_VAR_PROPS], vNodeProps[_PROPS_HANDLER], container, true);
4474
+ shouldRender ||= varPropsDifferent;
4314
4475
  // Update the owner after all props have been synced
4315
4476
  vNodeProps[_OWNER] = jsxProps[_OWNER];
4316
4477
  }
@@ -4335,7 +4496,6 @@ function handleChangedProps(src, dst, propsHandler, container, triggerEffects =
4335
4496
  continue;
4336
4497
  }
4337
4498
  if (!dst || src[key] !== dst[key]) {
4338
- changed = true;
4339
4499
  if (triggerEffects) {
4340
4500
  if (dst) {
4341
4501
  // Update the value in dst BEFORE triggering effects
@@ -4343,7 +4503,11 @@ function handleChangedProps(src, dst, propsHandler, container, triggerEffects =
4343
4503
  // Note: Value is not triggering effects, because we are modyfing direct VAR_PROPS object
4344
4504
  dst[key] = src[key];
4345
4505
  }
4346
- triggerPropsProxyEffect(propsHandler, key);
4506
+ const didTigger = triggerPropsProxyEffect(propsHandler, key);
4507
+ if (!didTigger) {
4508
+ // If the effect was not triggered, then the prop has changed and we should rerender
4509
+ changed = true;
4510
+ }
4347
4511
  }
4348
4512
  else {
4349
4513
  // Early return for const props (no effects)
@@ -4359,10 +4523,13 @@ function handleChangedProps(src, dst, propsHandler, container, triggerEffects =
4359
4523
  continue;
4360
4524
  }
4361
4525
  if (!src || !_hasOwnProperty.call(src, key)) {
4362
- changed = true;
4363
4526
  if (triggerEffects) {
4364
4527
  delete dst[key];
4365
- triggerPropsProxyEffect(propsHandler, key);
4528
+ const didTigger = triggerPropsProxyEffect(propsHandler, key);
4529
+ if (!didTigger) {
4530
+ // If the effect was not triggered, then the prop has changed and we should rerender
4531
+ changed = true;
4532
+ }
4366
4533
  }
4367
4534
  }
4368
4535
  }
@@ -4535,6 +4702,37 @@ function markVNodeAsDeleted(vCursor) {
4535
4702
  */
4536
4703
  vCursor.flags |= 32 /* VNodeFlags.Deleted */;
4537
4704
  }
4705
+ function areWrappedSignalsEqual(oldSignal, newSignal) {
4706
+ if (oldSignal === newSignal) {
4707
+ return true;
4708
+ }
4709
+ return (newSignal.$func$ === oldSignal.$func$ && areArgumentsEqual(newSignal.$args$, oldSignal.$args$));
4710
+ }
4711
+ function areArgumentsEqual(oldArgs, newArgs) {
4712
+ if (oldArgs === newArgs) {
4713
+ return true;
4714
+ }
4715
+ if (!oldArgs || !newArgs || oldArgs.length !== newArgs.length) {
4716
+ return false;
4717
+ }
4718
+ for (let i = 0; i < oldArgs.length; i++) {
4719
+ if (oldArgs[i] !== newArgs[i]) {
4720
+ return false;
4721
+ }
4722
+ }
4723
+ return true;
4724
+ }
4725
+ function containsWrappedSignal(data, signal) {
4726
+ if (!(signal instanceof WrappedSignalImpl)) {
4727
+ return false;
4728
+ }
4729
+ for (const item of data) {
4730
+ if (item instanceof WrappedSignalImpl && areWrappedSignalsEqual(item, signal)) {
4731
+ return true;
4732
+ }
4733
+ }
4734
+ return false;
4735
+ }
4538
4736
  /**
4539
4737
  * This marks the property as immutable. It is needed for the QRLs so that QwikLoader can get a hold
4540
4738
  * of them. This character must be `:` so that the `vnode_getAttr` can ignore them.
@@ -4542,6 +4740,41 @@ function markVNodeAsDeleted(vCursor) {
4542
4740
  const HANDLER_PREFIX = ':';
4543
4741
  let count = 0;
4544
4742
 
4743
+ /**
4744
+ * @internal
4745
+ * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
4746
+ */
4747
+ const useSequentialScope = () => {
4748
+ const iCtx = useInvokeContext();
4749
+ const hostElement = iCtx.$hostElement$;
4750
+ const host = hostElement;
4751
+ let seq = iCtx.$container$.getHostProp(host, ELEMENT_SEQ);
4752
+ if (seq === null) {
4753
+ seq = [];
4754
+ iCtx.$container$.setHostProp(host, ELEMENT_SEQ, seq);
4755
+ }
4756
+ let seqIdx = iCtx.$container$.getHostProp(host, ELEMENT_SEQ_IDX);
4757
+ if (seqIdx === null) {
4758
+ seqIdx = 0;
4759
+ }
4760
+ iCtx.$container$.setHostProp(host, ELEMENT_SEQ_IDX, seqIdx + 1);
4761
+ while (seq.length <= seqIdx) {
4762
+ seq.push(undefined);
4763
+ }
4764
+ const set = (value) => {
4765
+ if (qDev && qSerialize) {
4766
+ verifySerializable(value);
4767
+ }
4768
+ return (seq[seqIdx] = value);
4769
+ };
4770
+ return {
4771
+ val: seq[seqIdx],
4772
+ set,
4773
+ i: seqIdx,
4774
+ iCtx,
4775
+ };
4776
+ };
4777
+
4545
4778
  /** @internal */
4546
4779
  const useResourceQrl = (qrl, opts) => {
4547
4780
  const { val, set, i, iCtx } = useSequentialScope();
@@ -4627,6 +4860,7 @@ function getResourceValueAsPromise(props) {
4627
4860
  const isBrowser = !isServerPlatform();
4628
4861
  if (isBrowser) {
4629
4862
  if (state === 'pending' && props.onPending) {
4863
+ resource.value.catch(() => { });
4630
4864
  return Promise.resolve().then(useBindInvokeContext(props.onPending));
4631
4865
  }
4632
4866
  else if (state === 'rejected' && props.onRejected) {
@@ -4683,7 +4917,8 @@ const runResource = (task, container, host) => {
4683
4917
  iCtx.$container$ = container;
4684
4918
  const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
4685
4919
  const resource = task.$state$;
4686
- assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
4920
+ isDev &&
4921
+ assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
4687
4922
  const track = trackFn(task, container);
4688
4923
  const [cleanup, cleanups] = cleanupFn(task, (reason) => container.handleError(reason, host));
4689
4924
  const resourceTarget = unwrapStore(resource);
@@ -4776,33 +5011,6 @@ const runResource = (task, container, host) => {
4776
5011
  return promise;
4777
5012
  };
4778
5013
 
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
5014
  /**
4807
5015
  * Executes tasks for a vNode if the TASKS dirty bit is set. Tasks are stored in the ELEMENT_SEQ
4808
5016
  * property and executed in order.
@@ -4947,8 +5155,8 @@ function clearNodePropData(vNode) {
4947
5155
  const props = (vNode.props ||= {});
4948
5156
  delete props[NODE_PROPS_DATA_KEY];
4949
5157
  }
4950
- function setNodeProp(domVNode, journal, property, value, isConst) {
4951
- journal.push(createSetAttributeOperation(domVNode.node, property, value));
5158
+ function setNodeProp(domVNode, journal, property, value, isConst, scopedStyleIdPrefix = null) {
5159
+ journal.push(createSetAttributeOperation(domVNode.node, property, value, scopedStyleIdPrefix, (domVNode.flags & 128 /* VNodeFlags.NS_svg */) !== 0));
4952
5160
  if (!isConst) {
4953
5161
  if (domVNode.props && value == null) {
4954
5162
  delete domVNode.props[property];
@@ -4983,10 +5191,9 @@ function executeNodeProps(vNode, journal) {
4983
5191
  // TODO: Handle async signals (promises) - need to track pending async prop data
4984
5192
  value = value.value;
4985
5193
  }
4986
- // Process synchronously (same logic as scheduler)
4987
- const serializedValue = serializeAttribute(property, value, nodeProp.scopedStyleIdPrefix);
5194
+ // Pass raw value and scopedStyleIdPrefix - serialization happens in flush
4988
5195
  const isConst = nodeProp.isConst;
4989
- setNodeProp(domVNode, journal, property, serializedValue, isConst);
5196
+ setNodeProp(domVNode, journal, property, value, isConst, nodeProp.scopedStyleIdPrefix);
4990
5197
  }
4991
5198
  // Clear pending prop data after processing
4992
5199
  clearNodePropData(vNode);
@@ -5063,61 +5270,131 @@ const fastInsertBefore = (insertBeforeParent, target, insertBefore) => {
5063
5270
  _insertBefore = insertBeforeParent.insertBefore;
5064
5271
  }
5065
5272
  _insertBefore.call(insertBeforeParent, target, insertBefore);
5066
- };
5067
- 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;
5080
- }
5081
- case 16 /* VNodeOperationType.SetText */: {
5082
- operation.target.nodeValue = operation.text;
5083
- break;
5273
+ };
5274
+ function _flushJournal(journal) {
5275
+ let batchParent = null;
5276
+ let batchBefore = null;
5277
+ let batchNodes = null;
5278
+ const batchSet = new Set();
5279
+ const flush = () => {
5280
+ if (batchNodes) {
5281
+ if (batchNodes.length === 1) {
5282
+ fastInsertBefore(batchParent, batchNodes[0], batchBefore);
5084
5283
  }
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 */);
5284
+ else {
5285
+ const doc = batchParent.ownerDocument || batchParent;
5286
+ const fragment = doc.createDocumentFragment();
5287
+ for (const node of batchNodes) {
5288
+ fragment.appendChild(node);
5096
5289
  }
5097
- else if (shouldRemove) {
5098
- element.removeAttribute(attrName);
5290
+ fastInsertBefore(batchParent, fragment, batchBefore);
5291
+ }
5292
+ batchNodes = null;
5293
+ batchParent = null;
5294
+ batchBefore = null;
5295
+ batchSet.clear();
5296
+ }
5297
+ };
5298
+ for (const operation of journal) {
5299
+ if (operation instanceof InsertOrMoveOperation) {
5300
+ if (batchParent === operation.parent && batchBefore === operation.beforeTarget) {
5301
+ if (!batchNodes) {
5302
+ batchNodes = [];
5099
5303
  }
5100
- else if (attrName === 'value' && attrName in element) {
5101
- element.value = attrValue;
5304
+ batchNodes.push(operation.target);
5305
+ batchSet.add(operation.target);
5306
+ continue;
5307
+ }
5308
+ if (batchNodes) {
5309
+ // If we have an existing batch, we need to check if the new operation conflicts with it.
5310
+ // 1. If we are inserting into the same parent but with a different "before" reference, we must flush.
5311
+ if (batchParent === operation.parent) {
5312
+ flush();
5313
+ batchParent = operation.parent;
5314
+ batchBefore = operation.beforeTarget;
5315
+ batchNodes = [operation.target];
5316
+ batchSet.add(operation.target);
5317
+ continue;
5102
5318
  }
5103
- else {
5104
- element.setAttribute(attrName, attrValue);
5319
+ // 2. If we are moving a node that is currently in the batch, or moving the node that is the reference for the batch.
5320
+ if (batchSet.has(operation.target) ||
5321
+ (batchBefore && operation.target === batchBefore) ||
5322
+ (batchParent && operation.target === batchParent)) {
5323
+ flush();
5324
+ batchParent = operation.parent;
5325
+ batchBefore = operation.beforeTarget;
5326
+ batchNodes = [operation.target];
5327
+ batchSet.add(operation.target);
5328
+ continue;
5105
5329
  }
5106
- break;
5330
+ // 3. Otherwise, we can execute this operation immediately without flushing the current batch.
5331
+ // This is important for "interleaved" inserts, e.g. inserting <tr> into <tbody> (batched)
5332
+ // and then inserting <td> into that <tr> (immediate).
5333
+ // The <tr> is in memory, so inserting <td> into it is fine and doesn't require the <tr> to be in the DOM.
5107
5334
  }
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 = '';
5335
+ else {
5336
+ batchParent = operation.parent;
5337
+ batchBefore = operation.beforeTarget;
5338
+ batchNodes = [operation.target];
5339
+ batchSet.add(operation.target);
5340
+ continue;
5341
+ }
5342
+ fastInsertBefore(operation.parent, operation.target, operation.beforeTarget);
5343
+ continue;
5344
+ }
5345
+ if (operation instanceof DeleteOperation) {
5346
+ if (batchSet.has(operation.target) ||
5347
+ (batchBefore && operation.target === batchBefore) ||
5348
+ (batchParent && operation.target === batchParent)) {
5349
+ flush();
5350
+ }
5351
+ operation.target.remove();
5352
+ continue;
5353
+ }
5354
+ if (operation instanceof RemoveAllChildrenOperation) {
5355
+ if (batchSet.has(operation.target) ||
5356
+ (batchBefore && operation.target === batchBefore) ||
5357
+ (batchParent && operation.target === batchParent)) {
5358
+ flush();
5359
+ }
5360
+ // Removing children of a node in the batch is safe (clears detached node)
5361
+ const removeParent = operation.target;
5362
+ removeParent.textContent = '';
5363
+ continue;
5364
+ }
5365
+ if (operation instanceof SetTextOperation) {
5366
+ operation.target.nodeValue = operation.text;
5367
+ }
5368
+ else if (operation instanceof SetAttributeOperation) {
5369
+ const element = operation.target;
5370
+ const attrName = operation.attrName;
5371
+ const rawValue = operation.attrValue;
5372
+ const attrValue = rawValue != null
5373
+ ? serializeAttribute(attrName, rawValue, operation.scopedStyleIdPrefix)
5374
+ : null;
5375
+ const shouldRemove = attrValue == null || attrValue === false;
5376
+ if (isBooleanAttr(element, attrName)) {
5377
+ element[attrName] = parseBoolean(attrValue);
5378
+ }
5379
+ else if (attrName === dangerouslySetInnerHTML) {
5380
+ if (batchParent === element) {
5381
+ flush();
5116
5382
  }
5117
- break;
5383
+ element.innerHTML = attrValue;
5384
+ element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
5385
+ }
5386
+ else if (shouldRemove) {
5387
+ element.removeAttribute(attrName);
5388
+ }
5389
+ else if (attrName === 'value' && attrName in element) {
5390
+ element.value = attrValue;
5391
+ }
5392
+ else {
5393
+ directSetAttribute(element, attrName, attrValue, operation.isSvg);
5118
5394
  }
5119
5395
  }
5120
5396
  }
5397
+ flush();
5121
5398
  }
5122
5399
  function executeAfterFlush(container, cursorData) {
5123
5400
  const visibleTasks = cursorData.afterFlushTasks;
@@ -5253,7 +5530,7 @@ function processCursorQueue(options = {
5253
5530
  */
5254
5531
  function walkCursor(cursor, options) {
5255
5532
  const { timeBudget } = options;
5256
- const isServer = isServerPlatform();
5533
+ const isRunningOnServer = import.meta.env.TEST ? isServerPlatform() : isServer;
5257
5534
  const startTime = performance.now();
5258
5535
  const cursorData = getCursorData(cursor);
5259
5536
  // Check if cursor is blocked by a promise
@@ -5262,10 +5539,10 @@ function walkCursor(cursor, options) {
5262
5539
  return;
5263
5540
  }
5264
5541
  const container = cursorData.container;
5265
- assertDefined(container, 'Cursor container not found');
5542
+ isDev && assertDefined(container, 'Cursor container not found');
5266
5543
  // Check if cursor is already complete
5267
5544
  if (!cursor.dirty) {
5268
- finishWalk(container, cursor, cursorData, isServer);
5545
+ finishWalk(container, cursor, cursorData, isRunningOnServer);
5269
5546
  return;
5270
5547
  }
5271
5548
  const journal = (cursorData.journal ||= []);
@@ -5273,7 +5550,7 @@ function walkCursor(cursor, options) {
5273
5550
  let currentVNode = null;
5274
5551
  while ((currentVNode = cursorData.position)) {
5275
5552
  // Check time budget (only for DOM, not SSR)
5276
- if (!isServer && !import.meta.env.TEST) {
5553
+ if (!isRunningOnServer && !import.meta.env.TEST) {
5277
5554
  const elapsed = performance.now() - startTime;
5278
5555
  if (elapsed >= timeBudget) {
5279
5556
  // Schedule continuation as macrotask to actually yield to browser
@@ -5356,8 +5633,9 @@ function walkCursor(cursor, options) {
5356
5633
  return;
5357
5634
  }
5358
5635
  }
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);
5636
+ isDev &&
5637
+ assertFalse(!!(cursor.dirty & 127 /* ChoreBits.DIRTY_MASK */ && !cursorData.position), 'Cursor is still dirty and position is not set after walking');
5638
+ finishWalk(container, cursor, cursorData, isRunningOnServer);
5361
5639
  }
5362
5640
  function finishWalk(container, cursor, cursorData, isServer) {
5363
5641
  if (!(cursor.dirty & 127 /* ChoreBits.DIRTY_MASK */)) {
@@ -5377,7 +5655,7 @@ function finishWalk(container, cursor, cursorData, isServer) {
5377
5655
  function resolveCursor(container) {
5378
5656
  // TODO streaming as a cursor? otherwise we need to wait separately for it
5379
5657
  // or just ignore and resolve manually
5380
- if (container.$cursorCount$ === 0) {
5658
+ if (container.$cursorCount$ === 0 && container.$pausedCursorCount$ === 0) {
5381
5659
  container.$resolveRenderPromise$();
5382
5660
  container.$renderPromise$ = null;
5383
5661
  }
@@ -5441,6 +5719,7 @@ function getNextVNode(vNode, cursor) {
5441
5719
  // all array items checked, children are no longer dirty
5442
5720
  parent.dirty &= -33 /* ChoreBits.CHILDREN */;
5443
5721
  parent.dirtyChildren = null;
5722
+ parent.nextDirtyChildIndex = 0;
5444
5723
  return getNextVNode(parent, cursor);
5445
5724
  }
5446
5725
 
@@ -5529,8 +5808,13 @@ function _executeSsrChores(container, ssrNode) {
5529
5808
  promise = result;
5530
5809
  }
5531
5810
  }
5532
- if (ssrNode.dirty & 4 /* ChoreBits.COMPONENT */) ;
5533
- ssrNode.dirty &= -128 /* ChoreBits.DIRTY_MASK */;
5811
+ // In SSR, we don't handle the COMPONENT bit here.
5812
+ // During initial render, if a task completes and marks the component dirty,
5813
+ // we want to leave the COMPONENT bit set so that executeComponent can detect
5814
+ // it after $waitOn$ completes and re-execute the component function.
5815
+ // executeComponent will clear the bit after re-executing.
5816
+ // Clear all dirty bits EXCEPT COMPONENT
5817
+ ssrNode.dirty &= -124;
5534
5818
  if (promise) {
5535
5819
  return promise;
5536
5820
  }
@@ -5649,7 +5933,7 @@ function findAndPropagateToBlockingCursor(vNode) {
5649
5933
  return false;
5650
5934
  }
5651
5935
  function isSsrNodeGuard(_vNode) {
5652
- return isServerPlatform();
5936
+ return import.meta.env.TEST ? isServerPlatform() : isServer;
5653
5937
  }
5654
5938
  /**
5655
5939
  * Marks a vNode as dirty and propagates dirty bits up the tree.
@@ -5855,21 +6139,22 @@ const fastGetter = (prototype, name) => {
5855
6139
  */
5856
6140
  //////////////////////////////////////////////////////////////////////////////////////////////////////
5857
6141
  const vnode_newElement = (element, elementName, key = null) => {
5858
- assertEqual(fastNodeType(element), 1 /* ELEMENT_NODE */, 'Expecting element node.');
6142
+ isDev && assertEqual(fastNodeType(element), 1 /* ELEMENT_NODE */, 'Expecting element node.');
5859
6143
  const vnode = new ElementVNode(key, 1 /* VNodeFlags.Element */ | 8 /* VNodeFlags.Inflated */ | (-1 << 9 /* VNodeFlagsIndex.shift */), // Flag
5860
6144
  null, null, null, null, null, null, element, elementName);
5861
6145
  element.vNode = vnode;
5862
6146
  return vnode;
5863
6147
  };
5864
6148
  const vnode_newUnMaterializedElement = (element) => {
5865
- assertEqual(fastNodeType(element), 1 /* ELEMENT_NODE */, 'Expecting element node.');
6149
+ isDev && assertEqual(fastNodeType(element), 1 /* ELEMENT_NODE */, 'Expecting element node.');
5866
6150
  const vnode = new ElementVNode(null, 1 /* VNodeFlags.Element */ | (-1 << 9 /* VNodeFlagsIndex.shift */), // Flag
5867
6151
  null, null, null, null, undefined, undefined, element, undefined);
5868
6152
  element.vNode = vnode;
5869
6153
  return vnode;
5870
6154
  };
5871
6155
  const vnode_newSharedText = (previousTextNode, sharedTextNode, textContent) => {
5872
- sharedTextNode &&
6156
+ isDev &&
6157
+ sharedTextNode &&
5873
6158
  assertEqual(fastNodeType(sharedTextNode), 3 /* TEXT_NODE */, 'Expecting text node.');
5874
6159
  const vnode = new TextVNode(4 /* VNodeFlags.Text */ | (-1 << 9 /* VNodeFlagsIndex.shift */), // Flag
5875
6160
  null, // Parent
@@ -5886,18 +6171,18 @@ const vnode_newText = (textNode, textContent) => {
5886
6171
  null, textNode, // TextNode
5887
6172
  textContent // Text Content
5888
6173
  );
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.');
6174
+ isDev && assertEqual(fastNodeType(textNode), 3 /* TEXT_NODE */, 'Expecting text node.');
6175
+ isDev && assertFalse(vnode_isElementVNode(vnode), 'Incorrect format of TextVNode.');
6176
+ isDev && assertTrue(vnode_isTextVNode(vnode), 'Incorrect format of TextVNode.');
6177
+ isDev && assertFalse(vnode_isVirtualVNode(vnode), 'Incorrect format of TextVNode.');
5893
6178
  return vnode;
5894
6179
  };
5895
6180
  const vnode_newVirtual = () => {
5896
6181
  const vnode = new VirtualVNode(null, 2 /* VNodeFlags.Virtual */ | (-1 << 9 /* VNodeFlagsIndex.shift */), // Flags
5897
6182
  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.');
6183
+ isDev && assertFalse(vnode_isElementVNode(vnode), 'Incorrect format of TextVNode.');
6184
+ isDev && assertFalse(vnode_isTextVNode(vnode), 'Incorrect format of TextVNode.');
6185
+ isDev && assertTrue(vnode_isVirtualVNode(vnode), 'Incorrect format of TextVNode.');
5901
6186
  return vnode;
5902
6187
  };
5903
6188
  //////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -5905,16 +6190,16 @@ const vnode_isVNode = (vNode) => {
5905
6190
  return vNode instanceof VNode;
5906
6191
  };
5907
6192
  const vnode_isElementVNode = (vNode) => {
5908
- return vNode instanceof ElementVNode;
6193
+ return (vNode.flags & 1 /* VNodeFlags.Element */) === 1 /* VNodeFlags.Element */;
5909
6194
  };
5910
6195
  const vnode_isElementOrTextVNode = (vNode) => {
5911
- assertDefined(vNode, 'Missing vNode');
6196
+ isDev && assertDefined(vNode, 'Missing vNode');
5912
6197
  const flag = vNode.flags;
5913
6198
  return (flag & 5 /* VNodeFlags.ELEMENT_OR_TEXT_MASK */) !== 0;
5914
6199
  };
5915
6200
  /** @internal */
5916
6201
  const vnode_isMaterialized = (vNode) => {
5917
- assertDefined(vNode, 'Missing vNode');
6202
+ isDev && assertDefined(vNode, 'Missing vNode');
5918
6203
  const flag = vNode.flags;
5919
6204
  return ((flag & 1 /* VNodeFlags.Element */) === 1 /* VNodeFlags.Element */ &&
5920
6205
  vNode.firstChild !== undefined &&
@@ -5922,27 +6207,30 @@ const vnode_isMaterialized = (vNode) => {
5922
6207
  };
5923
6208
  /** @internal */
5924
6209
  const vnode_isTextVNode = (vNode) => {
5925
- return vNode instanceof TextVNode;
6210
+ return (vNode.flags & 4 /* VNodeFlags.Text */) === 4 /* VNodeFlags.Text */;
5926
6211
  };
5927
6212
  /** @internal */
5928
6213
  const vnode_isVirtualVNode = (vNode) => {
5929
- return vNode instanceof VirtualVNode;
6214
+ return (vNode.flags & 2 /* VNodeFlags.Virtual */) === 2 /* VNodeFlags.Virtual */;
5930
6215
  };
5931
6216
  const vnode_isProjection = (vNode) => {
5932
- assertDefined(vNode, 'Missing vNode');
6217
+ isDev && assertDefined(vNode, 'Missing vNode');
5933
6218
  const flag = vNode.flags;
5934
6219
  return ((flag & 2 /* VNodeFlags.Virtual */) === 2 /* VNodeFlags.Virtual */ && vnode_getProp(vNode, QSlot, null) !== null);
5935
6220
  };
5936
6221
  const ensureTextVNode = (vNode) => {
5937
- assertTrue(vnode_isTextVNode(vNode), 'Expecting TextVNode was: ' + vnode_getNodeTypeName(vNode));
6222
+ isDev &&
6223
+ assertTrue(vnode_isTextVNode(vNode), 'Expecting TextVNode was: ' + vnode_getNodeTypeName(vNode));
5938
6224
  return vNode;
5939
6225
  };
5940
6226
  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));
6227
+ isDev && assertDefined(vNode, 'Missing vNode');
6228
+ isDev &&
6229
+ assertTrue((vNode.flags & 3 /* VNodeFlags.ELEMENT_OR_VIRTUAL_MASK */) !== 0, 'Expecting ElementVNode or VirtualVNode was: ' + vnode_getNodeTypeName(vNode));
5943
6230
  };
5944
6231
  const ensureElementVNode = (vNode) => {
5945
- assertTrue(vnode_isElementVNode(vNode), 'Expecting ElementVNode was: ' + vnode_getNodeTypeName(vNode));
6232
+ isDev &&
6233
+ assertTrue(vnode_isElementVNode(vNode), 'Expecting ElementVNode was: ' + vnode_getNodeTypeName(vNode));
5946
6234
  return vNode;
5947
6235
  };
5948
6236
  const vnode_getNodeTypeName = (vNode) => {
@@ -5972,20 +6260,21 @@ const vnode_getProp = (vNode, key, getObject) => {
5972
6260
  return null;
5973
6261
  };
5974
6262
  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
- }
6263
+ if (value == null && vNode.props) {
6264
+ delete vNode.props[key];
6265
+ }
6266
+ else {
6267
+ vNode.props ||= {};
6268
+ vNode.props[key] = value;
5983
6269
  }
5984
6270
  };
5985
- const vnode_setAttr = (journal, vNode, key, value) => {
6271
+ const vnode_setAttr = (journal, vNode, key, value, scopedStyleIdPrefix = null) => {
5986
6272
  if (vnode_isElementVNode(vNode)) {
6273
+ import.meta.env.TEST &&
6274
+ scopedStyleIdPrefix &&
6275
+ vnode_setProp(vNode, debugStyleScopeIdPrefixAttr, scopedStyleIdPrefix);
5987
6276
  vnode_setProp(vNode, key, value);
5988
- addVNodeOperation(journal, createSetAttributeOperation(vNode.node, key, value));
6277
+ addVNodeOperation(journal, createSetAttributeOperation(vNode.node, key, value, scopedStyleIdPrefix, (vNode.flags & 128 /* VNodeFlags.NS_svg */) !== 0));
5989
6278
  }
5990
6279
  };
5991
6280
  const vnode_ensureElementKeyInflated = (vnode) => {
@@ -6218,7 +6507,7 @@ const vnode_ensureTextInflated = (journal, vnode) => {
6218
6507
  const flags = textVNode.flags;
6219
6508
  if ((flags & 8 /* VNodeFlags.Inflated */) === 0) {
6220
6509
  const parentNode = vnode_getDomParent(vnode, true);
6221
- assertDefined(parentNode, 'Missing parent node.');
6510
+ isDev && assertDefined(parentNode, 'Missing parent node.');
6222
6511
  const sharedTextNode = textVNode.node;
6223
6512
  const doc = fastOwnerDocument(parentNode);
6224
6513
  // Walk the previous siblings and inflate them.
@@ -6228,7 +6517,7 @@ const vnode_ensureTextInflated = (journal, vnode) => {
6228
6517
  // case we know that the next node MUST be either NULL or an Element.
6229
6518
  const node = vnode_getDomSibling(vnode, true, true);
6230
6519
  const insertBeforeNode = sharedTextNode ||
6231
- ((node instanceof ElementVNode ? node.node : node?.node) || null);
6520
+ ((node && vnode_isElementVNode(node) ? node.node : node?.node) || null);
6232
6521
  let lastPreviousTextNode = insertBeforeNode;
6233
6522
  while (vCursor && vnode_isTextVNode(vCursor)) {
6234
6523
  if ((vCursor.flags & 8 /* VNodeFlags.Inflated */) === 0) {
@@ -6268,7 +6557,7 @@ const vnode_locate = (rootVNode, id) => {
6268
6557
  let elementOffset = -1;
6269
6558
  let refElement;
6270
6559
  if (typeof id === 'string') {
6271
- assertDefined(qVNodeRefs, 'Missing qVNodeRefs.');
6560
+ isDev && assertDefined(qVNodeRefs, 'Missing qVNodeRefs.');
6272
6561
  elementOffset = parseInt(id);
6273
6562
  refElement = qVNodeRefs.get(elementOffset);
6274
6563
  }
@@ -6279,9 +6568,10 @@ const vnode_locate = (rootVNode, id) => {
6279
6568
  return vNode;
6280
6569
  }
6281
6570
  }
6282
- assertDefined(refElement, 'Missing refElement.');
6571
+ isDev && assertDefined(refElement, 'Missing refElement.');
6283
6572
  if (!vnode_isVNode(refElement)) {
6284
- assertTrue(containerElement.contains(refElement), `Couldn't find the element inside the container while locating the VNode.`);
6573
+ isDev &&
6574
+ assertTrue(containerElement.contains(refElement), `Couldn't find the element inside the container while locating the VNode.`);
6285
6575
  // We need to find the vnode.
6286
6576
  let parent = refElement;
6287
6577
  const elementPath = [refElement];
@@ -6329,10 +6619,10 @@ const vnode_locate = (rootVNode, id) => {
6329
6619
  };
6330
6620
  const vnode_getChildWithIdx = (vNode, childIdx) => {
6331
6621
  let child = vnode_getFirstChild(vNode);
6332
- assertDefined(child, 'Missing child.');
6622
+ isDev && assertDefined(child, 'Missing child.');
6333
6623
  while (child.flags >>> 9 /* VNodeFlagsIndex.shift */ !== childIdx) {
6334
6624
  child = child.nextSibling;
6335
- assertDefined(child, 'Missing child.');
6625
+ isDev && assertDefined(child, 'Missing child.');
6336
6626
  }
6337
6627
  return child;
6338
6628
  };
@@ -6340,7 +6630,7 @@ const vNodeStack = [];
6340
6630
  const vnode_getVNodeForChildNode = (vNode, childElement) => {
6341
6631
  ensureElementVNode(vNode);
6342
6632
  let child = vnode_getFirstChild(vNode);
6343
- assertDefined(child, 'Missing child.');
6633
+ isDev && assertDefined(child, 'Missing child.');
6344
6634
  while (child && (child instanceof ElementVNode ? child.node !== childElement : true)) {
6345
6635
  if (vnode_isVirtualVNode(child)) {
6346
6636
  const next = child.nextSibling;
@@ -6362,13 +6652,13 @@ const vnode_getVNodeForChildNode = (vNode, childElement) => {
6362
6652
  child = next || vNodeStack.pop();
6363
6653
  }
6364
6654
  }
6365
- assertDefined(child, 'Missing child.');
6655
+ isDev && assertDefined(child, 'Missing child.');
6366
6656
  }
6367
6657
  while (vNodeStack.length) {
6368
6658
  vNodeStack.pop();
6369
6659
  }
6370
6660
  ensureElementVNode(child);
6371
- assertEqual(child.node, childElement, 'Child not found.');
6661
+ isDev && assertEqual(child.node, childElement, 'Child not found.');
6372
6662
  // console.log('FOUND', child[VNodeProps.node]?.outerHTML);
6373
6663
  return child;
6374
6664
  };
@@ -6543,7 +6833,7 @@ const vnode_getDomParentVNode = (vnode, includeProjection) => {
6543
6833
  return vnode;
6544
6834
  };
6545
6835
  const vnode_remove = (journal, vParent, vToRemove, removeDOM) => {
6546
- assertEqual(vParent, vToRemove.parent, 'Parent mismatch.');
6836
+ isDev && assertEqual(vParent, vToRemove.parent, 'Parent mismatch.');
6547
6837
  if (vnode_isTextVNode(vToRemove)) {
6548
6838
  vnode_ensureTextInflated(journal, vToRemove);
6549
6839
  }
@@ -6580,7 +6870,7 @@ const vnode_remove = (journal, vParent, vToRemove, removeDOM) => {
6580
6870
  vToRemove.nextSibling = null;
6581
6871
  };
6582
6872
  const vnode_truncate = (journal, vParent, vDelete, removeDOM = true) => {
6583
- assertDefined(vDelete, 'Missing vDelete.');
6873
+ isDev && assertDefined(vDelete, 'Missing vDelete.');
6584
6874
  const parent = vnode_getDomParent(vParent, true);
6585
6875
  if (parent && removeDOM) {
6586
6876
  if (vnode_isElementVNode(vParent)) {
@@ -6699,8 +6989,8 @@ const ensureMaterialized = (vnode) => {
6699
6989
  vFirstChild = vnode_materialize(vParent);
6700
6990
  }
6701
6991
  }
6702
- assertTrue(vParent.firstChild !== undefined, 'Did not materialize.');
6703
- assertTrue(vParent.lastChild !== undefined, 'Did not materialize.');
6992
+ isDev && assertTrue(vParent.firstChild !== undefined, 'Did not materialize.');
6993
+ isDev && assertTrue(vParent.lastChild !== undefined, 'Did not materialize.');
6704
6994
  return vFirstChild;
6705
6995
  };
6706
6996
  let _fastHasAttribute = null;
@@ -7018,8 +7308,11 @@ function vnode_toString(depth = 20, offset = '', materialize = false, siblings =
7018
7308
  else if (vnode_isVirtualVNode(vnode)) {
7019
7309
  const idx = vnode.flags >>> 9 /* VNodeFlagsIndex.shift */;
7020
7310
  const attrs = ['[' + String(idx) + ']'];
7311
+ if (vnode.dirty) {
7312
+ attrs.push(` dirty:${vnode.dirty}`);
7313
+ }
7021
7314
  vnode_getAttrKeys(vnode).forEach((key) => {
7022
- if (key !== DEBUG_TYPE) {
7315
+ if (key !== DEBUG_TYPE && key !== debugStyleScopeIdPrefixAttr) {
7023
7316
  const value = vnode_getProp(vnode, key, null);
7024
7317
  attrs.push(' ' + key + '=' + qwikDebugToString(value));
7025
7318
  }
@@ -7309,6 +7602,79 @@ const vnode_getProjectionParentComponent = (vHost) => {
7309
7602
  return vHost;
7310
7603
  };
7311
7604
 
7605
+ let _locale = undefined;
7606
+ let localAsyncStore;
7607
+ if (isServer) {
7608
+ import('node:async_hooks')
7609
+ .then((module) => {
7610
+ localAsyncStore = new module.AsyncLocalStorage();
7611
+ })
7612
+ .catch(() => {
7613
+ // ignore if AsyncLocalStorage is not available
7614
+ });
7615
+ }
7616
+ /**
7617
+ * Retrieve the current locale.
7618
+ *
7619
+ * If no current locale and there is no `defaultLocale` the function throws an error.
7620
+ *
7621
+ * @returns The locale.
7622
+ * @public
7623
+ */
7624
+ function getLocale(defaultLocale) {
7625
+ // Prefer per-request locale from local AsyncLocalStorage if available (server-side)
7626
+ if (localAsyncStore) {
7627
+ const locale = localAsyncStore.getStore();
7628
+ if (locale) {
7629
+ return locale;
7630
+ }
7631
+ }
7632
+ if (_locale === undefined) {
7633
+ const ctx = tryGetInvokeContext();
7634
+ if (ctx && ctx.$locale$) {
7635
+ return ctx.$locale$;
7636
+ }
7637
+ if (defaultLocale !== undefined) {
7638
+ return defaultLocale;
7639
+ }
7640
+ throw new Error('Reading `locale` outside of context.');
7641
+ }
7642
+ return _locale;
7643
+ }
7644
+ /**
7645
+ * Override the `getLocale` with `lang` within the `fn` execution.
7646
+ *
7647
+ * @public
7648
+ */
7649
+ function withLocale(locale, fn) {
7650
+ if (localAsyncStore) {
7651
+ return localAsyncStore.run(locale, fn);
7652
+ }
7653
+ const previousLang = _locale;
7654
+ try {
7655
+ _locale = locale;
7656
+ return fn();
7657
+ }
7658
+ finally {
7659
+ _locale = previousLang;
7660
+ }
7661
+ }
7662
+ /**
7663
+ * Globally set a lang.
7664
+ *
7665
+ * This can be used only in browser. Server execution requires that each request could potentially
7666
+ * be a different lang, therefore setting a global lang would produce incorrect responses.
7667
+ *
7668
+ * @public
7669
+ */
7670
+ function setLocale(locale) {
7671
+ if (localAsyncStore) {
7672
+ localAsyncStore.enterWith(locale);
7673
+ return;
7674
+ }
7675
+ _locale = locale;
7676
+ }
7677
+
7312
7678
  let _context;
7313
7679
  const tryGetInvokeContext = () => {
7314
7680
  if (!_context) {
@@ -7336,8 +7702,8 @@ const useInvokeContext = () => {
7336
7702
  if (!ctx || ctx.$event$ !== RenderEvent) {
7337
7703
  throw qError(10 /* QError.useInvokeContext */);
7338
7704
  }
7339
- assertDefined(ctx.$hostElement$, `invoke: $hostElement$ must be defined`, ctx);
7340
- assertDefined(ctx.$effectSubscriber$, `invoke: $effectSubscriber$ must be defined`, ctx);
7705
+ isDev && assertDefined(ctx.$hostElement$, `invoke: $hostElement$ must be defined`, ctx);
7706
+ isDev && assertDefined(ctx.$effectSubscriber$, `invoke: $effectSubscriber$ must be defined`, ctx);
7341
7707
  return ctx;
7342
7708
  };
7343
7709
  function useBindInvokeContext(fn) {
@@ -7404,24 +7770,37 @@ function newInvokeContext(locale, hostElement, event, url) {
7404
7770
  return ctx;
7405
7771
  }
7406
7772
  /**
7407
- * Don't track listeners for this callback
7773
+ * Get the value of the expression without tracking listeners. A function will be invoked, signals
7774
+ * will return their value, and stores will be unwrapped (they return the backing object).
7408
7775
  *
7776
+ * When you pass a function, you can also pass additional arguments that the function will receive.
7777
+ *
7778
+ * Note that stores are not unwrapped recursively.
7779
+ *
7780
+ * @param expr - The function or object to evaluate without tracking.
7781
+ * @param args - Additional arguments to pass when `expr` is a function.
7409
7782
  * @public
7410
7783
  */
7411
- const untrack = (fn) => {
7412
- if (_context) {
7413
- const sub = _context.$effectSubscriber$;
7414
- try {
7415
- _context.$effectSubscriber$ = undefined;
7416
- return fn();
7784
+ const untrack = (expr, ...args) => {
7785
+ if (typeof expr === 'function') {
7786
+ if (_context) {
7787
+ const sub = _context.$effectSubscriber$;
7788
+ try {
7789
+ _context.$effectSubscriber$ = undefined;
7790
+ return expr(...args);
7791
+ }
7792
+ finally {
7793
+ _context.$effectSubscriber$ = sub;
7794
+ }
7417
7795
  }
7418
- finally {
7419
- _context.$effectSubscriber$ = sub;
7796
+ else {
7797
+ return expr(...args);
7420
7798
  }
7421
7799
  }
7422
- else {
7423
- return fn();
7800
+ if (isSignal(expr)) {
7801
+ return expr.untrackedValue;
7424
7802
  }
7803
+ return unwrapStore(expr);
7425
7804
  };
7426
7805
  const trackInvocation = /*#__PURE__*/ newRenderInvokeContext(undefined, undefined, undefined);
7427
7806
  /**
@@ -7556,7 +7935,8 @@ const _waitUntilRendered = (elm) => {
7556
7935
  */
7557
7936
  // </docs>
7558
7937
  const createContextId = (name) => {
7559
- assertTrue(/^[\w/.-]+$/.test(name), 'Context name must only contain A-Z,a-z,0-9,_,.,-', name);
7938
+ isDev &&
7939
+ assertTrue(/^[\w/.-]+$/.test(name), 'Context name must only contain A-Z,a-z,0-9,_,.,-', name);
7560
7940
  return /*#__PURE__*/ Object.freeze({
7561
7941
  id: fromCamelToKebabCase(name),
7562
7942
  });
@@ -7803,6 +8183,7 @@ const _typeIdNames = [
7803
8183
  'JSXNode',
7804
8184
  'PropsProxy',
7805
8185
  'SubscriptionData',
8186
+ 'EffectSubscription',
7806
8187
  ];
7807
8188
 
7808
8189
  function qrlToString(serializationContext, value, raw) {
@@ -7860,13 +8241,16 @@ function qrlToString(serializationContext, value, raw) {
7860
8241
  return qrlStringInline;
7861
8242
  }
7862
8243
  function createQRLWithBackChannel(chunk, symbol, captureIds) {
7863
- let qrlRef = null;
8244
+ let qrlImporter = null;
7864
8245
  if (isDev && chunk === QRL_RUNTIME_CHUNK) {
7865
8246
  const backChannel = globalThis.__qrl_back_channel__;
7866
- assertDefined(backChannel, 'Missing QRL_RUNTIME_CHUNK');
7867
- qrlRef = backChannel.get(symbol);
8247
+ isDev && assertDefined(backChannel, 'Missing QRL_RUNTIME_CHUNK');
8248
+ const fn = backChannel.get(symbol);
8249
+ if (fn) {
8250
+ qrlImporter = () => Promise.resolve({ [symbol]: fn });
8251
+ }
7868
8252
  }
7869
- return createQRL(chunk, symbol, qrlRef, null, captureIds, null);
8253
+ return createQRL(chunk, symbol, null, qrlImporter, captureIds, null);
7870
8254
  }
7871
8255
  /** Parses "chunk#hash[...rootRef]" */
7872
8256
  function parseQRL(qrl) {
@@ -7978,7 +8362,7 @@ const allocate = (container, typeId, value) => {
7978
8362
  case 30 /* TypeIds.FormData */:
7979
8363
  return new FormData();
7980
8364
  case 31 /* TypeIds.JSXNode */:
7981
- return new JSXNodeImpl(null);
8365
+ return new JSXNodeImpl(null, null, null, null, null);
7982
8366
  case 11 /* TypeIds.BigInt */:
7983
8367
  return BigInt(value);
7984
8368
  case 16 /* TypeIds.Set */:
@@ -8042,6 +8426,8 @@ const allocate = (container, typeId, value) => {
8042
8426
  }
8043
8427
  case 33 /* TypeIds.SubscriptionData */:
8044
8428
  return new SubscriptionData({});
8429
+ case 34 /* TypeIds.EffectSubscription */:
8430
+ return new EffectSubscription(null, null, null, null);
8045
8431
  default:
8046
8432
  throw qError(18 /* QError.serializeErrorCannotAllocate */, [typeId]);
8047
8433
  }
@@ -8164,33 +8550,6 @@ const _regSymbol = (symbol, hash) => {
8164
8550
  return symbol;
8165
8551
  };
8166
8552
 
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
8553
  /** @file Shared types */
8195
8554
  /** @internal */
8196
8555
  function isStringifiable(value) {
@@ -8469,6 +8828,14 @@ async function serialize(serializationContext) {
8469
8828
  else if (value instanceof SubscriptionData) {
8470
8829
  output(33 /* TypeIds.SubscriptionData */, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
8471
8830
  }
8831
+ else if (value instanceof EffectSubscription) {
8832
+ output(34 /* TypeIds.EffectSubscription */, [
8833
+ value.consumer,
8834
+ value.property,
8835
+ value.backRef,
8836
+ value.data,
8837
+ ]);
8838
+ }
8472
8839
  else if (isStore(value)) {
8473
8840
  if (isResource(value)) {
8474
8841
  // let render know about the resource
@@ -8918,7 +9285,7 @@ function filterEffectBackRefs(effectBackRef) {
8918
9285
  let effectBackRefToSerialize = undefined;
8919
9286
  if (effectBackRef) {
8920
9287
  for (const [effectProp, effect] of effectBackRef) {
8921
- if (effect[2 /* EffectSubscriptionProp.BACK_REF */]) {
9288
+ if (effect.backRef) {
8922
9289
  effectBackRefToSerialize ||= new Map();
8923
9290
  effectBackRefToSerialize.set(effectProp, effect);
8924
9291
  }
@@ -9087,6 +9454,7 @@ class _SharedContainer {
9087
9454
  $renderPromise$ = null;
9088
9455
  $resolveRenderPromise$ = null;
9089
9456
  $cursorCount$ = 0;
9457
+ $pausedCursorCount$ = 0;
9090
9458
  constructor(serverData, locale) {
9091
9459
  this.$serverData$ = serverData;
9092
9460
  this.$locale$ = locale;
@@ -9125,8 +9493,6 @@ const applyQwikComponentBody = (ssr, jsx, component) => {
9125
9493
  host.setProp(ELEMENT_KEY, jsx.key);
9126
9494
  }
9127
9495
  return executeComponent(ssr, host, host, componentQrl, srcProps);
9128
- // const componentChore = ssr.$scheduler$(ChoreType.COMPONENT, host, componentQrl, srcProps);
9129
- // return getChorePromise(componentChore);
9130
9496
  };
9131
9497
 
9132
9498
  class ParentComponentData {
@@ -9276,7 +9642,7 @@ function processJSXNode(ssr, enqueue, value, options) {
9276
9642
  children != null && enqueue(children);
9277
9643
  }
9278
9644
  else if (type === Slot) {
9279
- const componentFrame = options.parentComponentFrame || ssr.unclaimedProjectionComponentFrameQueue.shift();
9645
+ const componentFrame = options.parentComponentFrame;
9280
9646
  if (componentFrame) {
9281
9647
  const compId = componentFrame.componentNode.id || '';
9282
9648
  const projectionAttrs = isDev ? [DEBUG_TYPE, "P" /* VirtualType.Projection */] : [];
@@ -9398,6 +9764,9 @@ function toSsrAttrs(record, options) {
9398
9764
  if (isPreventDefault(key)) {
9399
9765
  addPreventDefaultEventToSerializationContext(options.serializationCtx, key);
9400
9766
  }
9767
+ else if (key === ITERATION_ITEM_SINGLE || key === ITERATION_ITEM_MULTI) {
9768
+ value = options.serializationCtx.$addRoot$(value);
9769
+ }
9401
9770
  value = serializeAttribute(key, value, options.styleScopedId);
9402
9771
  ssrAttrs.push(key, value);
9403
9772
  };
@@ -9714,7 +10083,7 @@ const inflate = (container, target, typeId, data) => {
9714
10083
  const d = data;
9715
10084
  let owner = d[0];
9716
10085
  if (owner === _UNINITIALIZED) {
9717
- owner = new JSXNodeImpl(Fragment, d[1], d[2]);
10086
+ owner = new JSXNodeImpl(Fragment, d[1], d[2], null, null);
9718
10087
  owner._proxy = propsProxy;
9719
10088
  }
9720
10089
  propsProxy[_OWNER] = owner;
@@ -9726,6 +10095,15 @@ const inflate = (container, target, typeId, data) => {
9726
10095
  effectData.data.$isConst$ = data[1];
9727
10096
  break;
9728
10097
  }
10098
+ case 34 /* TypeIds.EffectSubscription */: {
10099
+ const effectSub = target;
10100
+ const d = data;
10101
+ effectSub.consumer = d[0];
10102
+ effectSub.property = d[1];
10103
+ effectSub.backRef = d[2];
10104
+ effectSub.data = d[3];
10105
+ break;
10106
+ }
9729
10107
  default:
9730
10108
  throw qError(16 /* QError.serializeErrorNotImplemented */, [typeId]);
9731
10109
  }
@@ -9770,7 +10148,8 @@ function inflateWrappedSignalValue(signal) {
9770
10148
  let hasAttrValue = false;
9771
10149
  if (effects) {
9772
10150
  // Find string keys (attribute names) in the effect back refs
9773
- for (const [_, key] of effects) {
10151
+ for (const effect of effects) {
10152
+ const key = effect.property;
9774
10153
  if (isString(key)) {
9775
10154
  // This is an attribute name, try to read its value
9776
10155
  const attrValue = vnode_getProp(hostVNode, key, null);
@@ -10399,7 +10778,7 @@ class DomContainer extends _SharedContainer {
10399
10778
  };
10400
10779
  getSyncFn(id) {
10401
10780
  const fn = this.$qFuncs$[id];
10402
- assertTrue(typeof fn === 'function', 'Invalid reference: ' + id);
10781
+ isDev && assertTrue(typeof fn === 'function', 'Invalid reference: ' + id);
10403
10782
  return fn;
10404
10783
  }
10405
10784
  $appendStyle$(content, styleId, host, scoped) {
@@ -10441,6 +10820,81 @@ class DomContainer extends _SharedContainer {
10441
10820
  }
10442
10821
  }
10443
10822
 
10823
+ /** @internal */
10824
+ const useTaskQrl = (qrl, opts) => {
10825
+ const { val, set, iCtx, i } = useSequentialScope();
10826
+ if (val) {
10827
+ return;
10828
+ }
10829
+ assertQrl(qrl);
10830
+ set(1);
10831
+ const taskFlags =
10832
+ // enabled by default
10833
+ opts?.deferUpdates === false ? 0 : 16 /* TaskFlags.RENDER_BLOCKING */;
10834
+ const task = new Task(8 /* TaskFlags.DIRTY */ | 2 /* TaskFlags.TASK */ | taskFlags, i, iCtx.$hostElement$, qrl, undefined, null);
10835
+ // In V2 we add the task to the sequential scope. We need to do this
10836
+ // in order to be able to retrieve it later when the parent element is
10837
+ // deleted and we need to be able to release the task subscriptions.
10838
+ set(task);
10839
+ const container = iCtx.$container$;
10840
+ const { $waitOn$: waitOn } = iCtx;
10841
+ const result = maybeThen(waitOn, () => runTask(task, container, iCtx.$hostElement$));
10842
+ if (isPromise(result)) {
10843
+ iCtx.$waitOn$ = result;
10844
+ }
10845
+ };
10846
+ const runTask = (task, container, host) => {
10847
+ task.$flags$ &= -9 /* TaskFlags.DIRTY */;
10848
+ cleanupDestroyable(task);
10849
+ const iCtx = newInvokeContext(container.$locale$, host, TaskEvent);
10850
+ iCtx.$container$ = container;
10851
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
10852
+ const track = trackFn(task, container);
10853
+ const [cleanup] = cleanupFn(task, (reason) => container.handleError(reason, host));
10854
+ const taskApi = { track, cleanup };
10855
+ return safeCall(() => taskFn(taskApi), cleanup, (err) => {
10856
+ // If a Promise is thrown, that means we need to re-run the task.
10857
+ if (isPromise(err)) {
10858
+ return err.then(() => runTask(task, container, host));
10859
+ }
10860
+ else {
10861
+ container.handleError(err, host);
10862
+ }
10863
+ });
10864
+ };
10865
+ class Task extends BackRef {
10866
+ $flags$;
10867
+ $index$;
10868
+ $el$;
10869
+ $qrl$;
10870
+ $state$;
10871
+ $destroy$;
10872
+ constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
10873
+ super();
10874
+ this.$flags$ = $flags$;
10875
+ this.$index$ = $index$;
10876
+ this.$el$ = $el$;
10877
+ this.$qrl$ = $qrl$;
10878
+ this.$state$ = $state$;
10879
+ this.$destroy$ = $destroy$;
10880
+ }
10881
+ }
10882
+ /** @internal */
10883
+ const isTask = (value) => {
10884
+ return value instanceof Task;
10885
+ };
10886
+ /**
10887
+ * Used internally as a qrl event handler to schedule a task.
10888
+ *
10889
+ * @internal
10890
+ */
10891
+ const scheduleTask = (_event, element) => {
10892
+ const [task] = useLexicalScope();
10893
+ const container = getDomContainer(element);
10894
+ task.$flags$ |= 8 /* TaskFlags.DIRTY */;
10895
+ markVNodeDirty(container, task.$el$, 1 /* ChoreBits.TASKS */);
10896
+ };
10897
+
10444
10898
  const throwIfQRLNotResolved = (qrl) => {
10445
10899
  const resolved = qrl.resolved;
10446
10900
  if (!resolved) {
@@ -10457,18 +10911,16 @@ const isSignal = (value) => {
10457
10911
  return value instanceof SignalImpl;
10458
10912
  };
10459
10913
  const ensureContainsSubscription = (array, effectSubscription) => {
10460
- !array.has(effectSubscription) && array.add(effectSubscription);
10914
+ array.add(effectSubscription);
10461
10915
  };
10462
10916
  /** Ensure the item is in back refs set */
10463
10917
  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);
10918
+ (array.backRef ||= new Set()).add(value);
10467
10919
  };
10468
10920
  const addQrlToSerializationCtx = (effectSubscriber, container) => {
10469
- if (!!container && !isDomContainer(container)) {
10470
- const effect = effectSubscriber[0 /* EffectSubscriptionProp.CONSUMER */];
10471
- const property = effectSubscriber[1 /* EffectSubscriptionProp.PROPERTY */];
10921
+ if (container) {
10922
+ const effect = effectSubscriber.consumer;
10923
+ const property = effectSubscriber.property;
10472
10924
  let qrl = null;
10473
10925
  if (isTask(effect)) {
10474
10926
  qrl = effect.$qrl$;
@@ -10485,24 +10937,15 @@ const addQrlToSerializationCtx = (effectSubscriber, container) => {
10485
10937
  }
10486
10938
  };
10487
10939
  const scheduleEffects = (container, signal, effects) => {
10488
- const isBrowser = !isServerPlatform();
10940
+ const isBrowser = import.meta.env.TEST ? !isServerPlatform() : !isServer;
10489
10941
  if (effects) {
10490
- let tasksToTrigger = null;
10491
10942
  const scheduleEffect = (effectSubscription) => {
10492
- const consumer = effectSubscription[0 /* EffectSubscriptionProp.CONSUMER */];
10493
- const property = effectSubscription[1 /* EffectSubscriptionProp.PROPERTY */];
10494
- assertDefined(container, 'Container must be defined.');
10943
+ const consumer = effectSubscription.consumer;
10944
+ const property = effectSubscription.property;
10945
+ isDev && assertDefined(container, 'Container must be defined.');
10495
10946
  if (isTask(consumer)) {
10496
10947
  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
- }
10948
+ markVNodeDirty(container, consumer.$el$, 1 /* ChoreBits.TASKS */);
10506
10949
  }
10507
10950
  else if (consumer instanceof SignalImpl) {
10508
10951
  consumer.invalidate();
@@ -10517,7 +10960,7 @@ const scheduleEffects = (container, signal, effects) => {
10517
10960
  }
10518
10961
  }
10519
10962
  else {
10520
- const effectData = effectSubscription[3 /* EffectSubscriptionProp.DATA */];
10963
+ const effectData = effectSubscription.data;
10521
10964
  if (effectData instanceof SubscriptionData) {
10522
10965
  const data = effectData.data;
10523
10966
  const payload = {
@@ -10541,14 +10984,10 @@ const scheduleEffects = (container, signal, effects) => {
10541
10984
  }
10542
10985
  }
10543
10986
  };
10544
- for (const effect of effects) {
10987
+ const effectsSnapshot = Array.from(effects);
10988
+ for (const effect of effectsSnapshot) {
10545
10989
  scheduleEffect(effect);
10546
10990
  }
10547
- if (!isBrowser && container && tasksToTrigger) {
10548
- for (const task of tasksToTrigger) {
10549
- markVNodeDirty(container, task.$el$, 1 /* ChoreBits.TASKS */);
10550
- }
10551
- }
10552
10991
  }
10553
10992
  };
10554
10993
  /** @internal */
@@ -10779,7 +11218,8 @@ class StoreHandler {
10779
11218
  this.$container$ = ctx.$container$;
10780
11219
  }
10781
11220
  else {
10782
- assertTrue(!ctx.$container$ || ctx.$container$ === this.$container$, 'Do not use signals across containers');
11221
+ isDev &&
11222
+ assertTrue(!ctx.$container$ || ctx.$container$ === this.$container$, 'Do not use signals across containers');
10783
11223
  }
10784
11224
  const effectSubscriber = ctx.$effectSubscriber$;
10785
11225
  if (effectSubscriber) {
@@ -10881,7 +11321,8 @@ function addStoreEffect(target, prop, store, effectSubscription) {
10881
11321
  // to this signal.
10882
11322
  ensureContainsBackRef(effectSubscription, target);
10883
11323
  // TODO is this needed with the preloader?
10884
- addQrlToSerializationCtx(effectSubscription, store.$container$);
11324
+ (import.meta.env.TEST ? !isDomContainer(store.$container$) : isServer) &&
11325
+ addQrlToSerializationCtx(effectSubscription, store.$container$);
10885
11326
  }
10886
11327
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
10887
11328
  target[prop] = value;
@@ -11267,7 +11708,7 @@ function getObjectById(id, stateData) {
11267
11708
  if (typeof id === 'string') {
11268
11709
  id = parseInt(id, 10);
11269
11710
  }
11270
- assertTrue(id < stateData.length, `Invalid reference ${id} >= ${stateData.length}`);
11711
+ isDev && assertTrue(id < stateData.length, `Invalid reference ${id} >= ${stateData.length}`);
11271
11712
  return stateData[id];
11272
11713
  }
11273
11714
  function _createDeserializeContainer(stateData, element) {
@@ -11526,7 +11967,7 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
11526
11967
  }
11527
11968
  if (chunk === '') {
11528
11969
  // Sync QRL
11529
- assertDefined(_containerEl, 'Sync QRL must have container element');
11970
+ isDev && assertDefined(_containerEl, 'Sync QRL must have container element');
11530
11971
  const hash = _containerEl.getAttribute(QInstanceAttr);
11531
11972
  const doc = _containerEl.ownerDocument;
11532
11973
  const qFuncs = getQFuncs(doc, hash);
@@ -11768,8 +12209,8 @@ const _qrlSync = function (fn, serializedFn) {
11768
12209
  const componentQrl = (componentQrl) => {
11769
12210
  // Return a QComponent Factory function.
11770
12211
  function QwikComponent(props, key, flags = 0) {
11771
- assertQrl(componentQrl);
11772
- assertNumber(flags, 'The Qwik Component was not invoked correctly');
12212
+ isDev && assertQrl(componentQrl);
12213
+ isDev && assertNumber(flags, 'The Qwik Component was not invoked correctly');
11773
12214
  const hash = qTest ? 'sX' : componentQrl.$hash$.slice(0, 4);
11774
12215
  const finalKey = hash + ':' + (key ? key : '');
11775
12216
  const InnerCmp = () => { };
@@ -12713,15 +13154,20 @@ const useVisibleTaskQrl = (qrl, opts) => {
12713
13154
  return;
12714
13155
  }
12715
13156
  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);
13157
+ let flags;
12719
13158
  if (!isServerPlatform()) {
12720
- if (!qrl.resolved) {
12721
- qrl.resolve();
12722
- }
13159
+ // In DOM we immediately execute
13160
+ flags = 1 /* TaskFlags.VISIBLE_TASK */ | 8 /* TaskFlags.DIRTY */;
13161
+ qrl.resolve();
12723
13162
  markVNodeDirty(iCtx.$container$, iCtx.$hostElement$, 1 /* ChoreBits.TASKS */);
12724
13163
  }
13164
+ else {
13165
+ // In SSR we defer execution until triggered in DOM
13166
+ flags = 1 /* TaskFlags.VISIBLE_TASK */;
13167
+ }
13168
+ const task = new Task(flags, i, iCtx.$hostElement$, qrl, undefined, null);
13169
+ set(task);
13170
+ useRegisterTaskEvents(task, eagerness);
12725
13171
  };
12726
13172
  const useRegisterTaskEvents = (task, eagerness) => {
12727
13173
  if (eagerness === 'intersection-observer') {