@qwik.dev/core 2.0.0-beta.14 → 2.0.0-beta.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * @qwik.dev/core 2.0.0-beta.14-dev+1e7496d
3
+ * @qwik.dev/core 2.0.0-beta.16-dev+a83ccf3
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.14-dev+1e7496d";
17
+ const version = "2.0.0-beta.16-dev+a83ccf3";
18
18
 
19
19
  // same as isDev but separate so we can test
20
20
  const qDev = globalThis.qDev !== false;
@@ -251,7 +251,6 @@ const ELEMENT_KEY = 'q:key';
251
251
  const ELEMENT_PROPS = 'q:props';
252
252
  const ELEMENT_SEQ = 'q:seq';
253
253
  const ELEMENT_SEQ_IDX = 'q:seqIdx';
254
- const Q_PREFIX = 'q:';
255
254
  /** Non serializable markers - always begins with `:` character */
256
255
  const NON_SERIALIZABLE_MARKER_PREFIX = ':';
257
256
  const USE_ON_LOCAL = NON_SERIALIZABLE_MARKER_PREFIX + 'on';
@@ -916,7 +915,7 @@ const STORE_ALL_PROPS = Symbol('store.all');
916
915
  class SignalImpl {
917
916
  $untrackedValue$;
918
917
  /** Store a list of effects which are dependent on this signal. */
919
- $effects$ = null;
918
+ $effects$ = undefined;
920
919
  $container$ = null;
921
920
  $wrappedSignal$ = null;
922
921
  constructor(container, value) {
@@ -928,7 +927,7 @@ class SignalImpl {
928
927
  * remained the same object
929
928
  */
930
929
  force() {
931
- this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
930
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, undefined, this, this.$effects$);
932
931
  }
933
932
  get untrackedValue() {
934
933
  return this.$untrackedValue$;
@@ -943,7 +942,7 @@ class SignalImpl {
943
942
  set value(value) {
944
943
  if (value !== this.$untrackedValue$) {
945
944
  this.$untrackedValue$ = value;
946
- this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
945
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, undefined, this, this.$effects$);
947
946
  }
948
947
  }
949
948
  // prevent accidental use as value
@@ -1005,6 +1004,8 @@ const _CONST_PROPS = Symbol('CONST');
1005
1004
  const _VAR_PROPS = Symbol('VAR');
1006
1005
  /** @internal */
1007
1006
  const _OWNER = Symbol('OWNER');
1007
+ /** @internal */
1008
+ const _PROPS_HANDLER = Symbol('PROPS_HANDLER');
1008
1009
  /** @internal @deprecated v1 compat */
1009
1010
  const _IMMUTABLE = Symbol('IMMUTABLE');
1010
1011
  /** @internal */
@@ -1080,6 +1081,13 @@ const getEventDataFromHtmlAttribute = (htmlKey) => {
1080
1081
  }
1081
1082
  return ['document', htmlKey.substring(12)];
1082
1083
  };
1084
+ const getScopedEventName = (scope, eventName) => {
1085
+ const suffix = ':' + eventName;
1086
+ return scope ? scope + suffix : suffix;
1087
+ };
1088
+ const getLoaderScopedEventName = (scope, scopedEvent) => {
1089
+ return scope ? '-' + scopedEvent : scopedEvent;
1090
+ };
1083
1091
 
1084
1092
  /** @internal */
1085
1093
  const EMPTY_ARRAY = [];
@@ -1093,6 +1101,8 @@ function createPropsProxy(owner) {
1093
1101
  }
1094
1102
  class PropsProxyHandler {
1095
1103
  owner;
1104
+ $effects$ = undefined;
1105
+ $container$ = null;
1096
1106
  constructor(owner) {
1097
1107
  this.owner = owner;
1098
1108
  }
@@ -1101,12 +1111,15 @@ class PropsProxyHandler {
1101
1111
  if (prop === _CONST_PROPS) {
1102
1112
  return this.owner.constProps;
1103
1113
  }
1104
- if (prop === _VAR_PROPS) {
1114
+ else if (prop === _VAR_PROPS) {
1105
1115
  return this.owner.varProps;
1106
1116
  }
1107
- if (prop === _OWNER) {
1117
+ else if (prop === _OWNER) {
1108
1118
  return this.owner;
1109
1119
  }
1120
+ else if (prop === _PROPS_HANDLER) {
1121
+ return this;
1122
+ }
1110
1123
  let value;
1111
1124
  if (prop === 'children') {
1112
1125
  value = this.owner.children;
@@ -1119,6 +1132,9 @@ class PropsProxyHandler {
1119
1132
  }
1120
1133
  }
1121
1134
  value = directGetPropsProxyProp(this.owner, prop);
1135
+ if (prop in this.owner.varProps) {
1136
+ addPropsProxyEffect(this, prop);
1137
+ }
1122
1138
  }
1123
1139
  // a proxied value that the optimizer made
1124
1140
  return value instanceof WrappedSignalImpl && value.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */
@@ -1133,6 +1149,12 @@ class PropsProxyHandler {
1133
1149
  else if (prop === 'children') {
1134
1150
  this.owner.children = value;
1135
1151
  }
1152
+ else if (prop === _CONST_PROPS) {
1153
+ this.owner.constProps = value;
1154
+ }
1155
+ else if (prop === _VAR_PROPS) {
1156
+ this.owner.varProps = value;
1157
+ }
1136
1158
  else {
1137
1159
  if (typeof prop === 'string' && typeof this.owner.type === 'string') {
1138
1160
  const attr = jsxEventToHtmlAttribute(prop);
@@ -1150,12 +1172,18 @@ class PropsProxyHandler {
1150
1172
  else if (!(prop in this.owner.varProps)) {
1151
1173
  this.owner.toSort = true;
1152
1174
  }
1153
- this.owner.varProps[prop] = value;
1175
+ if (this.owner.varProps[prop] !== value) {
1176
+ this.owner.varProps[prop] = value;
1177
+ triggerPropsProxyEffect(this, prop);
1178
+ }
1154
1179
  }
1155
1180
  return true;
1156
1181
  }
1157
1182
  deleteProperty(_, prop) {
1158
1183
  let didDelete = delete this.owner.varProps[prop];
1184
+ if (didDelete) {
1185
+ triggerPropsProxyEffect(this, prop);
1186
+ }
1159
1187
  if (this.owner.constProps) {
1160
1188
  didDelete = delete this.owner.constProps[prop] || didDelete;
1161
1189
  }
@@ -1172,13 +1200,19 @@ class PropsProxyHandler {
1172
1200
  else if (prop === _CONST_PROPS || prop === _VAR_PROPS) {
1173
1201
  return true;
1174
1202
  }
1175
- if (typeof prop === 'string' && typeof this.owner.type === 'string') {
1176
- const attr = jsxEventToHtmlAttribute(prop);
1177
- if (attr) {
1178
- prop = attr;
1203
+ const inVarProps = prop in this.owner.varProps;
1204
+ if (typeof prop === 'string') {
1205
+ if (inVarProps) {
1206
+ addPropsProxyEffect(this, prop);
1207
+ }
1208
+ if (typeof this.owner.type === 'string') {
1209
+ const attr = jsxEventToHtmlAttribute(prop);
1210
+ if (attr) {
1211
+ prop = attr;
1212
+ }
1179
1213
  }
1180
1214
  }
1181
- return (prop in this.owner.varProps || (this.owner.constProps ? prop in this.owner.constProps : false));
1215
+ return inVarProps || (this.owner.constProps ? prop in this.owner.constProps : false);
1182
1216
  }
1183
1217
  getOwnPropertyDescriptor(_, p) {
1184
1218
  const value = p === 'children'
@@ -1207,6 +1241,34 @@ class PropsProxyHandler {
1207
1241
  return out;
1208
1242
  }
1209
1243
  }
1244
+ const addPropsProxyEffect = (propsProxy, prop) => {
1245
+ // Lazily grab the container from the invoke context
1246
+ const ctx = tryGetInvokeContext();
1247
+ if (ctx) {
1248
+ if (propsProxy.$container$ === null) {
1249
+ if (ctx.$container$) {
1250
+ propsProxy.$container$ = ctx.$container$;
1251
+ }
1252
+ }
1253
+ else {
1254
+ assertTrue(!ctx.$container$ || ctx.$container$ === propsProxy.$container$, 'Do not use props across containers');
1255
+ }
1256
+ }
1257
+ const effectSubscriber = ctx?.$effectSubscriber$;
1258
+ if (effectSubscriber) {
1259
+ addStoreEffect(propsProxy.owner._proxy, prop, propsProxy, effectSubscriber);
1260
+ }
1261
+ };
1262
+ const triggerPropsProxyEffect = (propsProxy, prop) => {
1263
+ const effects = getEffects$1(propsProxy.$effects$, prop);
1264
+ if (effects) {
1265
+ propsProxy.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, undefined, propsProxy, effects);
1266
+ }
1267
+ };
1268
+ function getEffects$1(effects, prop) {
1269
+ // TODO: Handle STORE_ALL_PROPS
1270
+ return effects?.get(prop);
1271
+ }
1210
1272
  /**
1211
1273
  * Instead of using PropsProxyHandler getter (which could create a component-level subscription).
1212
1274
  * Use this function to get the props directly from a const or var props.
@@ -1290,6 +1352,7 @@ const trackFn = (target, container) => (obj, prop) => {
1290
1352
  return obj.value;
1291
1353
  }
1292
1354
  else if (isObject(obj) && isStore(obj)) {
1355
+ // TODO: handle props proxy
1293
1356
  // track whole store
1294
1357
  addStoreEffect(getStoreTarget(obj), STORE_ALL_PROPS, getStoreHandler(obj), ctx.$effectSubscriber$);
1295
1358
  return obj;
@@ -1340,7 +1403,7 @@ class ComputedSignalImpl extends SignalImpl {
1340
1403
  */
1341
1404
  $computeQrl$;
1342
1405
  $flags$;
1343
- [_EFFECT_BACK_REF] = null;
1406
+ [_EFFECT_BACK_REF] = undefined;
1344
1407
  constructor(container, fn,
1345
1408
  // We need a separate flag to know when the computation needs running because
1346
1409
  // we need the old value to know if effects need running after computation
@@ -1354,7 +1417,7 @@ class ComputedSignalImpl extends SignalImpl {
1354
1417
  }
1355
1418
  invalidate() {
1356
1419
  this.$flags$ |= 1 /* SignalFlags.INVALID */;
1357
- this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
1420
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, undefined, this, this.$effects$);
1358
1421
  }
1359
1422
  /**
1360
1423
  * Use this to force running subscribers, for example when the calculated value has mutated but
@@ -1422,13 +1485,13 @@ class ComputedSignalImpl extends SignalImpl {
1422
1485
  */
1423
1486
  class AsyncComputedSignalImpl extends ComputedSignalImpl {
1424
1487
  $untrackedLoading$ = false;
1425
- $untrackedError$ = null;
1426
- $loadingEffects$ = null;
1427
- $errorEffects$ = null;
1488
+ $untrackedError$ = undefined;
1489
+ $loadingEffects$ = undefined;
1490
+ $errorEffects$ = undefined;
1428
1491
  $destroy$;
1429
1492
  $promiseValue$ = NEEDS_COMPUTATION;
1430
1493
  $promise$ = null;
1431
- [_EFFECT_BACK_REF] = null;
1494
+ [_EFFECT_BACK_REF] = undefined;
1432
1495
  constructor(container, fn, flags = 1 /* SignalFlags.INVALID */) {
1433
1496
  super(container, fn, flags);
1434
1497
  }
@@ -1442,7 +1505,7 @@ class AsyncComputedSignalImpl extends ComputedSignalImpl {
1442
1505
  set untrackedLoading(value) {
1443
1506
  if (value !== this.$untrackedLoading$) {
1444
1507
  this.$untrackedLoading$ = value;
1445
- this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$loadingEffects$);
1508
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, undefined, this, this.$loadingEffects$);
1446
1509
  }
1447
1510
  }
1448
1511
  get untrackedLoading() {
@@ -1455,7 +1518,7 @@ class AsyncComputedSignalImpl extends ComputedSignalImpl {
1455
1518
  set untrackedError(value) {
1456
1519
  if (value !== this.$untrackedError$) {
1457
1520
  this.$untrackedError$ = value;
1458
- this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$errorEffects$);
1521
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, undefined, this, this.$errorEffects$);
1459
1522
  }
1460
1523
  }
1461
1524
  get untrackedError() {
@@ -1486,7 +1549,7 @@ class AsyncComputedSignalImpl extends ComputedSignalImpl {
1486
1549
  if (isPromise(untrackedValue)) {
1487
1550
  const isFirstComputation = this.$promiseValue$ === NEEDS_COMPUTATION;
1488
1551
  this.untrackedLoading = true;
1489
- this.untrackedError = null;
1552
+ this.untrackedError = undefined;
1490
1553
  if (this.$promiseValue$ !== NEEDS_COMPUTATION) {
1491
1554
  // skip cleanup after resuming
1492
1555
  cleanupDestroyable(this);
@@ -1495,7 +1558,7 @@ class AsyncComputedSignalImpl extends ComputedSignalImpl {
1495
1558
  .then((promiseValue) => {
1496
1559
  this.$promiseValue$ = promiseValue;
1497
1560
  this.untrackedLoading = false;
1498
- this.untrackedError = null;
1561
+ this.untrackedError = undefined;
1499
1562
  if (this.setValue(promiseValue)) {
1500
1563
  scheduleEffects(this.$container$, this, this.$effects$);
1501
1564
  }
@@ -1728,25 +1791,32 @@ const _wrapProp = (...args) => {
1728
1791
  }
1729
1792
  if (isPropsProxy(obj)) {
1730
1793
  const constProps = obj[_CONST_PROPS];
1794
+ const varProps = obj[_VAR_PROPS];
1731
1795
  if (constProps && prop in constProps) {
1732
1796
  // Const props don't need wrapping
1733
1797
  return constProps[prop];
1734
1798
  }
1799
+ else if (prop in varProps) {
1800
+ const value = varProps[prop];
1801
+ return wrapIfNotSignal(value, args);
1802
+ }
1735
1803
  }
1736
1804
  else {
1737
1805
  const target = getStoreTarget(obj);
1738
1806
  if (target) {
1739
1807
  const value = target[prop];
1740
- const wrappedValue = isSignal(value)
1741
- ? // If the value is already a signal, we don't need to wrap it again
1742
- value
1743
- : getWrapped(args);
1744
- return wrappedValue;
1808
+ return wrapIfNotSignal(value, args);
1745
1809
  }
1746
1810
  }
1747
1811
  // the object is not reactive, so we can just return the value
1748
1812
  return obj[prop];
1749
1813
  };
1814
+ const wrapIfNotSignal = (value, args) => {
1815
+ return (isSignal(value)
1816
+ ? // If the value is already a signal, we don't need to wrap it again
1817
+ value
1818
+ : getWrapped(args));
1819
+ };
1750
1820
  /** @internal @deprecated v1 compat */
1751
1821
  const _wrapSignal = (obj, prop) => {
1752
1822
  const r = _wrapProp(obj, prop);
@@ -1761,8 +1831,8 @@ class WrappedSignalImpl extends SignalImpl {
1761
1831
  $func$;
1762
1832
  $funcStr$;
1763
1833
  $flags$;
1764
- $hostElement$ = null;
1765
- [_EFFECT_BACK_REF] = null;
1834
+ $hostElement$ = undefined;
1835
+ [_EFFECT_BACK_REF] = undefined;
1766
1836
  constructor(container, fn, args, fnStr,
1767
1837
  // We need a separate flag to know when the computation needs running because
1768
1838
  // we need the old value to know if effects need running after computation
@@ -1838,7 +1908,7 @@ class WrappedSignalImpl extends SignalImpl {
1838
1908
 
1839
1909
  /** Class for back reference to the EffectSubscription */
1840
1910
  let BackRef$1 = class BackRef {
1841
- [_EFFECT_BACK_REF] = null;
1911
+ [_EFFECT_BACK_REF] = undefined;
1842
1912
  };
1843
1913
  function clearAllEffects(container, consumer) {
1844
1914
  if (vnode_isVNode(consumer) && vnode_isElementVNode(consumer)) {
@@ -1851,6 +1921,7 @@ function clearAllEffects(container, consumer) {
1851
1921
  for (const [, effect] of effects) {
1852
1922
  clearEffectSubscription(container, effect);
1853
1923
  }
1924
+ effects.clear();
1854
1925
  }
1855
1926
  function clearEffectSubscription(container, effect) {
1856
1927
  const backRefs = effect[2 /* EffectSubscriptionProp.BACK_REF */];
@@ -1864,12 +1935,17 @@ function clearEffectSubscription(container, effect) {
1864
1935
  else if (producer instanceof AsyncComputedSignalImpl) {
1865
1936
  clearAsyncComputedSignal(producer, effect);
1866
1937
  }
1938
+ else if (isPropsProxy(producer)) {
1939
+ const propsHandler = producer[_PROPS_HANDLER];
1940
+ clearStoreOrProps(propsHandler, effect);
1941
+ }
1867
1942
  else if (container.$storeProxyMap$.has(producer)) {
1868
1943
  const target = container.$storeProxyMap$.get(producer);
1869
1944
  const storeHandler = getStoreHandler(target);
1870
- clearStore(storeHandler, effect);
1945
+ clearStoreOrProps(storeHandler, effect);
1871
1946
  }
1872
1947
  }
1948
+ backRefs.clear();
1873
1949
  }
1874
1950
  function clearSignal(container, producer, effect) {
1875
1951
  const effects = producer.$effects$;
@@ -1877,7 +1953,7 @@ function clearSignal(container, producer, effect) {
1877
1953
  effects.delete(effect);
1878
1954
  }
1879
1955
  if (producer instanceof WrappedSignalImpl) {
1880
- producer.$hostElement$ = null;
1956
+ producer.$hostElement$ = undefined;
1881
1957
  clearAllEffects(container, producer);
1882
1958
  }
1883
1959
  }
@@ -1891,14 +1967,14 @@ function clearAsyncComputedSignal(producer, effect) {
1891
1967
  pendingEffects.delete(effect);
1892
1968
  }
1893
1969
  }
1894
- function clearStore(producer, effect) {
1970
+ function clearStoreOrProps(producer, effect) {
1895
1971
  const effects = producer?.$effects$;
1896
1972
  if (effects) {
1897
- for (const [propKey, propEffects] of effects.entries()) {
1973
+ for (const [prop, propEffects] of effects.entries()) {
1898
1974
  if (propEffects.has(effect)) {
1899
1975
  propEffects.delete(effect);
1900
1976
  if (propEffects.size === 0) {
1901
- effects.delete(propKey);
1977
+ effects.delete(prop);
1902
1978
  }
1903
1979
  }
1904
1980
  }
@@ -2711,23 +2787,22 @@ const vnode_applyJournal = (journal) => {
2711
2787
  key = 'class';
2712
2788
  }
2713
2789
  const value = journal[idx++];
2790
+ const shouldRemove = value == null || value === false;
2714
2791
  if (isBooleanAttr(element, key)) {
2715
2792
  element[key] = parseBoolean(value);
2716
2793
  }
2717
- else if (key === 'value' && key in element) {
2718
- element.value = String(value);
2719
- }
2720
2794
  else if (key === dangerouslySetInnerHTML) {
2721
2795
  element.innerHTML = value;
2722
2796
  element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
2723
2797
  }
2798
+ else if (shouldRemove) {
2799
+ element.removeAttribute(key);
2800
+ }
2801
+ else if (key === 'value' && key in element) {
2802
+ element.value = String(value);
2803
+ }
2724
2804
  else {
2725
- if (value == null || value === false) {
2726
- element.removeAttribute(key);
2727
- }
2728
- else {
2729
- element.setAttribute(key, String(value));
2730
- }
2805
+ element.setAttribute(key, String(value));
2731
2806
  }
2732
2807
  break;
2733
2808
  case 3 /* VNodeJournalOpCode.HoistStyles */:
@@ -3467,6 +3542,7 @@ function materializeFromVNodeData(vParent, vData, element, child) {
3467
3542
  const nodeIsElement = isElement(node);
3468
3543
  return !nodeIsElement || (nodeIsElement && shouldSkipElement(node));
3469
3544
  };
3545
+ let components = null;
3470
3546
  processVNodeData$1(vData, (peek, consumeValue, consume, getChar, nextToConsumeIdx) => {
3471
3547
  if (isNumber(peek())) {
3472
3548
  // Element counts get encoded as numbers.
@@ -3493,6 +3569,7 @@ function materializeFromVNodeData(vParent, vData, element, child) {
3493
3569
  vParent.setAttr(QScopedStyle, consumeValue(), null);
3494
3570
  }
3495
3571
  else if (peek() === VNodeDataChar.RENDER_FN) {
3572
+ (components ||= []).push(vParent);
3496
3573
  vParent.setAttr(OnRenderProp, consumeValue(), null);
3497
3574
  }
3498
3575
  else if (peek() === VNodeDataChar.ID) {
@@ -3588,6 +3665,15 @@ function materializeFromVNodeData(vParent, vData, element, child) {
3588
3665
  // Text nodes get encoded as alphanumeric characters.
3589
3666
  }
3590
3667
  });
3668
+ if (components) {
3669
+ if (!container) {
3670
+ container = getDomContainer(element);
3671
+ }
3672
+ for (const component of components) {
3673
+ container.ensureProjectionResolved(component);
3674
+ }
3675
+ components = null;
3676
+ }
3591
3677
  vParent.lastChild = vLast;
3592
3678
  return vFirst;
3593
3679
  }
@@ -4338,14 +4424,14 @@ const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
4338
4424
  for (const k in varProps) {
4339
4425
  if (k === 'children') {
4340
4426
  children ||= varProps.children;
4341
- varProps.children = undefined;
4427
+ delete varProps.children;
4342
4428
  }
4343
4429
  else if (k === 'key') {
4344
4430
  key ||= varProps.key;
4345
- varProps.key = undefined;
4431
+ delete varProps.key;
4346
4432
  }
4347
4433
  else if (constProps && k in constProps) {
4348
- varProps[k] = undefined;
4434
+ delete varProps[k];
4349
4435
  }
4350
4436
  }
4351
4437
  }
@@ -5771,8 +5857,9 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
5771
5857
  else if (isSignal(jsxValue)) {
5772
5858
  expectVirtual("S" /* VirtualType.WrappedSignal */, null);
5773
5859
  const unwrappedSignal = jsxValue instanceof WrappedSignalImpl ? jsxValue.$unwrapIfSignal$() : jsxValue;
5774
- const currentSignal = vCurrent?.[_EFFECT_BACK_REF]?.get("." /* EffectProperty.VNODE */)?.[0 /* EffectSubscriptionProp.CONSUMER */];
5775
- if (currentSignal !== unwrappedSignal) {
5860
+ const hasUnwrappedSignal = vCurrent?.[_EFFECT_BACK_REF]
5861
+ ?.get("." /* EffectProperty.VNODE */)?.[2 /* EffectSubscriptionProp.BACK_REF */]?.has(unwrappedSignal);
5862
+ if (!hasUnwrappedSignal) {
5776
5863
  const vHost = (vNewNode || vCurrent);
5777
5864
  descend(resolveSignalAndDescend(() => trackSignalAndAssignHost(unwrappedSignal, vHost, "." /* EffectProperty.VNODE */, container)), true);
5778
5865
  }
@@ -6195,15 +6282,17 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
6195
6282
  if (isHtmlAttributeAnEventName(key)) {
6196
6283
  const data = getEventDataFromHtmlAttribute(key);
6197
6284
  if (data) {
6198
- const scope = data[0];
6199
- const eventName = data[1];
6285
+ const [scope, eventName] = data;
6286
+ const scopedEvent = getScopedEventName(scope, eventName);
6287
+ const loaderScopedEvent = getLoaderScopedEventName(scope, scopedEvent);
6200
6288
  if (eventName) {
6201
- vNewNode.setProp(HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
6289
+ vNewNode.setProp(HANDLER_PREFIX + ':' + scopedEvent, value);
6202
6290
  if (scope) {
6203
6291
  // window and document need attrs so qwik loader can find them
6204
6292
  vNewNode.setAttr(key, '', journal);
6205
6293
  }
6206
- registerQwikLoaderEvent(eventName);
6294
+ // register an event for qwik loader (window/document prefixed with '-')
6295
+ registerQwikLoaderEvent(loaderScopedEvent);
6207
6296
  }
6208
6297
  }
6209
6298
  needsQDispatchEventPatch = true;
@@ -6357,10 +6446,37 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
6357
6446
  let srcIdx = 0;
6358
6447
  let dstIdx = 0;
6359
6448
  let patchEventDispatch = false;
6360
- const setAttribute = (key, value, vHost) => {
6361
- vHost.setAttr(key, value !== null ? serializeAttribute(key, value, scopedStyleIdPrefix) : null, journal);
6449
+ /**
6450
+ * Optimized setAttribute that bypasses redundant checks when we already know:
6451
+ *
6452
+ * - The index in dstAttrs (no need for binary search)
6453
+ * - The vnode is ElementVNode (no instanceof check)
6454
+ * - The value has changed (no comparison needed)
6455
+ */
6456
+ const setAttributeDirect = (vnode, key, value, dstIdx, isNewKey) => {
6457
+ const serializedValue = value != null ? serializeAttribute(key, value, scopedStyleIdPrefix) : null;
6458
+ if (isNewKey) {
6459
+ // Adding new key - splice into sorted position
6460
+ if (serializedValue != null) {
6461
+ dstAttrs.splice(dstIdx, 0, key, serializedValue);
6462
+ journal.push(2 /* VNodeJournalOpCode.SetAttribute */, vnode.element, key, serializedValue);
6463
+ }
6464
+ }
6465
+ else {
6466
+ // Updating or removing existing key at dstIdx
6467
+ if (serializedValue != null) {
6468
+ // Update existing value
6469
+ dstAttrs[dstIdx + 1] = serializedValue;
6470
+ journal.push(2 /* VNodeJournalOpCode.SetAttribute */, vnode.element, key, serializedValue);
6471
+ }
6472
+ else {
6473
+ // Remove key (value is null)
6474
+ dstAttrs.splice(dstIdx, 2);
6475
+ journal.push(2 /* VNodeJournalOpCode.SetAttribute */, vnode.element, key, null);
6476
+ }
6477
+ }
6362
6478
  };
6363
- const record = (key, value) => {
6479
+ const record = (key, value, dstIdx, isNewKey) => {
6364
6480
  if (key.startsWith(':')) {
6365
6481
  vnode.setProp(key, value);
6366
6482
  return;
@@ -6404,20 +6520,31 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
6404
6520
  }
6405
6521
  }
6406
6522
  if (isPromise(value)) {
6523
+ // For async values, we can't use the known index since it will be stale by the time
6524
+ // the promise resolves. Do a binary search to find the current index.
6407
6525
  const vHost = vnode;
6408
- const attributePromise = value.then((resolvedValue) => setAttribute(key, resolvedValue, vHost));
6526
+ const attributePromise = value.then((resolvedValue) => {
6527
+ const idx = mapApp_findIndx(dstAttrs, key, 0);
6528
+ const isNewKey = idx < 0;
6529
+ const currentDstIdx = isNewKey ? idx ^ -1 : idx;
6530
+ setAttributeDirect(vHost, key, resolvedValue, currentDstIdx, isNewKey);
6531
+ });
6409
6532
  asyncAttributePromises.push(attributePromise);
6410
6533
  return;
6411
6534
  }
6412
- setAttribute(key, value, vnode);
6535
+ // Always use optimized direct path - we know the index from the merge algorithm
6536
+ setAttributeDirect(vnode, key, value, dstIdx, isNewKey);
6413
6537
  };
6414
6538
  const recordJsxEvent = (key, value) => {
6415
6539
  const data = getEventDataFromHtmlAttribute(key);
6416
6540
  if (data) {
6417
6541
  const [scope, eventName] = data;
6418
- record(':' + scope + ':' + eventName, value);
6419
- // register an event for qwik loader
6420
- registerQwikLoaderEvent(eventName);
6542
+ const scopedEvent = getScopedEventName(scope, eventName);
6543
+ const loaderScopedEvent = getLoaderScopedEventName(scope, scopedEvent);
6544
+ // Pass dummy index values since ':' prefixed keys take early return via setProp
6545
+ record(':' + scopedEvent, value, 0, false);
6546
+ // register an event for qwik loader (window/document prefixed with '-')
6547
+ registerQwikLoaderEvent(loaderScopedEvent);
6421
6548
  patchEventDispatch = true;
6422
6549
  }
6423
6550
  };
@@ -6426,8 +6553,8 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
6426
6553
  while (srcIdx < srcAttrs.length || dstIdx < dstAttrs.length) {
6427
6554
  const srcKey = srcIdx < srcAttrs.length ? srcAttrs[srcIdx] : undefined;
6428
6555
  const dstKey = dstIdx < dstAttrs.length ? dstAttrs[dstIdx] : undefined;
6429
- // Skip special keys in destination (HANDLER_PREFIX, Q_PREFIX)
6430
- if (dstKey?.startsWith(HANDLER_PREFIX) || dstKey?.startsWith(Q_PREFIX)) {
6556
+ // Skip special keys in destination HANDLER_PREFIX
6557
+ if (dstKey?.startsWith(HANDLER_PREFIX)) {
6431
6558
  dstIdx += 2; // skip key and value
6432
6559
  continue;
6433
6560
  }
@@ -6438,7 +6565,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
6438
6565
  dstIdx += 2; // skip key and value
6439
6566
  }
6440
6567
  else {
6441
- record(dstKey, null);
6568
+ record(dstKey, null, dstIdx, false);
6442
6569
  // After removal, dstAttrs shrinks by 2, so don't advance dstIdx
6443
6570
  }
6444
6571
  }
@@ -6449,7 +6576,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
6449
6576
  recordJsxEvent(srcKey, srcValue);
6450
6577
  }
6451
6578
  else {
6452
- record(srcKey, srcValue);
6579
+ record(srcKey, srcValue, dstIdx, true);
6453
6580
  }
6454
6581
  srcIdx += 2; // skip key and value
6455
6582
  // After addition, dstAttrs grows by 2 at sorted position, advance dstIdx
@@ -6465,7 +6592,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
6465
6592
  recordJsxEvent(srcKey, srcValue);
6466
6593
  }
6467
6594
  else {
6468
- record(srcKey, srcValue);
6595
+ record(srcKey, srcValue, dstIdx, false);
6469
6596
  }
6470
6597
  }
6471
6598
  else if (isEventHandler && !vnode.element.qDispatchEvent) {
@@ -6483,7 +6610,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
6483
6610
  recordJsxEvent(srcKey, srcValue);
6484
6611
  }
6485
6612
  else {
6486
- record(srcKey, srcValue);
6613
+ record(srcKey, srcValue, dstIdx, true);
6487
6614
  }
6488
6615
  srcIdx += 2; // skip key and value
6489
6616
  // After addition, dstAttrs grows at sorted position (before dstIdx), advance dstIdx
@@ -6496,7 +6623,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
6496
6623
  dstIdx += 2; // skip key and value
6497
6624
  }
6498
6625
  else {
6499
- record(dstKey, null);
6626
+ record(dstKey, null, dstIdx, false);
6500
6627
  // After removal, dstAttrs shrinks at dstIdx, so don't advance dstIdx
6501
6628
  }
6502
6629
  }
@@ -6698,32 +6825,11 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
6698
6825
  deleteFromSideBuffer(null, lookupKey);
6699
6826
  }
6700
6827
  if (host) {
6701
- let vNodeProps = host.getProp(ELEMENT_PROPS, container.$getObjectById$);
6702
- let propsAreDifferent = false;
6828
+ const vNodeProps = host.getProp(ELEMENT_PROPS, container.$getObjectById$);
6703
6829
  if (!shouldRender) {
6704
- propsAreDifferent =
6705
- propsDiffer(jsxProps[_CONST_PROPS], vNodeProps?.[_CONST_PROPS]) ||
6706
- propsDiffer(jsxProps[_VAR_PROPS], vNodeProps?.[_VAR_PROPS]);
6707
- shouldRender = shouldRender || propsAreDifferent;
6830
+ shouldRender ||= handleProps(host, jsxProps, vNodeProps, container);
6708
6831
  }
6709
6832
  if (shouldRender) {
6710
- if (propsAreDifferent) {
6711
- if (vNodeProps) {
6712
- // Reuse the same props instance, qrls can use the current props instance
6713
- // as a capture ref, so we can't change it.
6714
- // We need to do this directly, because normally we would subscribe to the signals
6715
- // if any signal is there.
6716
- vNodeProps[_CONST_PROPS] = jsxProps[_CONST_PROPS];
6717
- vNodeProps[_VAR_PROPS] = jsxProps[_VAR_PROPS];
6718
- vNodeProps[_OWNER] = jsxProps[_OWNER];
6719
- }
6720
- else if (jsxProps) {
6721
- // If there is no props instance, create a new one.
6722
- // We can do this because we are not using the props instance for anything else.
6723
- host.setProp(ELEMENT_PROPS, jsxProps);
6724
- vNodeProps = jsxProps;
6725
- }
6726
- }
6727
6833
  // Assign the new QRL instance to the host.
6728
6834
  // Unfortunately it is created every time, something to fix in the optimizer.
6729
6835
  host.setProp(OnRenderProp, componentQRL);
@@ -6860,7 +6966,37 @@ function getComponentHash(vNode, getObject) {
6860
6966
  * ```
6861
6967
  */
6862
6968
  function Projection() { }
6863
- function propsDiffer(src, dst) {
6969
+ function handleProps(host, jsxProps, vNodeProps, container) {
6970
+ let shouldRender = false;
6971
+ let propsAreDifferent = false;
6972
+ if (vNodeProps) {
6973
+ const effects = vNodeProps[_PROPS_HANDLER].$effects$;
6974
+ const constPropsDifferent = handleChangedProps(jsxProps[_CONST_PROPS], vNodeProps[_CONST_PROPS], vNodeProps[_PROPS_HANDLER], container, false);
6975
+ propsAreDifferent = constPropsDifferent;
6976
+ shouldRender ||= constPropsDifferent;
6977
+ if (effects && effects.size > 0) {
6978
+ const varPropsDifferent = handleChangedProps(jsxProps[_VAR_PROPS], vNodeProps[_VAR_PROPS], vNodeProps[_PROPS_HANDLER], container);
6979
+ propsAreDifferent ||= varPropsDifferent;
6980
+ // don't mark as should render, effects will take care of it
6981
+ // shouldRender ||= varPropsDifferent;
6982
+ }
6983
+ }
6984
+ if (propsAreDifferent) {
6985
+ if (vNodeProps) {
6986
+ // Reuse the same props instance, qrls can use the current props instance
6987
+ // as a capture ref, so we can't change it.
6988
+ vNodeProps[_OWNER] = jsxProps[_OWNER];
6989
+ }
6990
+ else if (jsxProps) {
6991
+ // If there is no props instance, create a new one.
6992
+ // We can do this because we are not using the props instance for anything else.
6993
+ host.setProp(ELEMENT_PROPS, jsxProps);
6994
+ vNodeProps = jsxProps;
6995
+ }
6996
+ }
6997
+ return shouldRender;
6998
+ }
6999
+ function handleChangedProps(src, dst, propsHandler, container, triggerEffects = true) {
6864
7000
  const srcEmpty = isPropsEmpty(src);
6865
7001
  const dstEmpty = isPropsEmpty(dst);
6866
7002
  if (srcEmpty && dstEmpty) {
@@ -6888,15 +7024,23 @@ function propsDiffer(src, dst) {
6888
7024
  if (srcLen !== dstLen) {
6889
7025
  return true;
6890
7026
  }
7027
+ let changed = false;
7028
+ propsHandler.$container$ = container;
6891
7029
  for (const key of srcKeys) {
6892
7030
  if (key === 'children' || key === QBackRefs) {
6893
7031
  continue;
6894
7032
  }
6895
7033
  if (!Object.prototype.hasOwnProperty.call(dst, key) || src[key] !== dst[key]) {
6896
- return true;
7034
+ changed = true;
7035
+ if (triggerEffects) {
7036
+ triggerPropsProxyEffect(propsHandler, key);
7037
+ }
7038
+ else {
7039
+ return true;
7040
+ }
6897
7041
  }
6898
7042
  }
6899
- return false;
7043
+ return changed;
6900
7044
  }
6901
7045
  function isPropsEmpty(props) {
6902
7046
  if (!props) {
@@ -7179,7 +7323,8 @@ function findAncestorBlockingChore(chore, type) {
7179
7323
  if (blockingChore.$type$ < 16 /* ChoreType.VISIBLE */ &&
7180
7324
  blockingChore.$type$ !== 3 /* ChoreType.TASK */ &&
7181
7325
  blockingChore.$type$ !== 1 /* ChoreType.QRL_RESOLVE */ &&
7182
- blockingChore.$type$ !== 2 /* ChoreType.RUN_QRL */) {
7326
+ blockingChore.$type$ !== 2 /* ChoreType.RUN_QRL */ &&
7327
+ blockingChore.$state$ === ChoreState.NONE) {
7183
7328
  return blockingChore;
7184
7329
  }
7185
7330
  }
@@ -7204,19 +7349,25 @@ function findBlockingChore(chore, choreQueue, blockedChores, runningChores, cont
7204
7349
  // Check in choreQueue
7205
7350
  // TODO(perf): better to iterate in reverse order?
7206
7351
  for (const candidate of choreQueue) {
7207
- if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
7352
+ if (candidate.$type$ === rule.blockingType &&
7353
+ rule.match(chore, candidate, container) &&
7354
+ candidate.$state$ === ChoreState.NONE) {
7208
7355
  return candidate;
7209
7356
  }
7210
7357
  }
7211
7358
  // Check in blockedChores
7212
7359
  for (const candidate of blockedChores) {
7213
- if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
7360
+ if (candidate.$type$ === rule.blockingType &&
7361
+ rule.match(chore, candidate, container) &&
7362
+ candidate.$state$ === ChoreState.NONE) {
7214
7363
  return candidate;
7215
7364
  }
7216
7365
  }
7217
7366
  // Check in runningChores
7218
7367
  for (const candidate of runningChores) {
7219
- if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
7368
+ if (candidate.$type$ === rule.blockingType &&
7369
+ rule.match(chore, candidate, container) &&
7370
+ candidate.$state$ !== ChoreState.FAILED) {
7220
7371
  return candidate;
7221
7372
  }
7222
7373
  }
@@ -7242,7 +7393,9 @@ function findBlockingChoreForVisible(chore, runningChores, container) {
7242
7393
  continue;
7243
7394
  }
7244
7395
  for (const candidate of runningChores) {
7245
- if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
7396
+ if (candidate.$type$ === rule.blockingType &&
7397
+ rule.match(chore, candidate, container) &&
7398
+ candidate.$state$ !== ChoreState.FAILED) {
7246
7399
  return candidate;
7247
7400
  }
7248
7401
  }
@@ -7483,6 +7636,7 @@ function choreComparator(a, b) {
7483
7636
  if (a.$type$ === 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */ &&
7484
7637
  b.$type$ === 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */ &&
7485
7638
  ((a.$target$ instanceof StoreHandler && b.$target$ instanceof StoreHandler) ||
7639
+ (a.$target$ instanceof PropsProxyHandler && b.$target$ instanceof PropsProxyHandler) ||
7486
7640
  (a.$target$ instanceof AsyncComputedSignalImpl &&
7487
7641
  b.$target$ instanceof AsyncComputedSignalImpl)) &&
7488
7642
  a.$payload$ !== b.$payload$) {
@@ -7685,16 +7839,18 @@ This is often caused by modifying a signal in an already rendered component duri
7685
7839
  }
7686
7840
  const shouldBlock = chore.$type$ !== 1 /* ChoreType.QRL_RESOLVE */ && chore.$type$ !== 2 /* ChoreType.RUN_QRL */;
7687
7841
  if (shouldBlock) {
7842
+ const runningChore = getRunningChore(chore);
7843
+ if (runningChore) {
7844
+ if (isResourceChore(runningChore)) {
7845
+ addBlockedChore(chore, runningChore, blockedChores);
7846
+ }
7847
+ return chore;
7848
+ }
7688
7849
  const blockingChore = findBlockingChore(chore, choreQueue, blockedChores, runningChores, container);
7689
7850
  if (blockingChore) {
7690
7851
  addBlockedChore(chore, blockingChore, blockedChores);
7691
7852
  return chore;
7692
7853
  }
7693
- const runningChore = getRunningChore(chore);
7694
- if (runningChore) {
7695
- addBlockedChore(chore, runningChore, blockedChores);
7696
- return chore;
7697
- }
7698
7854
  }
7699
7855
  addChoreAndIncrementBlockingCounter(chore, choreQueue);
7700
7856
  const runImmediately = (isServer && type === 6 /* ChoreType.COMPONENT */) || type === 2 /* ChoreType.RUN_QRL */;
@@ -7852,7 +8008,7 @@ This is often caused by modifying a signal in an already rendered component duri
7852
8008
  applyJournalFlush();
7853
8009
  const blockingChore = findBlockingChoreForVisible(chore, runningChores, container);
7854
8010
  if (blockingChore && blockingChore.$state$ === ChoreState.RUNNING) {
7855
- addBlockedChore(chore, blockingChore, blockedChores);
8011
+ (blockingChore.$blockedChores$ ||= new ChoreArray()).add(chore);
7856
8012
  continue;
7857
8013
  }
7858
8014
  }
@@ -8095,7 +8251,15 @@ function addChore(chore, choreArray) {
8095
8251
  function vNodeAlreadyDeleted(chore) {
8096
8252
  return !!(chore.$host$ && vnode_isVNode(chore.$host$) && chore.$host$.flags & 32 /* VNodeFlags.Deleted */);
8097
8253
  }
8254
+ function isResourceChore(chore) {
8255
+ return (chore.$type$ === 3 /* ChoreType.TASK */ &&
8256
+ !!chore.$payload$ &&
8257
+ !!(chore.$payload$.$flags$ & 4 /* TaskFlags.RESOURCE */));
8258
+ }
8098
8259
  function addBlockedChore(blockedChore, blockingChore, blockedChores) {
8260
+ if (!isResourceChore(blockedChore) && choreComparator(blockedChore, blockingChore) === 0) {
8261
+ return;
8262
+ }
8099
8263
  (blockingChore.$blockedChores$ ||= new ChoreArray()).add(blockedChore);
8100
8264
  blockedChores.add(blockedChore);
8101
8265
  if (vnode_isVNode(blockedChore.$host$)) {
@@ -8194,9 +8358,9 @@ function debugTrace(action, arg, queue, blockedChores) {
8194
8358
  }
8195
8359
  }
8196
8360
  // Blocked chores section
8197
- if (blockedChores && blockedChores.size > 0) {
8361
+ if (blockedChores && blockedChores.length > 0) {
8198
8362
  lines.push('');
8199
- lines.push(`🚫 Blocked Chores (${blockedChores.size} items):`);
8363
+ lines.push(`🚫 Blocked Chores (${blockedChores.length} items):`);
8200
8364
  Array.from(blockedChores).forEach((chore, index) => {
8201
8365
  const type = debugChoreTypeToString(chore.$type$);
8202
8366
  const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
@@ -8272,16 +8436,16 @@ async function serialize(serializationContext) {
8272
8436
  let parent;
8273
8437
  const qrlMap = new Map();
8274
8438
  /** Helper to output an array */
8275
- const outputArray = (value, keepNulls, writeFn) => {
8439
+ const outputArray = (value, keepUndefined, writeFn) => {
8276
8440
  $writer$.write('[');
8277
8441
  let separator = false;
8278
8442
  let length;
8279
- if (keepNulls) {
8443
+ if (keepUndefined) {
8280
8444
  length = value.length;
8281
8445
  }
8282
8446
  else {
8283
8447
  length = value.length - 1;
8284
- while (length >= 0 && value[length] === null) {
8448
+ while (length >= 0 && value[length] === undefined) {
8285
8449
  length--;
8286
8450
  }
8287
8451
  length++;
@@ -8298,7 +8462,7 @@ async function serialize(serializationContext) {
8298
8462
  $writer$.write(']');
8299
8463
  };
8300
8464
  /** Output a type,value pair. If the value is an array, it calls writeValue on each item. */
8301
- const output = (type, value, keepNulls) => {
8465
+ const output = (type, value, keepUndefined) => {
8302
8466
  $writer$.write(`${type},`);
8303
8467
  if (typeof value === 'number') {
8304
8468
  $writer$.write(value.toString());
@@ -8315,7 +8479,7 @@ async function serialize(serializationContext) {
8315
8479
  $writer$.write(lastIdx === 0 ? s : s.slice(lastIdx));
8316
8480
  }
8317
8481
  else {
8318
- outputArray(value, keepNulls, (valueItem, idx) => {
8482
+ outputArray(value, !!keepUndefined, (valueItem, idx) => {
8319
8483
  writeValue(valueItem, idx);
8320
8484
  });
8321
8485
  }
@@ -8506,7 +8670,12 @@ async function serialize(serializationContext) {
8506
8670
  const writeObjectValue = (value) => {
8507
8671
  if (isPropsProxy(value)) {
8508
8672
  const owner = value[_OWNER];
8509
- output(32 /* TypeIds.PropsProxy */, [_serializationWeakRef(owner), owner.varProps, owner.constProps]);
8673
+ output(32 /* TypeIds.PropsProxy */, [
8674
+ _serializationWeakRef(owner),
8675
+ owner.varProps,
8676
+ owner.constProps,
8677
+ value[_PROPS_HANDLER].$effects$,
8678
+ ]);
8510
8679
  }
8511
8680
  else if (value instanceof SubscriptionData) {
8512
8681
  output(33 /* TypeIds.SubscriptionData */, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
@@ -8536,7 +8705,7 @@ async function serialize(serializationContext) {
8536
8705
  }
8537
8706
  }
8538
8707
  const out = [storeTarget, flags, effects, ...innerStores];
8539
- while (out[out.length - 1] == null) {
8708
+ while (out[out.length - 1] === undefined) {
8540
8709
  out.pop();
8541
8710
  }
8542
8711
  output(29 /* TypeIds.Store */, out);
@@ -8546,7 +8715,7 @@ async function serialize(serializationContext) {
8546
8715
  const result = value[SerializerSymbol](value);
8547
8716
  if (isPromise(result)) {
8548
8717
  const forwardRef = resolvePromise(result, $addRoot$, (resolved, resolvedValue) => {
8549
- return new PromiseResult(28 /* TypeIds.SerializerSignal */, resolved, resolvedValue, null, null);
8718
+ return new PromiseResult(28 /* TypeIds.SerializerSignal */, resolved, resolvedValue, undefined, undefined);
8550
8719
  });
8551
8720
  output(2 /* TypeIds.ForwardRef */, forwardRef);
8552
8721
  }
@@ -8581,10 +8750,16 @@ async function serialize(serializationContext) {
8581
8750
  else if (value instanceof SignalImpl) {
8582
8751
  if (value instanceof SerializerSignalImpl) {
8583
8752
  addPreloadQrl(value.$computeQrl$);
8584
- const forwardRefId = resolvePromise(getCustomSerializerPromise(value, value.$untrackedValue$), $addRoot$, (resolved, resolvedValue) => {
8585
- return new PromiseResult(28 /* TypeIds.SerializerSignal */, resolved, resolvedValue, value.$effects$, value.$computeQrl$);
8586
- });
8587
- output(2 /* TypeIds.ForwardRef */, forwardRefId);
8753
+ const maybeValue = getCustomSerializerPromise(value, value.$untrackedValue$);
8754
+ if (isPromise(maybeValue)) {
8755
+ const forwardRefId = resolvePromise(maybeValue, $addRoot$, (resolved, resolvedValue) => {
8756
+ return new PromiseResult(28 /* TypeIds.SerializerSignal */, resolved, resolvedValue, value.$effects$, value.$computeQrl$);
8757
+ });
8758
+ output(2 /* TypeIds.ForwardRef */, forwardRefId);
8759
+ }
8760
+ else {
8761
+ output(28 /* TypeIds.SerializerSignal */, [value.$computeQrl$, value.$effects$, maybeValue]);
8762
+ }
8588
8763
  return;
8589
8764
  }
8590
8765
  if (value instanceof WrappedSignalImpl) {
@@ -8617,13 +8792,28 @@ async function serialize(serializationContext) {
8617
8792
  if (isAsync) {
8618
8793
  out.push(value.$loadingEffects$, value.$errorEffects$, value.$untrackedLoading$, value.$untrackedError$);
8619
8794
  }
8795
+ let keepUndefined = false;
8620
8796
  if (v !== NEEDS_COMPUTATION) {
8621
8797
  out.push(v);
8798
+ if (!isAsync && v === undefined) {
8799
+ /**
8800
+ * If value is undefined, we need to keep it in the output. If we don't do that, later
8801
+ * during resuming, the value will be set to symbol(invalid) with flag invalid, and
8802
+ * thats is incorrect.
8803
+ */
8804
+ keepUndefined = true;
8805
+ }
8622
8806
  }
8623
- output(isAsync ? 27 /* TypeIds.AsyncComputedSignal */ : 26 /* TypeIds.ComputedSignal */, out);
8807
+ output(isAsync ? 27 /* TypeIds.AsyncComputedSignal */ : 26 /* TypeIds.ComputedSignal */, out, keepUndefined);
8624
8808
  }
8625
8809
  else {
8626
- output(24 /* TypeIds.Signal */, [value.$untrackedValue$, ...(value.$effects$ || [])]);
8810
+ const v = value.$untrackedValue$;
8811
+ const keepUndefined = v === undefined;
8812
+ const out = [v];
8813
+ if (value.$effects$) {
8814
+ out.push(...value.$effects$);
8815
+ }
8816
+ output(24 /* TypeIds.Signal */, out, keepUndefined);
8627
8817
  }
8628
8818
  }
8629
8819
  else if (value instanceof URL) {
@@ -8707,9 +8897,9 @@ async function serialize(serializationContext) {
8707
8897
  value.varProps,
8708
8898
  value.constProps,
8709
8899
  value.children,
8710
- value.toSort || null,
8900
+ value.toSort || undefined,
8711
8901
  ];
8712
- while (out[out.length - 1] == null) {
8902
+ while (out[out.length - 1] === undefined) {
8713
8903
  out.pop();
8714
8904
  }
8715
8905
  output(31 /* TypeIds.JSXNode */, out);
@@ -8723,7 +8913,7 @@ async function serialize(serializationContext) {
8723
8913
  value[_EFFECT_BACK_REF],
8724
8914
  value.$state$,
8725
8915
  ];
8726
- while (out[out.length - 1] == null) {
8916
+ while (out[out.length - 1] === undefined) {
8727
8917
  out.pop();
8728
8918
  }
8729
8919
  output(21 /* TypeIds.Task */, out);
@@ -8850,7 +9040,7 @@ class PromiseResult {
8850
9040
  $value$;
8851
9041
  $effects$;
8852
9042
  $qrl$;
8853
- constructor($type$, $resolved$, $value$, $effects$ = null, $qrl$ = null) {
9043
+ constructor($type$, $resolved$, $value$, $effects$ = undefined, $qrl$ = undefined) {
8854
9044
  this.$type$ = $type$;
8855
9045
  this.$resolved$ = $resolved$;
8856
9046
  this.$value$ = $value$;
@@ -8859,20 +9049,24 @@ class PromiseResult {
8859
9049
  }
8860
9050
  }
8861
9051
  function getCustomSerializerPromise(signal, value) {
8862
- return new Promise((resolve) => {
8863
- signal.$computeQrl$.resolve().then((arg) => {
8864
- let data;
8865
- if (arg.serialize) {
8866
- data = arg.serialize(value);
8867
- }
8868
- else if (SerializerSymbol in value) {
8869
- data = value[SerializerSymbol](value);
8870
- }
8871
- if (data === undefined) {
8872
- data = NEEDS_COMPUTATION;
8873
- }
8874
- resolve(data);
8875
- });
9052
+ if (value === NEEDS_COMPUTATION) {
9053
+ return value;
9054
+ }
9055
+ return maybeThen((signal.$computeQrl$.resolved || signal.$computeQrl$.resolve()), (arg) => {
9056
+ let data;
9057
+ if (typeof arg === 'function') {
9058
+ arg = arg();
9059
+ }
9060
+ if (arg.serialize) {
9061
+ data = arg.serialize(value);
9062
+ }
9063
+ else if (typeof value === 'object' && SerializerSymbol in value) {
9064
+ data = value[SerializerSymbol](value);
9065
+ }
9066
+ if (data === undefined) {
9067
+ data = NEEDS_COMPUTATION;
9068
+ }
9069
+ return data;
8876
9070
  });
8877
9071
  }
8878
9072
  const discoverValuesForVNodeData = (vnodeData, callback) => {
@@ -8923,7 +9117,7 @@ function serializeWrappingFn(serializationContext, value) {
8923
9117
  return [syncFnId, value.$args$];
8924
9118
  }
8925
9119
  function filterEffectBackRefs(effectBackRef) {
8926
- let effectBackRefToSerialize = null;
9120
+ let effectBackRefToSerialize = undefined;
8927
9121
  if (effectBackRef) {
8928
9122
  for (const [effectProp, effect] of effectBackRef) {
8929
9123
  if (effect[2 /* EffectSubscriptionProp.BACK_REF */]) {
@@ -9103,7 +9297,7 @@ class _SharedContainer {
9103
9297
  throw Error('Not implemented');
9104
9298
  };
9105
9299
  const choreQueue = new ChoreArray();
9106
- const blockedChores = new Set();
9300
+ const blockedChores = new ChoreArray();
9107
9301
  const runningChores = new Set();
9108
9302
  this.$scheduler$ = createScheduler(this, journalFlush, choreQueue, blockedChores, runningChores);
9109
9303
  }
@@ -9188,7 +9382,7 @@ async function _walkJSX(ssr, value, options) {
9188
9382
  }
9189
9383
  function processJSXNode(ssr, enqueue, value, options) {
9190
9384
  // console.log('processJSXNode', value);
9191
- if (value === null || value === undefined) {
9385
+ if (value == null) {
9192
9386
  ssr.textNode('');
9193
9387
  }
9194
9388
  else if (typeof value === 'boolean') {
@@ -9245,12 +9439,12 @@ function processJSXNode(ssr, enqueue, value, options) {
9245
9439
  appendQwikInspectorAttribute(jsx, qwikInspectorAttrValue);
9246
9440
  }
9247
9441
  }
9248
- const innerHTML = ssr.openElement(type, varPropsToSsrAttrs(jsx.varProps, jsx.constProps, {
9442
+ const innerHTML = ssr.openElement(type, toSsrAttrs(jsx.varProps, {
9249
9443
  serializationCtx: ssr.serializationCtx,
9250
9444
  styleScopedId: options.styleScoped,
9251
9445
  key: jsx.key,
9252
9446
  toSort: jsx.toSort,
9253
- }), constPropsToSsrAttrs(jsx.constProps, jsx.varProps, {
9447
+ }), toSsrAttrs(jsx.constProps, {
9254
9448
  serializationCtx: ssr.serializationCtx,
9255
9449
  styleScopedId: options.styleScoped,
9256
9450
  }), qwikInspectorAttrValue);
@@ -9347,12 +9541,22 @@ function processJSXNode(ssr, enqueue, value, options) {
9347
9541
  const componentFrame = ssr.getParentComponentFrame();
9348
9542
  componentFrame.distributeChildrenIntoSlots(jsx.children, options.styleScoped, options.parentComponentFrame);
9349
9543
  const jsxOutput = applyQwikComponentBody(ssr, jsx, type);
9350
- const compStyleComponentId = addComponentStylePrefix(host.getProp(QScopedStyle));
9351
9544
  enqueue(new ParentComponentData(options.styleScoped, options.parentComponentFrame));
9352
9545
  enqueue(ssr.closeComponent);
9353
- enqueue(jsxOutput);
9354
- isPromise(jsxOutput) && enqueue(Promise);
9355
- enqueue(new ParentComponentData(compStyleComponentId, componentFrame));
9546
+ if (isPromise(jsxOutput)) {
9547
+ // Defer reading QScopedStyle until after the promise resolves
9548
+ enqueue(async () => {
9549
+ const resolvedOutput = await jsxOutput;
9550
+ const compStyleComponentId = addComponentStylePrefix(host.getProp(QScopedStyle));
9551
+ enqueue(resolvedOutput);
9552
+ enqueue(new ParentComponentData(compStyleComponentId, componentFrame));
9553
+ });
9554
+ }
9555
+ else {
9556
+ enqueue(jsxOutput);
9557
+ const compStyleComponentId = addComponentStylePrefix(host.getProp(QScopedStyle));
9558
+ enqueue(new ParentComponentData(compStyleComponentId, componentFrame));
9559
+ }
9356
9560
  }
9357
9561
  else {
9358
9562
  const inlineComponentProps = [ELEMENT_KEY, jsx.key];
@@ -9369,12 +9573,6 @@ function processJSXNode(ssr, enqueue, value, options) {
9369
9573
  }
9370
9574
  }
9371
9575
  }
9372
- function varPropsToSsrAttrs(varProps, constProps, options) {
9373
- return toSsrAttrs(varProps, options);
9374
- }
9375
- function constPropsToSsrAttrs(constProps, varProps, options) {
9376
- return toSsrAttrs(constProps, options);
9377
- }
9378
9576
  function toSsrAttrs(record, options) {
9379
9577
  if (record == null) {
9380
9578
  return null;
@@ -9468,14 +9666,16 @@ function addQwikEventToSerializationContext(serializationCtx, key, qrl) {
9468
9666
  // TODO extract window/document too so qwikloader can precisely listen
9469
9667
  const data = getEventDataFromHtmlAttribute(key);
9470
9668
  if (data) {
9471
- const eventName = data[1];
9472
- serializationCtx.$eventNames$.add(eventName);
9669
+ const [scope, eventName] = data;
9670
+ const scopedEvent = getScopedEventName(scope, eventName);
9671
+ const loaderScopedEvent = getLoaderScopedEventName(scope, scopedEvent);
9672
+ serializationCtx.$eventNames$.add(loaderScopedEvent);
9473
9673
  serializationCtx.$eventQrls$.add(qrl);
9474
9674
  }
9475
9675
  }
9476
9676
  function addPreventDefaultEventToSerializationContext(serializationCtx, key) {
9477
- // skip first 15 chars, this is length of the `preventdefault:`
9478
- const eventName = key.substring(15);
9677
+ // skip first 15 chars, this is length of the `preventdefault`, leave the ":"
9678
+ const eventName = key.substring(14);
9479
9679
  if (eventName) {
9480
9680
  serializationCtx.$eventNames$.add(eventName);
9481
9681
  }
@@ -9613,7 +9813,7 @@ const inflate = (container, target, typeId, data) => {
9613
9813
  asyncComputed.$loadingEffects$ = new Set(d[2]);
9614
9814
  asyncComputed.$errorEffects$ = new Set(d[3]);
9615
9815
  asyncComputed.$untrackedLoading$ = d[4];
9616
- asyncComputed.$untrackedError$ = d[5] || null;
9816
+ asyncComputed.$untrackedError$ = d[5];
9617
9817
  const hasValue = d.length > 6;
9618
9818
  if (hasValue) {
9619
9819
  asyncComputed.$untrackedValue$ = d[6];
@@ -9628,7 +9828,9 @@ const inflate = (container, target, typeId, data) => {
9628
9828
  const computed = target;
9629
9829
  const d = data;
9630
9830
  computed.$computeQrl$ = d[0];
9631
- computed.$effects$ = new Set(d[1]);
9831
+ if (d[1]) {
9832
+ computed.$effects$ = new Set(d[1]);
9833
+ }
9632
9834
  const hasValue = d.length > 2;
9633
9835
  if (hasValue) {
9634
9836
  computed.$untrackedValue$ = d[2];
@@ -9723,6 +9925,7 @@ const inflate = (container, target, typeId, data) => {
9723
9925
  owner._proxy = propsProxy;
9724
9926
  }
9725
9927
  propsProxy[_OWNER] = owner;
9928
+ propsProxy[_PROPS_HANDLER].$effects$ = d[3];
9726
9929
  break;
9727
9930
  case 33 /* TypeIds.SubscriptionData */: {
9728
9931
  const effectData = target;
@@ -10272,9 +10475,6 @@ class DomContainer extends _SharedContainer {
10272
10475
  this.$setServerData$();
10273
10476
  element.setAttribute(QContainerAttr, "resumed" /* QContainerValue.RESUMED */);
10274
10477
  element.qContainer = this;
10275
- if (!qTest && element.isConnected) {
10276
- element.dispatchEvent(new CustomEvent('qresume', { bubbles: true }));
10277
- }
10278
10478
  const qwikStates = element.querySelectorAll('script[type="qwik/state"]');
10279
10479
  if (qwikStates.length !== 0) {
10280
10480
  const lastState = qwikStates[qwikStates.length - 1];
@@ -10282,6 +10482,9 @@ class DomContainer extends _SharedContainer {
10282
10482
  preprocessState(this.$rawStateData$, this);
10283
10483
  this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
10284
10484
  }
10485
+ if (!qTest && element.isConnected) {
10486
+ element.dispatchEvent(new CustomEvent('qresume', { bubbles: true }));
10487
+ }
10285
10488
  }
10286
10489
  $setRawState$(id, vParent) {
10287
10490
  this.$stateData$[id] = vParent;
@@ -10717,7 +10920,7 @@ const getOrCreateStore = (obj, flags, container) => {
10717
10920
  class StoreHandler {
10718
10921
  $flags$;
10719
10922
  $container$;
10720
- $effects$ = null;
10923
+ $effects$ = undefined;
10721
10924
  constructor($flags$, $container$) {
10722
10925
  this.$flags$ = $flags$;
10723
10926
  this.$container$ = $container$;
@@ -10727,7 +10930,7 @@ class StoreHandler {
10727
10930
  }
10728
10931
  force(prop) {
10729
10932
  const target = getStoreTarget(this);
10730
- this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, getEffects(target, prop, this.$effects$));
10933
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, undefined, this, getEffects(target, prop, this.$effects$));
10731
10934
  }
10732
10935
  get(target, prop) {
10733
10936
  // TODO(perf): handle better `slice` calls
@@ -10796,7 +10999,7 @@ class StoreHandler {
10796
10999
  if (!Array.isArray(target)) {
10797
11000
  // If the target is an array, we don't need to trigger effects.
10798
11001
  // Changing the length property will trigger effects.
10799
- this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, getEffects(target, prop, this.$effects$));
11002
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, undefined, this, getEffects(target, prop, this.$effects$));
10800
11003
  }
10801
11004
  return true;
10802
11005
  }
@@ -10852,13 +11055,14 @@ function addStoreEffect(target, prop, store, effectSubscription) {
10852
11055
  // to unsubscribe from. So we need to store the reference from the effect back
10853
11056
  // to this signal.
10854
11057
  ensureContainsBackRef(effectSubscription, target);
11058
+ // TODO is this needed with the preloader?
10855
11059
  addQrlToSerializationCtx(effectSubscription, store.$container$);
10856
11060
  }
10857
11061
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
10858
11062
  target[prop] = value;
10859
11063
  const effects = getEffects(target, prop, currentStore.$effects$);
10860
11064
  if (effects) {
10861
- currentStore.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, currentStore, effects);
11065
+ currentStore.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, undefined, currentStore, effects);
10862
11066
  }
10863
11067
  }
10864
11068
  function getEffects(target, prop, storeEffects) {
@@ -10883,7 +11087,7 @@ function getEffects(target, prop, storeEffects) {
10883
11087
  effectsToTrigger.add(effect);
10884
11088
  }
10885
11089
  }
10886
- return effectsToTrigger || null;
11090
+ return effectsToTrigger;
10887
11091
  }
10888
11092
 
10889
11093
  const canSerialize = (value, seen = new WeakSet()) => {
@@ -12673,21 +12877,22 @@ const useVisibleTaskQrl = (qrl, opts) => {
12673
12877
  const { val, set, i, iCtx } = useSequentialScope();
12674
12878
  const eagerness = opts?.strategy ?? 'intersection-observer';
12675
12879
  if (val) {
12676
- if (isServerPlatform()) {
12677
- useRunTask(val, eagerness);
12880
+ if (!(val.$flags$ & 32 /* TaskFlags.EVENTS_REGISTERED */) && !isServerPlatform()) {
12881
+ val.$flags$ |= 32 /* TaskFlags.EVENTS_REGISTERED */;
12882
+ useRegisterTaskEvents(val, eagerness);
12678
12883
  }
12679
12884
  return;
12680
12885
  }
12681
12886
  assertQrl(qrl);
12682
12887
  const task = new Task(1 /* TaskFlags.VISIBLE_TASK */, i, iCtx.$hostElement$, qrl, undefined, null);
12683
12888
  set(task);
12684
- useRunTask(task, eagerness);
12889
+ useRegisterTaskEvents(task, eagerness);
12685
12890
  if (!isServerPlatform()) {
12686
12891
  qrl.resolve(iCtx.$element$);
12687
12892
  iCtx.$container$.$scheduler$(16 /* ChoreType.VISIBLE */, task);
12688
12893
  }
12689
12894
  };
12690
- const useRunTask = (task, eagerness) => {
12895
+ const useRegisterTaskEvents = (task, eagerness) => {
12691
12896
  if (eagerness === 'intersection-observer') {
12692
12897
  useOn('qvisible', getTaskHandlerQrl(task));
12693
12898
  }