@qwik.dev/core 2.0.0-beta.7 → 2.0.0-beta.9

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 (46) hide show
  1. package/bindings/qwik.darwin-arm64.node +0 -0
  2. package/bindings/qwik.darwin-x64.node +0 -0
  3. package/bindings/qwik.linux-x64-gnu.node +0 -0
  4. package/bindings/qwik.win32-x64-msvc.node +0 -0
  5. package/bindings/qwik_wasm_bg.wasm +0 -0
  6. package/dist/backpatch/index.cjs +6 -0
  7. package/dist/backpatch/index.d.ts +2 -0
  8. package/dist/backpatch/index.mjs +5 -0
  9. package/dist/backpatch/package.json +8 -0
  10. package/dist/backpatch-executor.debug.js +34 -0
  11. package/dist/backpatch-executor.js +1 -0
  12. package/dist/build/package.json +1 -1
  13. package/dist/cli.cjs +17 -17
  14. package/dist/core-internal.d.ts +112 -56
  15. package/dist/core.cjs +1022 -469
  16. package/dist/core.cjs.map +1 -1
  17. package/dist/core.min.mjs +1 -1
  18. package/dist/core.mjs +1018 -469
  19. package/dist/core.mjs.map +1 -1
  20. package/dist/core.prod.cjs +654 -361
  21. package/dist/core.prod.mjs +729 -373
  22. package/dist/loader/index.cjs +2 -2
  23. package/dist/loader/index.mjs +2 -2
  24. package/dist/loader/package.json +1 -1
  25. package/dist/optimizer.cjs +78 -76
  26. package/dist/optimizer.mjs +78 -78
  27. package/dist/qwikloader.debug.js +0 -13
  28. package/dist/qwikloader.js +1 -1
  29. package/dist/server.cjs +217 -63
  30. package/dist/server.d.ts +9 -0
  31. package/dist/server.mjs +213 -63
  32. package/dist/starters/features/auth/package.json +1 -1
  33. package/dist/starters/features/localize/package.json +3 -3
  34. package/dist/starters/features/pandacss/package.json +1 -1
  35. package/dist/starters/features/playwright/playwright-report/index.html +943 -903
  36. package/dist/starters/features/postcss/postcss.config.js +1 -1
  37. package/dist/starters/features/tailwind/package.json +2 -2
  38. package/dist/starters/features/tailwind/prettier.config.js +10 -0
  39. package/dist/starters/features/tailwind-v3/package.json +1 -1
  40. package/dist/starters/features/tailwind-v3/prettier.config.js +10 -0
  41. package/dist/testing/index.cjs +3826 -952
  42. package/dist/testing/index.d.ts +972 -1
  43. package/dist/testing/index.mjs +3811 -946
  44. package/dist/testing/package.json +1 -1
  45. package/package.json +8 -6
  46. package/dist/starters/features/tailwind/.prettierrc.js +0 -3
package/dist/core.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * @qwik.dev/core 2.0.0-beta.7-dev+2dd89a6
3
+ * @qwik.dev/core 2.0.0-beta.9-dev+6b582c7
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
@@ -11,6 +11,13 @@
11
11
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.qwikCore = {}, global.qwikBuild, global.qwikPreloader));
12
12
  })(this, (function (exports, build, preloader) { 'use strict';
13
13
 
14
+ /**
15
+ * QWIK_VERSION
16
+ *
17
+ * @public
18
+ */
19
+ const version = "2.0.0-beta.9-dev+6b582c7";
20
+
14
21
  // same as isDev but separate so we can test
15
22
  const qDev = globalThis.qDev !== false;
16
23
  const qInspector = globalThis.qInspector === true;
@@ -88,6 +95,9 @@
88
95
  const isString = (v) => {
89
96
  return typeof v === 'string';
90
97
  };
98
+ const isNumber$1 = (v) => {
99
+ return typeof v === 'number';
100
+ };
91
101
  const isFunction = (v) => {
92
102
  return typeof v === 'function';
93
103
  };
@@ -290,13 +300,6 @@
290
300
  });
291
301
  });
292
302
  },
293
- nextTick: (fn) => {
294
- return new Promise((resolve) => {
295
- setTimeout(() => {
296
- resolve(fn());
297
- });
298
- });
299
- },
300
303
  chunkForSymbol(symbolName, chunk) {
301
304
  return [symbolName, chunk ?? '_'];
302
305
  },
@@ -554,6 +557,13 @@
554
557
  this.$container$ = container;
555
558
  this.$untrackedValue$ = value;
556
559
  }
560
+ /**
561
+ * Use this to force running subscribers, for example when the calculated value has mutated but
562
+ * remained the same object
563
+ */
564
+ force() {
565
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
566
+ }
557
567
  get untrackedValue() {
558
568
  return this.$untrackedValue$;
559
569
  }
@@ -567,14 +577,7 @@
567
577
  set value(value) {
568
578
  if (value !== this.$untrackedValue$) {
569
579
  this.$untrackedValue$ = value;
570
- // TODO: move this to the scheduler
571
- triggerEffects(this.$container$, this, this.$effects$);
572
- // this.$container$?.$scheduler$(
573
- // ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS,
574
- // null,
575
- // this,
576
- // this.$effects$
577
- // );
580
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
578
581
  }
579
582
  }
580
583
  // prevent accidental use as value
@@ -636,12 +639,11 @@
636
639
  $funcStr$;
637
640
  $flags$;
638
641
  $hostElement$ = null;
639
- $forceRunEffects$ = false;
640
642
  [_EFFECT_BACK_REF] = null;
641
643
  constructor(container, fn, args, fnStr,
642
644
  // We need a separate flag to know when the computation needs running because
643
645
  // we need the old value to know if effects need running after computation
644
- flags = 1 /* SignalFlags.INVALID */ | 2 /* WrappedSignalFlags.UNWRAP */) {
646
+ flags = 1 /* SignalFlags.INVALID */ | 4 /* WrappedSignalFlags.UNWRAP */) {
645
647
  super(container, NEEDS_COMPUTATION);
646
648
  this.$args$ = args;
647
649
  this.$func$ = fn;
@@ -650,7 +652,6 @@
650
652
  }
651
653
  invalidate() {
652
654
  this.$flags$ |= 1 /* SignalFlags.INVALID */;
653
- this.$forceRunEffects$ = false;
654
655
  this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, this.$hostElement$, this, this.$effects$);
655
656
  }
656
657
  /**
@@ -658,29 +659,26 @@
658
659
  * remained the same object.
659
660
  */
660
661
  force() {
661
- this.$forceRunEffects$ = true;
662
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
662
663
  this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, this.$hostElement$, this, this.$effects$);
663
664
  }
664
665
  get untrackedValue() {
665
- const didChange = this.$computeIfNeeded$();
666
- if (didChange) {
667
- this.$forceRunEffects$ = didChange;
668
- }
666
+ this.$computeIfNeeded$();
669
667
  assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
670
668
  return this.$untrackedValue$;
671
669
  }
672
670
  $computeIfNeeded$() {
673
671
  if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
674
- return false;
672
+ return;
675
673
  }
676
674
  const untrackedValue = trackSignal(() => this.$func$(...this.$args$), this, "." /* EffectProperty.VNODE */, this.$container$);
677
- // TODO: we should remove invalid flag here
675
+ // TODO: we should remove invalid flag here, but some tests are failing
678
676
  // this.$flags$ &= ~SignalFlags.INVALID;
679
677
  const didChange = untrackedValue !== this.$untrackedValue$;
680
678
  if (didChange) {
679
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
681
680
  this.$untrackedValue$ = untrackedValue;
682
681
  }
683
- return didChange;
684
682
  }
685
683
  // Make this signal read-only
686
684
  set value(_) {
@@ -875,12 +873,26 @@
875
873
  };
876
874
  /** @internal */
877
875
  const _waitUntilRendered = (elm) => {
878
- const containerEl = _getQContainerElement(elm);
879
- if (!containerEl) {
876
+ const container = _getQContainerElement(elm)?.qContainer;
877
+ if (!container) {
880
878
  return Promise.resolve();
881
879
  }
882
- const container = containerEl.qContainer;
883
- return container?.renderDone ?? Promise.resolve();
880
+ // Multi-cycle idle: loop WAIT_FOR_QUEUE until the flush epoch stays stable
881
+ // across an extra microtask, which signals that no new work re-scheduled.
882
+ return (async () => {
883
+ for (;;) {
884
+ await container.$scheduler$(255 /* ChoreType.WAIT_FOR_QUEUE */).$returnValue$;
885
+ const firstEpoch = container.$flushEpoch$ || 0;
886
+ // Give a microtask for any immediate follow-up scheduling to enqueue
887
+ await Promise.resolve();
888
+ const secondEpoch = container.$flushEpoch$ || 0;
889
+ // If no epoch change occurred during and after WAIT_FOR_QUEUE, we are idle.
890
+ if (firstEpoch === secondEpoch) {
891
+ return;
892
+ }
893
+ // Continue loop if epoch advanced, meaning more work flushed.
894
+ }
895
+ })();
884
896
  };
885
897
 
886
898
  /**
@@ -958,7 +970,7 @@
958
970
  }
959
971
  return null; // Return null if not matching expected format
960
972
  }
961
- function eventNameToJsxEvent(eventName, prefix, startIdx = 0) {
973
+ function eventNameToJsxEvent(eventName, prefix) {
962
974
  eventName = eventName.charAt(0).toUpperCase() + eventName.substring(1);
963
975
  return prefix + eventName + EVENT_SUFFIX;
964
976
  }
@@ -1247,13 +1259,6 @@
1247
1259
  return true;
1248
1260
  };
1249
1261
 
1250
- /**
1251
- * QWIK_VERSION
1252
- *
1253
- * @public
1254
- */
1255
- const version = "2.0.0-beta.7-dev+2dd89a6";
1256
-
1257
1262
  /** @internal */
1258
1263
  const EMPTY_ARRAY = [];
1259
1264
  const EMPTY_OBJ = {};
@@ -1394,7 +1399,7 @@
1394
1399
  *
1395
1400
  * @internal
1396
1401
  */
1397
- const queueQRL = (...args) => {
1402
+ const _run = (...args) => {
1398
1403
  // This will already check container
1399
1404
  const [runQrl] = useLexicalScope();
1400
1405
  const context = getInvokeContext();
@@ -1408,7 +1413,9 @@
1408
1413
  if (!scheduler) {
1409
1414
  throw qError(1 /* QError.schedulerNotFound */);
1410
1415
  }
1411
- return scheduler(2 /* ChoreType.RUN_QRL */, hostElement, runQrl, args);
1416
+ // We don't return anything, the scheduler is in charge now
1417
+ const chore = scheduler(2 /* ChoreType.RUN_QRL */, hostElement, runQrl, args);
1418
+ return getChorePromise(chore);
1412
1419
  };
1413
1420
 
1414
1421
  /** @internal */
@@ -1432,17 +1439,17 @@
1432
1439
  return (bottom << 1) ^ -1;
1433
1440
  };
1434
1441
  /** @internal */
1435
- const mapArray_set = (array, key, value, start) => {
1442
+ const mapArray_set = (array, key, value, start, allowNullValue = false) => {
1436
1443
  const indx = mapApp_findIndx(array, key, start);
1437
1444
  if (indx >= 0) {
1438
- if (value == null) {
1445
+ if (value == null && !allowNullValue) {
1439
1446
  array.splice(indx, 2);
1440
1447
  }
1441
1448
  else {
1442
1449
  array[indx + 1] = value;
1443
1450
  }
1444
1451
  }
1445
- else if (value != null) {
1452
+ else if (value != null || allowNullValue) {
1446
1453
  array.splice(indx ^ -1, 0, key, value);
1447
1454
  }
1448
1455
  };
@@ -1456,6 +1463,9 @@
1456
1463
  return null;
1457
1464
  }
1458
1465
  };
1466
+ const mapArray_has = (array, key, start) => {
1467
+ return mapApp_findIndx(array, key, start) >= 0;
1468
+ };
1459
1469
 
1460
1470
  /** @internal */
1461
1471
  const _CONST_PROPS = Symbol('CONST');
@@ -1529,13 +1539,12 @@
1529
1539
  */
1530
1540
  $computeQrl$;
1531
1541
  $flags$;
1532
- $forceRunEffects$ = false;
1533
1542
  [_EFFECT_BACK_REF] = null;
1534
1543
  constructor(container, fn,
1535
1544
  // We need a separate flag to know when the computation needs running because
1536
1545
  // we need the old value to know if effects need running after computation
1537
1546
  flags = 1 /* SignalFlags.INVALID */ |
1538
- 16 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */) {
1547
+ 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */) {
1539
1548
  // The value is used for comparison when signals trigger, which can only happen
1540
1549
  // when it was calculated before. Therefore we can pass whatever we like.
1541
1550
  super(container, NEEDS_COMPUTATION);
@@ -1544,7 +1553,6 @@
1544
1553
  }
1545
1554
  invalidate() {
1546
1555
  this.$flags$ |= 1 /* SignalFlags.INVALID */;
1547
- this.$forceRunEffects$ = false;
1548
1556
  this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
1549
1557
  }
1550
1558
  /**
@@ -1552,20 +1560,17 @@
1552
1560
  * remained the same object
1553
1561
  */
1554
1562
  force() {
1555
- this.$forceRunEffects$ = true;
1556
- this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
1563
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
1564
+ super.force();
1557
1565
  }
1558
1566
  get untrackedValue() {
1559
- const didChange = this.$computeIfNeeded$();
1560
- if (didChange) {
1561
- this.$forceRunEffects$ = didChange;
1562
- }
1567
+ this.$computeIfNeeded$();
1563
1568
  assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
1564
1569
  return this.$untrackedValue$;
1565
1570
  }
1566
1571
  $computeIfNeeded$() {
1567
1572
  if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
1568
- return false;
1573
+ return;
1569
1574
  }
1570
1575
  const computeQrl = this.$computeQrl$;
1571
1576
  throwIfQRLNotResolved(computeQrl);
@@ -1584,9 +1589,12 @@
1584
1589
  this.$flags$ &= ~1 /* SignalFlags.INVALID */;
1585
1590
  const didChange = untrackedValue !== this.$untrackedValue$;
1586
1591
  if (didChange) {
1592
+ // skip first computation when value is not changed
1593
+ if (this.$untrackedValue$ !== NEEDS_COMPUTATION) {
1594
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
1595
+ }
1587
1596
  this.$untrackedValue$ = untrackedValue;
1588
1597
  }
1589
- return didChange;
1590
1598
  }
1591
1599
  finally {
1592
1600
  if (ctx) {
@@ -1612,12 +1620,12 @@
1612
1620
  */
1613
1621
  class SerializerSignalImpl extends ComputedSignalImpl {
1614
1622
  constructor(container, argQrl) {
1615
- super(container, argQrl, 1 /* SignalFlags.INVALID */ | 16 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
1623
+ super(container, argQrl, 1 /* SignalFlags.INVALID */ | 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
1616
1624
  }
1617
1625
  $didInitialize$ = false;
1618
1626
  $computeIfNeeded$() {
1619
1627
  if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
1620
- return false;
1628
+ return;
1621
1629
  }
1622
1630
  throwIfQRLNotResolved(this.$computeQrl$);
1623
1631
  let arg = this.$computeQrl$.resolved;
@@ -1635,9 +1643,9 @@
1635
1643
  this.$flags$ &= -2 /* SignalFlags.INVALID */;
1636
1644
  this.$didInitialize$ = true;
1637
1645
  if (didChange) {
1646
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
1638
1647
  this.$untrackedValue$ = untrackedValue;
1639
1648
  }
1640
- return didChange;
1641
1649
  }
1642
1650
  }
1643
1651
 
@@ -1647,6 +1655,28 @@
1647
1655
  const getStoreTarget = (value) => {
1648
1656
  return value?.[STORE_TARGET] || null;
1649
1657
  };
1658
+ /**
1659
+ * Force a store to recompute and schedule effects.
1660
+ *
1661
+ * @public
1662
+ */
1663
+ const forceStoreEffects = (value, prop) => {
1664
+ const handler = getStoreHandler(value);
1665
+ if (handler) {
1666
+ handler.force(prop);
1667
+ }
1668
+ };
1669
+ /**
1670
+ * @returns True if the store has effects for the given prop
1671
+ * @internal
1672
+ */
1673
+ const _hasStoreEffects = (value, prop) => {
1674
+ const handler = getStoreHandler(value);
1675
+ if (handler) {
1676
+ return (handler.$effects$?.get(prop)?.size ?? 0) > 0;
1677
+ }
1678
+ return false;
1679
+ };
1650
1680
  /**
1651
1681
  * Get the original object that was wrapped by the store. Useful if you want to clone a store
1652
1682
  * (structuredClone, IndexedDB,...)
@@ -1685,7 +1715,12 @@
1685
1715
  toString() {
1686
1716
  return '[Store]';
1687
1717
  }
1718
+ force(prop) {
1719
+ const target = getStoreTarget(this);
1720
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, getEffects(target, prop, this.$effects$));
1721
+ }
1688
1722
  get(target, prop) {
1723
+ // TODO(perf): handle better `slice` calls
1689
1724
  if (typeof prop === 'symbol') {
1690
1725
  if (prop === STORE_TARGET) {
1691
1726
  return target;
@@ -1748,7 +1783,11 @@
1748
1783
  if (typeof prop != 'string' || !delete target[prop]) {
1749
1784
  return false;
1750
1785
  }
1751
- triggerEffects(this.$container$, this, getEffects(target, prop, this.$effects$));
1786
+ if (!Array.isArray(target)) {
1787
+ // If the target is an array, we don't need to trigger effects.
1788
+ // Changing the length property will trigger effects.
1789
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, getEffects(target, prop, this.$effects$));
1790
+ }
1752
1791
  return true;
1753
1792
  }
1754
1793
  has(target, prop) {
@@ -1807,8 +1846,10 @@
1807
1846
  }
1808
1847
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
1809
1848
  target[prop] = value;
1810
- // TODO: trigger effects through the scheduler
1811
- triggerEffects(currentStore.$container$, currentStore, getEffects(target, prop, currentStore.$effects$));
1849
+ const effects = getEffects(target, prop, currentStore.$effects$);
1850
+ if (effects) {
1851
+ currentStore.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, currentStore, effects);
1852
+ }
1812
1853
  }
1813
1854
  function getEffects(target, prop, storeEffects) {
1814
1855
  let effectsToTrigger;
@@ -1936,7 +1977,7 @@
1936
1977
  }
1937
1978
  $computeIfNeeded$() {
1938
1979
  if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
1939
- return false;
1980
+ return;
1940
1981
  }
1941
1982
  const computeQrl = this.$computeQrl$;
1942
1983
  throwIfQRLNotResolved(computeQrl);
@@ -1966,6 +2007,7 @@
1966
2007
  this.$flags$ &= -2 /* SignalFlags.INVALID */;
1967
2008
  const didChange = untrackedValue !== this.$untrackedValue$;
1968
2009
  if (didChange) {
2010
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
1969
2011
  this.$untrackedValue$ = untrackedValue;
1970
2012
  }
1971
2013
  return didChange;
@@ -2053,7 +2095,7 @@
2053
2095
  if (!(obj instanceof AsyncComputedSignalImpl)) {
2054
2096
  assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
2055
2097
  }
2056
- if (obj instanceof WrappedSignalImpl && obj.flags & 2 /* WrappedSignalFlags.UNWRAP */) {
2098
+ if (obj instanceof WrappedSignalImpl && obj.flags & 4 /* WrappedSignalFlags.UNWRAP */) {
2057
2099
  return obj;
2058
2100
  }
2059
2101
  return getWrapped(args);
@@ -2114,7 +2156,7 @@
2114
2156
  return !prop.startsWith('q:') && !prop.startsWith(NON_SERIALIZABLE_MARKER_PREFIX);
2115
2157
  }
2116
2158
  /** @internal */
2117
- const _restProps = (props, omit, target = {}) => {
2159
+ const _restProps = (props, omit = [], target = {}) => {
2118
2160
  let constPropsTarget = null;
2119
2161
  const constProps = props[_CONST_PROPS];
2120
2162
  if (constProps) {
@@ -2569,12 +2611,12 @@
2569
2611
  }
2570
2612
  if (targetElement) {
2571
2613
  if (targetElement.type === 'script' && key === qVisibleEvent) {
2572
- eventKey = 'document:onQinit$';
2614
+ eventKey = 'document:onQInit$';
2573
2615
  logWarn('You are trying to add an event "' +
2574
2616
  key +
2575
2617
  '" using `useVisibleTask$` hook, ' +
2576
2618
  'but a node to which you can add an event is not found. ' +
2577
- 'Using document:onQinit$ instead.');
2619
+ 'Using document:onQInit$ instead.');
2578
2620
  }
2579
2621
  addUseOnEvent(targetElement, eventKey, useOnEvents[key]);
2580
2622
  }
@@ -2697,13 +2739,13 @@
2697
2739
  if (srcProps && srcProps.children) {
2698
2740
  delete srcProps.children;
2699
2741
  }
2700
- const scheduler = ssr.$scheduler$;
2701
2742
  host.setProp(OnRenderProp, componentQrl);
2702
2743
  host.setProp(ELEMENT_PROPS, srcProps);
2703
2744
  if (jsx.key !== null) {
2704
2745
  host.setProp(ELEMENT_KEY, jsx.key);
2705
2746
  }
2706
- return scheduler(6 /* ChoreType.COMPONENT */, host, componentQrl, srcProps);
2747
+ const componentChore = ssr.$scheduler$(6 /* ChoreType.COMPONENT */, host, componentQrl, srcProps);
2748
+ return getChorePromise(componentChore);
2707
2749
  };
2708
2750
 
2709
2751
  class ParentComponentData {
@@ -2811,7 +2853,14 @@
2811
2853
  appendQwikInspectorAttribute(jsx, qwikInspectorAttrValue);
2812
2854
  }
2813
2855
  }
2814
- const innerHTML = ssr.openElement(type, varPropsToSsrAttrs(jsx.varProps, jsx.constProps, ssr.serializationCtx, options.styleScoped, jsx.key), constPropsToSsrAttrs(jsx.constProps, jsx.varProps, ssr.serializationCtx, options.styleScoped), qwikInspectorAttrValue);
2856
+ const innerHTML = ssr.openElement(type, varPropsToSsrAttrs(jsx.varProps, jsx.constProps, {
2857
+ serializationCtx: ssr.serializationCtx,
2858
+ styleScopedId: options.styleScoped,
2859
+ key: jsx.key,
2860
+ }), constPropsToSsrAttrs(jsx.constProps, jsx.varProps, {
2861
+ serializationCtx: ssr.serializationCtx,
2862
+ styleScopedId: options.styleScoped,
2863
+ }), qwikInspectorAttrValue);
2815
2864
  if (innerHTML) {
2816
2865
  ssr.htmlNode(innerHTML);
2817
2866
  }
@@ -2927,16 +2976,17 @@
2927
2976
  }
2928
2977
  }
2929
2978
  }
2930
- function varPropsToSsrAttrs(varProps, constProps, serializationCtx, styleScopedId, key) {
2931
- return toSsrAttrs(varProps, constProps, serializationCtx, true, styleScopedId, key);
2979
+ function varPropsToSsrAttrs(varProps, constProps, options) {
2980
+ return toSsrAttrs(varProps, constProps, false, options);
2932
2981
  }
2933
- function constPropsToSsrAttrs(constProps, varProps, serializationCtx, styleScopedId) {
2934
- return toSsrAttrs(constProps, varProps, serializationCtx, false, styleScopedId);
2982
+ function constPropsToSsrAttrs(constProps, varProps, options) {
2983
+ return toSsrAttrs(constProps, varProps, true, options);
2935
2984
  }
2936
- function toSsrAttrs(record, anotherRecord, serializationCtx, pushMergedEventProps, styleScopedId, key) {
2985
+ function toSsrAttrs(record, anotherRecord, isConst, options) {
2937
2986
  if (record == null) {
2938
2987
  return null;
2939
2988
  }
2989
+ const pushMergedEventProps = !isConst;
2940
2990
  const ssrAttrs = [];
2941
2991
  for (const key in record) {
2942
2992
  let value = record[key];
@@ -2973,7 +3023,7 @@
2973
3023
  }
2974
3024
  }
2975
3025
  }
2976
- const eventValue = setEvent(serializationCtx, key, value);
3026
+ const eventValue = setEvent(options.serializationCtx, key, value);
2977
3027
  if (eventValue) {
2978
3028
  ssrAttrs.push(jsxEventToHtmlAttribute(key), eventValue);
2979
3029
  }
@@ -2983,7 +3033,7 @@
2983
3033
  // write signal as is. We will track this signal inside `writeAttrs`
2984
3034
  if (isClassAttr(key)) {
2985
3035
  // additionally append styleScopedId for class attr
2986
- ssrAttrs.push(key, [value, styleScopedId]);
3036
+ ssrAttrs.push(key, [value, options.styleScopedId]);
2987
3037
  }
2988
3038
  else {
2989
3039
  ssrAttrs.push(key, value);
@@ -2991,13 +3041,13 @@
2991
3041
  continue;
2992
3042
  }
2993
3043
  if (isPreventDefault(key)) {
2994
- addPreventDefaultEventToSerializationContext(serializationCtx, key);
3044
+ addPreventDefaultEventToSerializationContext(options.serializationCtx, key);
2995
3045
  }
2996
- value = serializeAttribute(key, value, styleScopedId);
3046
+ value = serializeAttribute(key, value, options.styleScopedId);
2997
3047
  ssrAttrs.push(key, value);
2998
3048
  }
2999
- if (key != null) {
3000
- ssrAttrs.push(ELEMENT_KEY, key);
3049
+ if (options.key != null) {
3050
+ ssrAttrs.push(ELEMENT_KEY, options.key);
3001
3051
  }
3002
3052
  return ssrAttrs;
3003
3053
  }
@@ -3046,7 +3096,7 @@
3046
3096
  * For internal qrls (starting with `_`) we assume that they do the right thing.
3047
3097
  */
3048
3098
  if (!qrl.$symbol$.startsWith('_') && (qrl.$captureRef$ || qrl.$capture$)) {
3049
- qrl = createQRL(null, '_run', queueQRL, null, null, [qrl]);
3099
+ qrl = createQRL(null, '_run', _run, null, null, [qrl]);
3050
3100
  }
3051
3101
  return qrlToString(serializationCtx, qrl);
3052
3102
  };
@@ -3126,10 +3176,9 @@
3126
3176
  // deleted and we need to be able to release the task subscriptions.
3127
3177
  set(task);
3128
3178
  const container = iCtx.$container$;
3129
- const promise = container.$scheduler$(3 /* ChoreType.TASK */, task);
3130
- if (isPromise(promise)) {
3131
- // TODO: should we handle this differently?
3132
- promise.catch(() => { });
3179
+ const result = runTask(task, container, iCtx.$hostElement$);
3180
+ if (isPromise(result)) {
3181
+ throw result;
3133
3182
  }
3134
3183
  };
3135
3184
  const runTask = (task, container, host) => {
@@ -3141,7 +3190,7 @@
3141
3190
  const track = trackFn(task, container);
3142
3191
  const [cleanup] = cleanupFn(task, (reason) => container.handleError(reason, host));
3143
3192
  const taskApi = { track, cleanup };
3144
- const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
3193
+ return safeCall(() => taskFn(taskApi), cleanup, (err) => {
3145
3194
  // If a Promise is thrown, that means we need to re-run the task.
3146
3195
  if (isPromise(err)) {
3147
3196
  return err.then(() => runTask(task, container, host));
@@ -3150,7 +3199,6 @@
3150
3199
  throw err;
3151
3200
  }
3152
3201
  });
3153
- return result;
3154
3202
  };
3155
3203
  const cleanupTask = (task) => {
3156
3204
  const destroy = task.$destroy$;
@@ -3192,7 +3240,7 @@
3192
3240
  */
3193
3241
  const scheduleTask = (_event, element) => {
3194
3242
  const [task] = useLexicalScope();
3195
- const type = task.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */ ? 32 /* ChoreType.VISIBLE */ : 3 /* ChoreType.TASK */;
3243
+ const type = task.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */ ? 16 /* ChoreType.VISIBLE */ : 3 /* ChoreType.TASK */;
3196
3244
  const container = getDomContainer(element);
3197
3245
  container.$scheduler$(type, task);
3198
3246
  };
@@ -3444,7 +3492,7 @@
3444
3492
  ? this.$constProps$[prop]
3445
3493
  : this.$varProps$[prop];
3446
3494
  // a proxied value that the optimizer made
3447
- return value instanceof WrappedSignalImpl && value.$flags$ & 2 /* WrappedSignalFlags.UNWRAP */
3495
+ return value instanceof WrappedSignalImpl && value.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */
3448
3496
  ? value.value
3449
3497
  : value;
3450
3498
  }
@@ -3520,6 +3568,24 @@
3520
3568
  const directGetPropsProxyProp = (jsx, prop) => {
3521
3569
  return (jsx.constProps && prop in jsx.constProps ? jsx.constProps[prop] : jsx.varProps[prop]);
3522
3570
  };
3571
+ /** @internal */
3572
+ const _getVarProps = (props) => {
3573
+ if (!props) {
3574
+ return null;
3575
+ }
3576
+ return _VAR_PROPS in props
3577
+ ? 'children' in props
3578
+ ? { ...props[_VAR_PROPS], children: props.children }
3579
+ : props[_VAR_PROPS]
3580
+ : props;
3581
+ };
3582
+ /** @internal */
3583
+ const _getConstProps = (props) => {
3584
+ if (!props) {
3585
+ return null;
3586
+ }
3587
+ return _CONST_PROPS in props ? props[_CONST_PROPS] : null;
3588
+ };
3523
3589
 
3524
3590
  const isForeignObjectElement = (elementName) => {
3525
3591
  return build.isDev ? elementName.toLowerCase() === 'foreignobject' : elementName === 'foreignObject';
@@ -3840,6 +3906,10 @@
3840
3906
  vNewNode = null;
3841
3907
  vCurrent = vnode_getFirstChild(vStartNode);
3842
3908
  stackPush(jsxNode, true);
3909
+ if (vParent[0 /* VNodeProps.flags */] & 32 /* VNodeFlags.Deleted */) {
3910
+ // Ignore diff if the parent is deleted.
3911
+ return;
3912
+ }
3843
3913
  while (stack.length) {
3844
3914
  while (jsxIdx < jsxCount) {
3845
3915
  assertFalse(vParent === vCurrent, "Parent and current can't be the same");
@@ -4268,8 +4338,10 @@
4268
4338
  value = trackSignalAndAssignHost(value, vNewNode, key, container, signalData);
4269
4339
  }
4270
4340
  if (key === dangerouslySetInnerHTML) {
4271
- element.innerHTML = value;
4272
- element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
4341
+ if (value) {
4342
+ element.innerHTML = String(value);
4343
+ element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
4344
+ }
4273
4345
  continue;
4274
4346
  }
4275
4347
  if (elementName === 'textarea' && key === 'value') {
@@ -4370,8 +4442,13 @@
4370
4442
  let returnValue = false;
4371
4443
  qrls.flat(2).forEach((qrl) => {
4372
4444
  if (qrl) {
4373
- const value = container.$scheduler$(2 /* ChoreType.RUN_QRL */, vNode, qrl, [event, element]);
4374
- returnValue = returnValue || value === true;
4445
+ if (isSyncQrl(qrl)) {
4446
+ qrl(event, element);
4447
+ }
4448
+ else {
4449
+ const value = container.$scheduler$(2 /* ChoreType.RUN_QRL */, vNode, qrl, [event, element]);
4450
+ returnValue = returnValue || value === true;
4451
+ }
4375
4452
  }
4376
4453
  });
4377
4454
  return returnValue;
@@ -4873,7 +4950,7 @@
4873
4950
  const task = obj;
4874
4951
  clearAllEffects(container, task);
4875
4952
  if (task.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */) {
4876
- container.$scheduler$(48 /* ChoreType.CLEANUP_VISIBLE */, task);
4953
+ container.$scheduler$(32 /* ChoreType.CLEANUP_VISIBLE */, task);
4877
4954
  }
4878
4955
  else {
4879
4956
  cleanupTask(task);
@@ -4924,7 +5001,16 @@
4924
5001
  */
4925
5002
  const vFirstChild = vnode_getFirstChild(vCursor);
4926
5003
  if (vFirstChild) {
4927
- vnode_walkVNode(vFirstChild);
5004
+ vnode_walkVNode(vFirstChild, (vNode) => {
5005
+ /**
5006
+ * Instead of an ID, we store a direct reference to the VNode. This is necessary to
5007
+ * locate the slot's parent in a detached subtree, as the ID would become invalid.
5008
+ */
5009
+ if (vNode[0 /* VNodeProps.flags */] & 2 /* VNodeFlags.Virtual */) {
5010
+ // The QSlotParent is used to find the slot parent during scheduling
5011
+ vnode_getProp(vNode, QSlotParent, (id) => vnode_locate(container.rootVNode, id));
5012
+ }
5013
+ });
4928
5014
  return;
4929
5015
  }
4930
5016
  }
@@ -5001,8 +5087,8 @@
5001
5087
  const resource = createResourceReturn(container, opts);
5002
5088
  const el = iCtx.$hostElement$;
5003
5089
  const task = new Task(8 /* TaskFlags.DIRTY */ | 4 /* TaskFlags.RESOURCE */, i, el, qrl, resource, null);
5004
- container.$scheduler$(3 /* ChoreType.TASK */, task);
5005
5090
  set(resource);
5091
+ runResource(task, container, el);
5006
5092
  return resource;
5007
5093
  };
5008
5094
  // <docs markdown="../readme.md#useResource">
@@ -5070,10 +5156,10 @@
5070
5156
  function getResourceValueAsPromise(props) {
5071
5157
  const resource = props.value;
5072
5158
  if (isResourceReturn(resource)) {
5159
+ // create a subscription for the resource._state changes
5160
+ const state = resource._state;
5073
5161
  const isBrowser = !isServerPlatform();
5074
5162
  if (isBrowser) {
5075
- // create a subscription for the resource._state changes
5076
- const state = resource._state;
5077
5163
  if (state === 'pending' && props.onPending) {
5078
5164
  return Promise.resolve().then(useBindInvokeContext(props.onPending));
5079
5165
  }
@@ -5088,14 +5174,7 @@
5088
5174
  }
5089
5175
  }
5090
5176
  }
5091
- const value = resource.value;
5092
- if (value) {
5093
- return value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
5094
- }
5095
- else {
5096
- // this is temporary value until the `runResource` is executed and promise is assigned to the value
5097
- return Promise.resolve(undefined);
5098
- }
5177
+ return untrack(() => resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
5099
5178
  }
5100
5179
  else if (isPromise(resource)) {
5101
5180
  return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
@@ -5113,7 +5192,7 @@
5113
5192
  const resource = {
5114
5193
  __brand: 'resource',
5115
5194
  value: undefined,
5116
- loading: isServerPlatform() ? false : true,
5195
+ loading: !isServerPlatform(),
5117
5196
  _resolved: undefined,
5118
5197
  _error: undefined,
5119
5198
  _state: 'pending',
@@ -5164,19 +5243,22 @@
5164
5243
  done = true;
5165
5244
  if (resolved) {
5166
5245
  done = true;
5167
- resource.loading = false;
5168
- resource._state = 'resolved';
5169
- resource._resolved = value;
5170
- resource._error = undefined;
5246
+ resourceTarget.loading = false;
5247
+ resourceTarget._state = 'resolved';
5248
+ resourceTarget._resolved = value;
5249
+ resourceTarget._error = undefined;
5171
5250
  resolve(value);
5172
5251
  }
5173
5252
  else {
5174
5253
  done = true;
5175
- resource.loading = false;
5176
- resource._state = 'rejected';
5177
- resource._error = value;
5254
+ resourceTarget.loading = false;
5255
+ resourceTarget._state = 'rejected';
5256
+ resourceTarget._error = value;
5178
5257
  reject(value);
5179
5258
  }
5259
+ if (!isServerPlatform()) {
5260
+ forceStoreEffects(resource, '_state');
5261
+ }
5180
5262
  return true;
5181
5263
  }
5182
5264
  return false;
@@ -5192,17 +5274,17 @@
5192
5274
  }
5193
5275
  });
5194
5276
  // Execute mutation inside empty invocation
5277
+ // TODO: is it right? why we need to invoke inside context and trigger effects?
5195
5278
  invoke(iCtx, () => {
5196
5279
  // console.log('RESOURCE.pending: ');
5197
5280
  resource._state = 'pending';
5198
5281
  resource.loading = !isServerPlatform();
5199
- const promise = (resource.value = new Promise((r, re) => {
5282
+ resource.value = new Promise((r, re) => {
5200
5283
  resolve = r;
5201
5284
  reject = re;
5202
- }));
5203
- promise.catch(ignoreErrorToPreventNodeFromCrashing);
5285
+ });
5204
5286
  });
5205
- const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
5287
+ const promise = safeCall(() => taskFn(opts), (value) => {
5206
5288
  setState(true, value);
5207
5289
  }, (err) => {
5208
5290
  if (isPromise(err)) {
@@ -5225,10 +5307,6 @@
5225
5307
  }
5226
5308
  return promise;
5227
5309
  };
5228
- const ignoreErrorToPreventNodeFromCrashing = (err) => {
5229
- // ignore error to prevent node from crashing
5230
- // node will crash in promise is rejected and no one is listening to the rejection.
5231
- };
5232
5310
 
5233
5311
  /// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
5234
5312
  const aVNodePath = [];
@@ -5311,11 +5389,11 @@
5311
5389
  let bDepth = -1;
5312
5390
  while (a) {
5313
5391
  const ssrNode = (aSsrNodePath[++aDepth] = a);
5314
- a = ssrNode.parentSsrNode;
5392
+ a = ssrNode.parentComponent;
5315
5393
  }
5316
5394
  while (b) {
5317
5395
  const ssrNode = (bSsrNodePath[++bDepth] = b);
5318
- b = ssrNode.parentSsrNode;
5396
+ b = ssrNode.parentComponent;
5319
5397
  }
5320
5398
  while (aDepth >= 0 && bDepth >= 0) {
5321
5399
  a = aSsrNodePath[aDepth];
@@ -5332,6 +5410,210 @@
5332
5410
  return aDepth < bDepth ? -1 : 1;
5333
5411
  };
5334
5412
 
5413
+ /**
5414
+ * Rules for determining if a chore is blocked by another chore. Some chores can block other chores.
5415
+ * They cannot run until the blocking chore has completed.
5416
+ *
5417
+ * The match function is used to determine if the blocked chore is blocked by the blocking chore.
5418
+ * The match function is called with the blocked chore, the blocking chore, and the container.
5419
+ */
5420
+ const VISIBLE_BLOCKING_RULES = [
5421
+ // NODE_DIFF blocks VISIBLE on same host,
5422
+ // if the blocked chore is a child of the blocking chore
5423
+ // or the blocked chore is a sibling of the blocking chore
5424
+ {
5425
+ blockedType: 16 /* ChoreType.VISIBLE */,
5426
+ blockingType: 4 /* ChoreType.NODE_DIFF */,
5427
+ match: (blocked, blocking, container) => isDescendant(blocked, blocking, container) || isDescendant(blocking, blocked, container),
5428
+ },
5429
+ // COMPONENT blocks VISIBLE on same host
5430
+ // if the blocked chore is a child of the blocking chore
5431
+ // or the blocked chore is a sibling of the blocking chore
5432
+ {
5433
+ blockedType: 16 /* ChoreType.VISIBLE */,
5434
+ blockingType: 6 /* ChoreType.COMPONENT */,
5435
+ match: (blocked, blocking, container) => isDescendant(blocked, blocking, container) || isDescendant(blocking, blocked, container),
5436
+ },
5437
+ ];
5438
+ const BLOCKING_RULES = [
5439
+ // QRL_RESOLVE blocks RUN_QRL, TASK, VISIBLE on same host
5440
+ {
5441
+ blockedType: 2 /* ChoreType.RUN_QRL */,
5442
+ blockingType: 1 /* ChoreType.QRL_RESOLVE */,
5443
+ match: (blocked, blocking) => {
5444
+ const blockedQrl = blocked.$target$;
5445
+ const blockingQrl = blocking.$target$;
5446
+ return isSameHost(blocked, blocking) && isSameQrl(blockedQrl, blockingQrl);
5447
+ },
5448
+ },
5449
+ {
5450
+ blockedType: 3 /* ChoreType.TASK */,
5451
+ blockingType: 1 /* ChoreType.QRL_RESOLVE */,
5452
+ match: (blocked, blocking) => {
5453
+ const blockedTask = blocked.$payload$;
5454
+ const blockingQrl = blocking.$target$;
5455
+ return isSameHost(blocked, blocking) && isSameQrl(blockedTask.$qrl$, blockingQrl);
5456
+ },
5457
+ },
5458
+ {
5459
+ blockedType: 16 /* ChoreType.VISIBLE */,
5460
+ blockingType: 1 /* ChoreType.QRL_RESOLVE */,
5461
+ match: (blocked, blocking) => {
5462
+ const blockedTask = blocked.$payload$;
5463
+ const blockingQrl = blocking.$target$;
5464
+ return isSameHost(blocked, blocking) && isSameQrl(blockedTask.$qrl$, blockingQrl);
5465
+ },
5466
+ },
5467
+ // COMPONENT blocks NODE_DIFF, NODE_PROP on same host
5468
+ {
5469
+ blockedType: 4 /* ChoreType.NODE_DIFF */,
5470
+ blockingType: 6 /* ChoreType.COMPONENT */,
5471
+ match: (blocked, blocking) => blocked.$host$ === blocking.$host$,
5472
+ },
5473
+ {
5474
+ blockedType: 5 /* ChoreType.NODE_PROP */,
5475
+ blockingType: 6 /* ChoreType.COMPONENT */,
5476
+ match: (blocked, blocking) => blocked.$host$ === blocking.$host$,
5477
+ },
5478
+ ...VISIBLE_BLOCKING_RULES,
5479
+ // TASK blocks subsequent TASKs in the same component
5480
+ {
5481
+ blockedType: 3 /* ChoreType.TASK */,
5482
+ blockingType: 3 /* ChoreType.TASK */,
5483
+ match: (blocked, blocking, container) => {
5484
+ if (blocked.$host$ !== blocking.$host$) {
5485
+ return false;
5486
+ }
5487
+ const blockedIdx = blocked.$idx$;
5488
+ if (!isNumber$1(blockedIdx) || blockedIdx <= 0) {
5489
+ return false;
5490
+ }
5491
+ const previousTask = findPreviousTaskInComponent(blocked.$host$, blockedIdx, container);
5492
+ return previousTask === blocking.$payload$;
5493
+ },
5494
+ },
5495
+ ];
5496
+ function isDescendant(descendantChore, ancestorChore, container) {
5497
+ const descendantHost = descendantChore.$host$;
5498
+ const ancestorHost = ancestorChore.$host$;
5499
+ if (!vnode_isVNode(descendantHost) || !vnode_isVNode(ancestorHost)) {
5500
+ return false;
5501
+ }
5502
+ return vnode_isDescendantOf(descendantHost, ancestorHost, container.rootVNode);
5503
+ }
5504
+ function isSameHost(a, b) {
5505
+ return a.$host$ === b.$host$;
5506
+ }
5507
+ function isSameQrl(a, b) {
5508
+ return a.$symbol$ === b.$symbol$;
5509
+ }
5510
+ function findBlockingChoreInQueue(chore, choreQueue, container) {
5511
+ for (const candidate of choreQueue) {
5512
+ // everything after VISIBLE is not blocking. Visible task, task and resource should not block anything in this rule.
5513
+ if (candidate.$type$ >= 16 /* ChoreType.VISIBLE */ || candidate.$type$ === 3 /* ChoreType.TASK */) {
5514
+ continue;
5515
+ }
5516
+ if (isDescendant(chore, candidate, container)) {
5517
+ return candidate;
5518
+ }
5519
+ }
5520
+ return null;
5521
+ }
5522
+ function findBlockingChore(chore, choreQueue, blockedChores, runningChores, container) {
5523
+ const blockingChoreInChoreQueue = findBlockingChoreInQueue(chore, choreQueue, container);
5524
+ if (blockingChoreInChoreQueue) {
5525
+ return blockingChoreInChoreQueue;
5526
+ }
5527
+ const blockingChoreInBlockedChores = findBlockingChoreInQueue(chore, Array.from(blockedChores), container);
5528
+ if (blockingChoreInBlockedChores) {
5529
+ return blockingChoreInBlockedChores;
5530
+ }
5531
+ const blockingChoreInRunningChores = findBlockingChoreInQueue(chore, Array.from(runningChores), container);
5532
+ if (blockingChoreInRunningChores) {
5533
+ return blockingChoreInRunningChores;
5534
+ }
5535
+ for (const rule of BLOCKING_RULES) {
5536
+ if (chore.$type$ !== rule.blockedType) {
5537
+ continue;
5538
+ }
5539
+ // Check in choreQueue
5540
+ // TODO(perf): better to iterate in reverse order?
5541
+ for (const candidate of choreQueue) {
5542
+ if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
5543
+ return candidate;
5544
+ }
5545
+ }
5546
+ // Check in blockedChores
5547
+ for (const candidate of blockedChores) {
5548
+ if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
5549
+ return candidate;
5550
+ }
5551
+ }
5552
+ // Check in runningChores
5553
+ for (const candidate of runningChores) {
5554
+ if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
5555
+ return candidate;
5556
+ }
5557
+ }
5558
+ }
5559
+ return null;
5560
+ }
5561
+ function findPreviousTaskInComponent(host, currentTaskIdx, container) {
5562
+ const elementSeq = container.getHostProp(host, ELEMENT_SEQ);
5563
+ if (!elementSeq || elementSeq.length <= currentTaskIdx) {
5564
+ return null;
5565
+ }
5566
+ for (let i = currentTaskIdx - 1; i >= 0; i--) {
5567
+ const candidate = elementSeq[i];
5568
+ if (candidate instanceof Task && candidate.$flags$ & 2 /* TaskFlags.TASK */) {
5569
+ return candidate;
5570
+ }
5571
+ }
5572
+ return null;
5573
+ }
5574
+ function findBlockingChoreForVisible(chore, runningChores, container) {
5575
+ for (const rule of VISIBLE_BLOCKING_RULES) {
5576
+ if (chore.$type$ !== rule.blockedType) {
5577
+ continue;
5578
+ }
5579
+ for (const candidate of runningChores) {
5580
+ if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
5581
+ return candidate;
5582
+ }
5583
+ }
5584
+ }
5585
+ return null;
5586
+ }
5587
+
5588
+ // This can't be in platform.ts because it uses MessageChannel which cannot post messages with functions
5589
+ // TODO: move this to platform.ts somehow
5590
+ const createNextTick = (fn) => {
5591
+ let nextTick;
5592
+ // according to the https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate#notes
5593
+ if (typeof setImmediate === 'function') {
5594
+ // setImmediate is the fastest way to schedule a task, but works only in node.js
5595
+ nextTick = () => {
5596
+ setImmediate(fn);
5597
+ };
5598
+ }
5599
+ else if (typeof MessageChannel !== 'undefined') {
5600
+ const channel = new MessageChannel();
5601
+ channel.port1.onmessage = () => {
5602
+ fn();
5603
+ };
5604
+ nextTick = () => {
5605
+ channel.port2.postMessage(null);
5606
+ };
5607
+ }
5608
+ else {
5609
+ // setTimeout is a fallback, creates 4ms delay
5610
+ nextTick = () => {
5611
+ setTimeout(fn);
5612
+ };
5613
+ }
5614
+ return nextTick;
5615
+ };
5616
+
5335
5617
  /**
5336
5618
  * Scheduler is responsible for running application code in predictable order.
5337
5619
  *
@@ -5415,29 +5697,43 @@
5415
5697
  */
5416
5698
  // Turn this on to get debug output of what the scheduler is doing.
5417
5699
  const DEBUG = false;
5418
- const getPromise = (chore) => (chore.$promise$ ||= new Promise((resolve) => {
5419
- chore.$resolve$ = resolve;
5420
- }));
5421
- const createScheduler = (container, scheduleDrain, journalFlush) => {
5422
- const choreQueue = [];
5423
- const qrlRuns = [];
5424
- let currentChore = null;
5700
+ var ChoreState;
5701
+ (function (ChoreState) {
5702
+ ChoreState[ChoreState["NONE"] = 0] = "NONE";
5703
+ ChoreState[ChoreState["RUNNING"] = 1] = "RUNNING";
5704
+ ChoreState[ChoreState["FAILED"] = 2] = "FAILED";
5705
+ ChoreState[ChoreState["DONE"] = 3] = "DONE";
5706
+ })(ChoreState || (ChoreState = {}));
5707
+ const getChorePromise = (chore) => chore.$state$ === ChoreState.NONE
5708
+ ? (chore.$returnValue$ ||= new Promise((resolve, reject) => {
5709
+ chore.$resolve$ = resolve;
5710
+ chore.$reject$ = reject;
5711
+ }))
5712
+ : chore.$returnValue$;
5713
+ const createScheduler = (container, journalFlush, choreQueue = [], blockedChores = new Set(), runningChores = new Set()) => {
5714
+ let drainChore = null;
5425
5715
  let drainScheduled = false;
5716
+ let isDraining = false;
5717
+ let isJournalFlushRunning = false;
5718
+ let flushBudgetStart = 0;
5719
+ let currentTime = performance.now();
5720
+ const nextTick = createNextTick(drainChoreQueue);
5721
+ function drainInNextTick() {
5722
+ if (!drainScheduled) {
5723
+ drainScheduled = true;
5724
+ nextTick();
5725
+ }
5726
+ }
5727
+ // Drain for ~16.67ms, then apply journal flush for ~16.67ms, then repeat
5728
+ // We divide by 60 because we want to run at 60fps
5729
+ const FREQUENCY_MS = Math.floor(1000 / 60);
5426
5730
  return schedule;
5427
5731
  ///// IMPLEMENTATION /////
5428
5732
  function schedule(type, hostOrTask = null, targetOrQrl = null, payload = null) {
5429
- const isServer = !isDomContainer(container);
5430
- const isComponentSsr = isServer && type === 6 /* ChoreType.COMPONENT */;
5431
- const runLater = type !== 255 /* ChoreType.WAIT_FOR_ALL */ && !isComponentSsr && type !== 2 /* ChoreType.RUN_QRL */;
5432
- const isTask = type === 3 /* ChoreType.TASK */ || type === 32 /* ChoreType.VISIBLE */ || type === 48 /* ChoreType.CLEANUP_VISIBLE */;
5433
- const isClientOnly = type === 16 /* ChoreType.JOURNAL_FLUSH */ ||
5434
- type === 4 /* ChoreType.NODE_DIFF */ ||
5435
- type === 5 /* ChoreType.NODE_PROP */ ||
5436
- type === 1 /* ChoreType.QRL_RESOLVE */ ||
5437
- type === 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */;
5438
- if (isServer && isClientOnly) {
5439
- return;
5733
+ if (type === 255 /* ChoreType.WAIT_FOR_QUEUE */ && drainChore) {
5734
+ return drainChore;
5440
5735
  }
5736
+ const isTask = type === 3 /* ChoreType.TASK */ || type === 16 /* ChoreType.VISIBLE */ || type === 32 /* ChoreType.CLEANUP_VISIBLE */;
5441
5737
  if (isTask) {
5442
5738
  hostOrTask.$flags$ |= 8 /* TaskFlags.DIRTY */;
5443
5739
  }
@@ -5451,167 +5747,310 @@
5451
5747
  $host$: isTask ? hostOrTask.$el$ : hostOrTask,
5452
5748
  $target$: targetOrQrl,
5453
5749
  $payload$: isTask ? hostOrTask : payload,
5454
- $resolve$: null,
5455
- $promise$: null,
5750
+ $state$: ChoreState.NONE,
5751
+ $blockedChores$: null,
5752
+ $startTime$: undefined,
5753
+ $endTime$: undefined,
5754
+ $resolve$: undefined,
5755
+ $reject$: undefined,
5456
5756
  $returnValue$: null,
5457
- $executed$: false,
5458
5757
  };
5459
- chore = sortedInsert(choreQueue, chore, container.rootVNode || null);
5460
- if (!drainScheduled && runLater) {
5461
- // If we are not currently draining, we need to schedule a drain.
5462
- drainScheduled = true;
5463
- schedule(16 /* ChoreType.JOURNAL_FLUSH */);
5464
- // Catch here to avoid unhandled promise rejection
5465
- scheduleDrain()?.catch?.(() => { });
5758
+ if (type === 255 /* ChoreType.WAIT_FOR_QUEUE */) {
5759
+ getChorePromise(chore);
5760
+ drainChore = chore;
5761
+ drainInNextTick();
5762
+ return chore;
5763
+ }
5764
+ const isServer = isServerPlatform();
5765
+ const isClientOnly = type === 4 /* ChoreType.NODE_DIFF */ || type === 1 /* ChoreType.QRL_RESOLVE */;
5766
+ if (isServer && isClientOnly) {
5767
+ // Mark skipped client-only chores as completed on the server
5768
+ finishChore(chore, undefined);
5769
+ return chore;
5770
+ }
5771
+ if (isServer && chore.$host$ && isSsrNode(chore.$host$)) {
5772
+ const isUpdatable = !!(chore.$host$.flags & 1 /* SsrNodeFlags.Updatable */);
5773
+ if (!isUpdatable) {
5774
+ if (
5775
+ // backpatching exceptions:
5776
+ // - node prop is allowed because it is used to update the node property
5777
+ // - recompute and schedule effects because it triggers effects (so node prop too)
5778
+ chore.$type$ !== 5 /* ChoreType.NODE_PROP */ &&
5779
+ chore.$type$ !== 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */) {
5780
+ // We are running on the server.
5781
+ // On server we can't schedule task for a different host!
5782
+ // Server is SSR, and therefore scheduling for anything but the current host
5783
+ // implies that things need to be re-run and that is not supported because of streaming.
5784
+ const warningMessage = `A '${choreTypeToName(chore.$type$)}' chore was scheduled on a host element that has already been streamed to the client.
5785
+ This can lead to inconsistencies between Server-Side Rendering (SSR) and Client-Side Rendering (CSR).
5786
+
5787
+ Problematic chore:
5788
+ - Type: ${choreTypeToName(chore.$type$)}
5789
+ - Host: ${chore.$host$.toString()}
5790
+ - Nearest element location: ${chore.$host$.currentFile}
5791
+
5792
+ This is often caused by modifying a signal in an already rendered component during SSR.`;
5793
+ logWarn(warningMessage);
5794
+ return chore;
5795
+ }
5796
+ }
5797
+ }
5798
+ const blockingChore = findBlockingChore(chore, choreQueue, blockedChores, runningChores, container);
5799
+ if (blockingChore) {
5800
+ addBlockedChore(chore, blockingChore, blockedChores);
5801
+ return chore;
5466
5802
  }
5467
- // TODO figure out what to do with chore errors
5468
- if (runLater) {
5469
- return getPromise(chore);
5803
+ chore = sortedInsert(choreQueue, chore, container.rootVNode || null);
5804
+ const runImmediately = (isServer && type === 6 /* ChoreType.COMPONENT */) || type === 2 /* ChoreType.RUN_QRL */;
5805
+ if (runImmediately && !isDraining) {
5806
+ immediateDrain();
5470
5807
  }
5471
5808
  else {
5472
- return drainUpTo(chore, isServer);
5473
- }
5474
- }
5475
- /** Execute all of the chores up to and including the given chore. */
5476
- function drainUpTo(runUptoChore, isServer) {
5477
- let maxRetries = 5000;
5478
- while (choreQueue.length) {
5479
- if (maxRetries-- < 0) {
5480
- throw new Error('drainUpTo: max retries reached');
5481
- }
5482
- if (currentChore) {
5483
- // Already running chore, which means we're waiting for async completion
5484
- return getPromise(currentChore)
5485
- .then(() => drainUpTo(runUptoChore, isServer))
5486
- .catch((e) => {
5487
- container.handleError(e, currentChore?.$host$);
5488
- });
5809
+ drainInNextTick();
5810
+ }
5811
+ return chore;
5812
+ }
5813
+ function immediateDrain() {
5814
+ drainScheduled = true;
5815
+ drainChoreQueue();
5816
+ }
5817
+ ////////////////////////////////////////////////////////////////////////////////
5818
+ // Drain queue helpers
5819
+ ////////////////////////////////////////////////////////////////////////////////
5820
+ function applyJournalFlush() {
5821
+ if (!isJournalFlushRunning) {
5822
+ // prevent multiple journal flushes from running at the same time
5823
+ isJournalFlushRunning = true;
5824
+ journalFlush();
5825
+ isJournalFlushRunning = false;
5826
+ flushBudgetStart = performance.now();
5827
+ }
5828
+ }
5829
+ function shouldApplyJournalFlush(isServer) {
5830
+ return !isServer && currentTime - flushBudgetStart >= FREQUENCY_MS;
5831
+ }
5832
+ function drainChoreQueue() {
5833
+ const isServer = isServerPlatform();
5834
+ drainScheduled = false;
5835
+ if (isDraining) {
5836
+ return;
5837
+ }
5838
+ // early return if the queue is empty
5839
+ if (!choreQueue.length) {
5840
+ applyJournalFlush();
5841
+ if (drainChore && !runningChores.size) {
5842
+ // resolve drainChore only if there are no running chores, because
5843
+ // we are sure that we are done
5844
+ drainChore.$resolve$(null);
5845
+ drainChore = null;
5489
5846
  }
5490
- const nextChore = choreQueue[0];
5491
- if (nextChore.$executed$) {
5492
- choreQueue.shift();
5493
- if (nextChore === runUptoChore) {
5494
- break;
5847
+ return;
5848
+ }
5849
+ isDraining = true;
5850
+ flushBudgetStart = performance.now();
5851
+ const maybeFinishDrain = () => {
5852
+ if (choreQueue.length) {
5853
+ drainInNextTick();
5854
+ return false;
5855
+ }
5856
+ if (drainChore && runningChores.size) {
5857
+ if (shouldApplyJournalFlush(isServer)) {
5858
+ // apply journal flush even if we are not finished draining the queue
5859
+ applyJournalFlush();
5495
5860
  }
5496
- continue;
5861
+ return false;
5497
5862
  }
5498
- if (vNodeAlreadyDeleted(nextChore) &&
5499
- // we need to process cleanup tasks for deleted nodes
5500
- nextChore.$type$ !== 48 /* ChoreType.CLEANUP_VISIBLE */) {
5501
- choreQueue.shift();
5502
- continue;
5863
+ currentChore = null;
5864
+ applyJournalFlush();
5865
+ drainChore?.$resolve$(null);
5866
+ drainChore = null;
5867
+ return true;
5868
+ };
5869
+ const scheduleBlockedChoresAndDrainIfNeeded = (chore) => {
5870
+ let blockedChoresScheduled = false;
5871
+ if (chore.$blockedChores$) {
5872
+ for (const blockedChore of chore.$blockedChores$) {
5873
+ const blockingChore = findBlockingChore(blockedChore, choreQueue, blockedChores, runningChores, container);
5874
+ if (blockingChore) {
5875
+ addBlockedChore(blockedChore, blockingChore, blockedChores);
5876
+ }
5877
+ else {
5878
+ blockedChores.delete(blockedChore);
5879
+ sortedInsert(choreQueue, blockedChore, container.rootVNode || null);
5880
+ blockedChoresScheduled = true;
5881
+ }
5882
+ }
5883
+ chore.$blockedChores$ = null;
5503
5884
  }
5504
- executeChore(nextChore, isServer);
5505
- }
5506
- return runUptoChore.$returnValue$;
5507
- }
5508
- function executeChore(chore, isServer) {
5509
- const host = chore.$host$;
5510
- assertEqual(currentChore, null, 'Chore already running.');
5511
- currentChore = chore;
5512
- let returnValue = null;
5885
+ if (blockedChoresScheduled && !isDraining) {
5886
+ drainInNextTick();
5887
+ }
5888
+ };
5889
+ let currentChore = null;
5513
5890
  try {
5514
- switch (chore.$type$) {
5515
- case 255 /* ChoreType.WAIT_FOR_ALL */:
5516
- {
5517
- if (isServer) {
5518
- drainScheduled = false;
5519
- }
5520
- }
5521
- break;
5522
- case 16 /* ChoreType.JOURNAL_FLUSH */:
5523
- {
5524
- returnValue = journalFlush();
5525
- drainScheduled = false;
5891
+ while (choreQueue.length) {
5892
+ currentTime = performance.now();
5893
+ const chore = (currentChore = choreQueue.shift());
5894
+ if (chore.$state$ !== ChoreState.NONE) {
5895
+ continue;
5896
+ }
5897
+ if (vNodeAlreadyDeleted(chore) &&
5898
+ // we need to process cleanup tasks for deleted nodes
5899
+ chore.$type$ !== 32 /* ChoreType.CLEANUP_VISIBLE */) {
5900
+ // skip deleted chore
5901
+ DEBUG && debugTrace('skip chore', chore, choreQueue, blockedChores);
5902
+ continue;
5903
+ }
5904
+ if (chore.$type$ === 16 /* ChoreType.VISIBLE */) {
5905
+ // ensure that the journal flush is applied before the visible chore is executed
5906
+ // so that the visible chore can see the latest DOM changes
5907
+ applyJournalFlush();
5908
+ const blockingChore = findBlockingChoreForVisible(chore, runningChores, container);
5909
+ if (blockingChore && blockingChore.$state$ === ChoreState.RUNNING) {
5910
+ addBlockedChore(chore, blockingChore, blockedChores);
5911
+ continue;
5526
5912
  }
5527
- break;
5528
- case 6 /* ChoreType.COMPONENT */:
5529
- {
5530
- returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
5531
- if (isServer) {
5532
- return jsx;
5913
+ }
5914
+ // Note that this never throws
5915
+ chore.$startTime$ = performance.now();
5916
+ const result = executeChore(chore, isServer);
5917
+ chore.$returnValue$ = result;
5918
+ if (isPromise(result)) {
5919
+ runningChores.add(chore);
5920
+ chore.$state$ = ChoreState.RUNNING;
5921
+ result
5922
+ .then((value) => {
5923
+ finishChore(chore, value);
5924
+ })
5925
+ .catch((e) => {
5926
+ if (chore.$state$ !== ChoreState.RUNNING) {
5927
+ // we already handled the error
5928
+ return;
5929
+ }
5930
+ handleError(chore, e);
5931
+ })
5932
+ .finally(() => {
5933
+ runningChores.delete(chore);
5934
+ // Note that we ignore failed chores so the app keeps working
5935
+ // TODO decide if this is ok and document it
5936
+ scheduleBlockedChoresAndDrainIfNeeded(chore);
5937
+ // If drainChore is not null, we are waiting for it to finish.
5938
+ // If there are no running chores, we can finish the drain.
5939
+ if (!runningChores.size) {
5940
+ let finished = false;
5941
+ if (drainChore) {
5942
+ finished = maybeFinishDrain();
5533
5943
  }
5534
- else {
5535
- const styleScopedId = container.getHostProp(host, QScopedStyle);
5536
- return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
5944
+ if (!finished && !isDraining) {
5945
+ // if finished, then journal flush is already applied
5946
+ applyJournalFlush();
5537
5947
  }
5538
- }, (err) => container.handleError(err, host));
5539
- }
5540
- break;
5541
- case 2 /* ChoreType.RUN_QRL */:
5542
- {
5543
- const fn = chore.$target$.getFn();
5544
- const result = retryOnPromise(() => fn(...chore.$payload$));
5545
- if (isPromise(result)) {
5546
- const handled = result
5547
- .finally(() => {
5548
- qrlRuns.splice(qrlRuns.indexOf(handled), 1);
5549
- })
5550
- .catch((error) => {
5551
- container.handleError(error, chore.$host$);
5552
- });
5553
- // Don't wait for the promise to resolve
5554
- // TODO come up with a better solution, we also want concurrent signal handling with tasks but serial tasks
5555
- qrlRuns.push(handled);
5556
- DEBUG &&
5557
- debugTrace('execute.DONE (but still running)', chore, currentChore, choreQueue);
5558
- chore.$returnValue$ = handled;
5559
- chore.$resolve$?.(handled);
5560
- currentChore = null;
5561
- chore.$executed$ = true;
5562
- // early out so we don't call after()
5563
- return;
5564
5948
  }
5565
- returnValue = null;
5566
- }
5567
- break;
5568
- case 3 /* ChoreType.TASK */:
5569
- case 32 /* ChoreType.VISIBLE */:
5570
- {
5571
- const payload = chore.$payload$;
5572
- if (payload.$flags$ & 4 /* TaskFlags.RESOURCE */) {
5573
- const result = runResource(payload, container, host);
5574
- // Don't await the return value of the resource, because async resources should not be awaited.
5575
- // The reason for this is that we should be able to update for example a node with loading
5576
- // text. If we await the resource, the loading text will not be displayed until the resource
5577
- // is loaded.
5578
- // Awaiting on the client also causes a deadlock.
5579
- // In any case, the resource will never throw.
5580
- returnValue = isServer ? result : null;
5949
+ });
5950
+ }
5951
+ else {
5952
+ finishChore(chore, result);
5953
+ scheduleBlockedChoresAndDrainIfNeeded(chore);
5954
+ }
5955
+ if (shouldApplyJournalFlush(isServer)) {
5956
+ applyJournalFlush();
5957
+ drainInNextTick();
5958
+ return;
5959
+ }
5960
+ }
5961
+ }
5962
+ catch (e) {
5963
+ handleError(currentChore, e);
5964
+ scheduleBlockedChoresAndDrainIfNeeded(currentChore);
5965
+ }
5966
+ finally {
5967
+ isDraining = false;
5968
+ maybeFinishDrain();
5969
+ }
5970
+ }
5971
+ function finishChore(chore, value) {
5972
+ chore.$endTime$ = performance.now();
5973
+ chore.$state$ = ChoreState.DONE;
5974
+ chore.$returnValue$ = value;
5975
+ chore.$resolve$?.(value);
5976
+ }
5977
+ function handleError(chore, e) {
5978
+ chore.$endTime$ = performance.now();
5979
+ chore.$state$ = ChoreState.FAILED;
5980
+ // If we used the result as promise, this won't exist
5981
+ chore.$reject$?.(e);
5982
+ container.handleError(e, chore.$host$);
5983
+ }
5984
+ function executeChore(chore, isServer) {
5985
+ const host = chore.$host$;
5986
+ let returnValue;
5987
+ switch (chore.$type$) {
5988
+ case 6 /* ChoreType.COMPONENT */:
5989
+ {
5990
+ returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
5991
+ if (isServer) {
5992
+ return jsx;
5581
5993
  }
5582
5994
  else {
5583
- returnValue = runTask(payload, container, host);
5995
+ const styleScopedId = container.getHostProp(host, QScopedStyle);
5996
+ return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
5584
5997
  }
5998
+ }, (err) => {
5999
+ handleError(chore, err);
6000
+ });
6001
+ }
6002
+ break;
6003
+ case 2 /* ChoreType.RUN_QRL */:
6004
+ {
6005
+ const fn = chore.$target$.getFn();
6006
+ returnValue = retryOnPromise(() => fn(...chore.$payload$));
6007
+ }
6008
+ break;
6009
+ case 3 /* ChoreType.TASK */:
6010
+ case 16 /* ChoreType.VISIBLE */:
6011
+ {
6012
+ const payload = chore.$payload$;
6013
+ if (payload.$flags$ & 4 /* TaskFlags.RESOURCE */) {
6014
+ returnValue = runResource(payload, container, host);
5585
6015
  }
5586
- break;
5587
- case 48 /* ChoreType.CLEANUP_VISIBLE */:
5588
- {
5589
- const task = chore.$payload$;
5590
- cleanupTask(task);
6016
+ else {
6017
+ returnValue = runTask(payload, container, host);
5591
6018
  }
5592
- break;
5593
- case 4 /* ChoreType.NODE_DIFF */:
5594
- {
5595
- const parentVirtualNode = chore.$target$;
5596
- let jsx = chore.$payload$;
5597
- if (isSignal(jsx)) {
5598
- jsx = jsx.value;
5599
- }
5600
- returnValue = retryOnPromise(() => vnode_diff(container, jsx, parentVirtualNode, null));
6019
+ }
6020
+ break;
6021
+ case 32 /* ChoreType.CLEANUP_VISIBLE */:
6022
+ {
6023
+ const task = chore.$payload$;
6024
+ cleanupTask(task);
6025
+ }
6026
+ break;
6027
+ case 4 /* ChoreType.NODE_DIFF */:
6028
+ {
6029
+ const parentVirtualNode = chore.$target$;
6030
+ let jsx = chore.$payload$;
6031
+ if (isSignal(jsx)) {
6032
+ jsx = jsx.value;
5601
6033
  }
5602
- break;
5603
- case 5 /* ChoreType.NODE_PROP */:
5604
- {
5605
- const virtualNode = chore.$host$;
5606
- const payload = chore.$payload$;
5607
- let value = payload.$value$;
5608
- if (isSignal(value)) {
5609
- value = value.value;
5610
- }
5611
- const isConst = payload.$isConst$;
5612
- const journal = container.$journal$;
5613
- const property = chore.$idx$;
5614
- const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
6034
+ returnValue = retryOnPromise(() => vnode_diff(container, jsx, parentVirtualNode, null));
6035
+ }
6036
+ break;
6037
+ case 5 /* ChoreType.NODE_PROP */:
6038
+ {
6039
+ const virtualNode = chore.$host$;
6040
+ const payload = chore.$payload$;
6041
+ let value = payload.$value$;
6042
+ if (isSignal(value)) {
6043
+ value = value.value;
6044
+ }
6045
+ const isConst = payload.$isConst$;
6046
+ const journal = container.$journal$;
6047
+ const property = chore.$idx$;
6048
+ const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
6049
+ if (isServer) {
6050
+ container.addBackpatchEntry(chore.$host$.id, property, serializedValue);
6051
+ returnValue = null;
6052
+ }
6053
+ else {
5615
6054
  if (isConst) {
5616
6055
  const element = virtualNode[6 /* ElementVNodeProps.element */];
5617
6056
  journal.push(2 /* VNodeJournalOpCode.SetAttribute */, element, property, serializedValue);
@@ -5619,64 +6058,49 @@
5619
6058
  else {
5620
6059
  vnode_setAttr(journal, virtualNode, property, serializedValue);
5621
6060
  }
6061
+ returnValue = undefined;
5622
6062
  }
5623
- break;
5624
- case 1 /* ChoreType.QRL_RESOLVE */: {
5625
- {
5626
- const target = chore.$target$;
5627
- returnValue = !target.resolved ? target.resolve() : null;
5628
- }
5629
- break;
5630
6063
  }
5631
- case 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */: {
5632
- {
5633
- const target = chore.$target$;
5634
- const effects = chore.$payload$;
6064
+ break;
6065
+ case 1 /* ChoreType.QRL_RESOLVE */: {
6066
+ {
6067
+ const target = chore.$target$;
6068
+ returnValue = (!target.resolved ? target.resolve() : null);
6069
+ }
6070
+ break;
6071
+ }
6072
+ case 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */: {
6073
+ {
6074
+ const target = chore.$target$;
6075
+ const effects = chore.$payload$;
6076
+ if (!effects?.size) {
6077
+ break;
6078
+ }
6079
+ let shouldCompute = target instanceof ComputedSignalImpl || target instanceof WrappedSignalImpl;
6080
+ if (target instanceof AsyncComputedSignalImpl && effects !== target.$effects$) {
6081
+ shouldCompute = false;
6082
+ }
6083
+ if (shouldCompute) {
5635
6084
  const ctx = newInvokeContext();
5636
6085
  ctx.$container$ = container;
5637
- if (target instanceof ComputedSignalImpl || target instanceof WrappedSignalImpl) {
5638
- const forceRunEffects = target.$forceRunEffects$;
5639
- target.$forceRunEffects$ = false;
5640
- if (!effects?.size && !forceRunEffects) {
5641
- break;
6086
+ // needed for computed signals and throwing QRLs
6087
+ returnValue = maybeThen(retryOnPromise(() => invoke.call(target, ctx, target.$computeIfNeeded$)), () => {
6088
+ if (target.$flags$ & 2 /* SignalFlags.RUN_EFFECTS */) {
6089
+ target.$flags$ &= -3 /* SignalFlags.RUN_EFFECTS */;
6090
+ return retryOnPromise(() => triggerEffects(container, target, effects));
5642
6091
  }
5643
- // needed for computed signals and throwing QRLs
5644
- returnValue = maybeThen(retryOnPromise(() => invoke.call(target, ctx, target.$computeIfNeeded$)), (didChange) => {
5645
- if (didChange || forceRunEffects) {
5646
- return retryOnPromise(() => triggerEffects(container, target, effects));
5647
- }
5648
- });
5649
- }
5650
- else {
5651
- returnValue = retryOnPromise(() => triggerEffects(container, target, effects));
5652
- }
6092
+ });
6093
+ }
6094
+ else {
6095
+ returnValue = retryOnPromise(() => {
6096
+ triggerEffects(container, target, effects);
6097
+ });
5653
6098
  }
5654
- break;
5655
6099
  }
6100
+ break;
5656
6101
  }
5657
6102
  }
5658
- catch (e) {
5659
- returnValue = Promise.reject(e);
5660
- }
5661
- const after = (value, error) => {
5662
- currentChore = null;
5663
- chore.$executed$ = true;
5664
- if (error) {
5665
- container.handleError(error, host);
5666
- }
5667
- else {
5668
- chore.$returnValue$ = value;
5669
- chore.$resolve$?.(value);
5670
- }
5671
- };
5672
- if (isPromise(returnValue)) {
5673
- chore.$promise$ = returnValue.then(after, (error) => after(undefined, error));
5674
- chore.$resolve$?.(chore.$promise$);
5675
- chore.$resolve$ = undefined;
5676
- }
5677
- else {
5678
- after(returnValue);
5679
- }
6103
+ return returnValue;
5680
6104
  }
5681
6105
  /**
5682
6106
  * Compares two chores to determine their execution order in the scheduler's queue.
@@ -5705,15 +6129,6 @@
5705
6129
  else {
5706
6130
  assertFalse(vnode_isVNode(aHost), 'expected aHost to be SSRNode but it is a VNode');
5707
6131
  assertFalse(vnode_isVNode(bHost), 'expected bHost to be SSRNode but it is a VNode');
5708
- // we are running on the server.
5709
- // On server we can't schedule task for a different host!
5710
- // Server is SSR, and therefore scheduling for anything but the current host
5711
- // implies that things need to be re-run nad that is not supported because of streaming.
5712
- const errorMessage = `SERVER: during HTML streaming, re-running tasks on a different host is not allowed.
5713
- You are attempting to change a state that has already been streamed to the client.
5714
- This can lead to inconsistencies between Server-Side Rendering (SSR) and Client-Side Rendering (CSR).
5715
- Problematic Node: ${aHost.toString()}`;
5716
- logWarn(errorMessage);
5717
6132
  const hostDiff = ssrNodeDocumentPosition(aHost, bHost);
5718
6133
  if (hostDiff !== 0) {
5719
6134
  return hostDiff;
@@ -5737,8 +6152,14 @@
5737
6152
  // 1 means that we are going to process chores as FIFO
5738
6153
  return 1;
5739
6154
  }
5740
- // If the chore is the same as the current chore, we will run it again
5741
- if (b === currentChore) {
6155
+ // ensure that the effect chores are scheduled for the same target
6156
+ // TODO: can we do this better?
6157
+ if (a.$type$ === 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */ &&
6158
+ b.$type$ === 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */ &&
6159
+ ((a.$target$ instanceof StoreHandler && b.$target$ instanceof StoreHandler) ||
6160
+ (a.$target$ instanceof AsyncComputedSignalImpl &&
6161
+ b.$target$ instanceof AsyncComputedSignalImpl)) &&
6162
+ a.$payload$ !== b.$payload$) {
5742
6163
  return 1;
5743
6164
  }
5744
6165
  // The chores are the same and will run only once
@@ -5770,6 +6191,15 @@
5770
6191
  /// We need to ensure that the `queue` is sorted by priority.
5771
6192
  /// 1. Find a place where to insert into.
5772
6193
  const idx = sortedFindIndex(sortedArray, value, rootVNode);
6194
+ if (idx < 0 && runningChores.size) {
6195
+ // 1.1. Check if the chore is already running.
6196
+ for (const chore of runningChores) {
6197
+ const comp = choreComparator(value, chore, rootVNode);
6198
+ if (comp === 0) {
6199
+ return chore;
6200
+ }
6201
+ }
6202
+ }
5773
6203
  if (idx < 0) {
5774
6204
  /// 2. Insert the chore into the queue.
5775
6205
  sortedArray.splice(~idx, 0, value);
@@ -5784,9 +6214,6 @@
5784
6214
  if (existing.$payload$ !== value.$payload$) {
5785
6215
  existing.$payload$ = value.$payload$;
5786
6216
  }
5787
- if (existing.$executed$) {
5788
- existing.$executed$ = false;
5789
- }
5790
6217
  return existing;
5791
6218
  }
5792
6219
  };
@@ -5798,6 +6225,25 @@
5798
6225
  vnode_isVNode(chore.$host$) &&
5799
6226
  chore.$host$[0 /* VNodeProps.flags */] & 32 /* VNodeFlags.Deleted */);
5800
6227
  }
6228
+ function addBlockedChore(blockedChore, blockingChore, blockedChores) {
6229
+ blockingChore.$blockedChores$ ||= [];
6230
+ blockingChore.$blockedChores$.push(blockedChore);
6231
+ blockedChores.add(blockedChore);
6232
+ }
6233
+ function choreTypeToName(type) {
6234
+ return ({
6235
+ [1 /* ChoreType.QRL_RESOLVE */]: 'Resolve QRL',
6236
+ [2 /* ChoreType.RUN_QRL */]: 'Run QRL',
6237
+ [3 /* ChoreType.TASK */]: 'Task',
6238
+ [4 /* ChoreType.NODE_DIFF */]: 'Changes diffing',
6239
+ [5 /* ChoreType.NODE_PROP */]: 'Updating node property',
6240
+ [6 /* ChoreType.COMPONENT */]: 'Component',
6241
+ [7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */]: 'Signal recompute',
6242
+ [16 /* ChoreType.VISIBLE */]: 'Visible',
6243
+ [32 /* ChoreType.CLEANUP_VISIBLE */]: 'Cleanup visible',
6244
+ [255 /* ChoreType.WAIT_FOR_QUEUE */]: 'Wait for queue',
6245
+ }[type] || 'Unknown: ' + type);
6246
+ }
5801
6247
  function debugChoreTypeToString(type) {
5802
6248
  return ({
5803
6249
  [1 /* ChoreType.QRL_RESOLVE */]: 'QRL_RESOLVE',
@@ -5807,31 +6253,87 @@
5807
6253
  [5 /* ChoreType.NODE_PROP */]: 'NODE_PROP',
5808
6254
  [6 /* ChoreType.COMPONENT */]: 'COMPONENT',
5809
6255
  [7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */]: 'RECOMPUTE_SIGNAL',
5810
- [16 /* ChoreType.JOURNAL_FLUSH */]: 'JOURNAL_FLUSH',
5811
- [32 /* ChoreType.VISIBLE */]: 'VISIBLE',
5812
- [48 /* ChoreType.CLEANUP_VISIBLE */]: 'CLEANUP_VISIBLE',
5813
- [255 /* ChoreType.WAIT_FOR_ALL */]: 'WAIT_FOR_ALL',
6256
+ [16 /* ChoreType.VISIBLE */]: 'VISIBLE',
6257
+ [32 /* ChoreType.CLEANUP_VISIBLE */]: 'CLEANUP_VISIBLE',
6258
+ [255 /* ChoreType.WAIT_FOR_QUEUE */]: 'WAIT_FOR_QUEUE',
5814
6259
  }[type] || 'UNKNOWN: ' + type);
5815
6260
  }
5816
- function debugChoreToString(chore) {
5817
- const type = debugChoreTypeToString(chore.$type$);
5818
- const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
5819
- const qrlTarget = chore.$target$?.$symbol$;
5820
- return `Chore(${type} ${chore.$type$ === 1 /* ChoreType.QRL_RESOLVE */ || chore.$type$ === 2 /* ChoreType.RUN_QRL */ ? qrlTarget : host} ${chore.$idx$})`;
5821
- }
5822
- function debugTrace(action, arg, currentChore, queue) {
5823
- const lines = ['===========================\nScheduler: ' + action];
5824
- if (arg && !('$type$' in arg)) {
5825
- lines.push(' arg: ' + String(arg).replaceAll(/\n.*/gim, ''));
5826
- }
5827
- if (queue) {
5828
- queue.forEach((chore) => {
5829
- const active = chore === arg ? '>>>' : ' ';
5830
- lines.push(` ${active} > ` +
5831
- (chore === currentChore ? '[running] ' : '') +
5832
- debugChoreToString(chore));
6261
+ function debugTrace(action, arg, queue, blockedChores) {
6262
+ const lines = [];
6263
+ // Header
6264
+ lines.push(`Scheduler: ${action}`);
6265
+ // Argument section
6266
+ if (arg) {
6267
+ lines.push('');
6268
+ if (arg && '$type$' in arg) {
6269
+ const chore = arg;
6270
+ const type = debugChoreTypeToString(chore.$type$);
6271
+ const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
6272
+ const qrlTarget = chore.$target$?.$symbol$;
6273
+ const targetOrHost = chore.$type$ === 1 /* ChoreType.QRL_RESOLVE */ || chore.$type$ === 2 /* ChoreType.RUN_QRL */
6274
+ ? qrlTarget
6275
+ : host;
6276
+ lines.push(`🎯 Current Chore:`);
6277
+ lines.push(` Type: ${type}`);
6278
+ lines.push(` Host: ${targetOrHost}`);
6279
+ // Show execution time if available
6280
+ if (chore.$startTime$ && chore.$endTime$) {
6281
+ const executionTime = chore.$endTime$ - chore.$startTime$;
6282
+ lines.push(` Time: ${executionTime.toFixed(2)}ms`);
6283
+ }
6284
+ else if (chore.$startTime$) {
6285
+ const elapsedTime = performance.now() - chore.$startTime$;
6286
+ lines.push(` Time: ${elapsedTime.toFixed(2)}ms (running)`);
6287
+ }
6288
+ // Show blocked chores for this chore
6289
+ if (chore.$blockedChores$ && chore.$blockedChores$.length > 0) {
6290
+ lines.push(` ⛔ Blocked Chores:`);
6291
+ chore.$blockedChores$.forEach((blockedChore, index) => {
6292
+ const blockedType = debugChoreTypeToString(blockedChore.$type$);
6293
+ const blockedTarget = String(blockedChore.$host$).replaceAll(/\n.*/gim, '');
6294
+ lines.push(` ${index + 1}. ${blockedType} ${blockedTarget} ${blockedChore.$idx$}`);
6295
+ });
6296
+ }
6297
+ }
6298
+ else {
6299
+ lines.push(`📝 Argument: ${String(arg).replaceAll(/\n.*/gim, '')}`);
6300
+ }
6301
+ }
6302
+ // Queue section
6303
+ if (queue && queue.length > 0) {
6304
+ lines.push('');
6305
+ lines.push(`📋 Queue (${queue.length} items):`);
6306
+ queue.forEach((chore, index) => {
6307
+ const isActive = chore === arg;
6308
+ const activeMarker = isActive ? `▶ ` : ' ';
6309
+ const type = debugChoreTypeToString(chore.$type$);
6310
+ const state = chore.$state$ ? `[${ChoreState[chore.$state$]}]` : '';
6311
+ const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
6312
+ const qrlTarget = chore.$target$?.$symbol$;
6313
+ const target = chore.$type$ === 1 /* ChoreType.QRL_RESOLVE */ || chore.$type$ === 2 /* ChoreType.RUN_QRL */
6314
+ ? qrlTarget
6315
+ : host;
6316
+ const line = `${activeMarker}${state} ${type} ${target} ${chore.$idx$}`;
6317
+ lines.push(line);
5833
6318
  });
5834
6319
  }
6320
+ // Blocked chores section
6321
+ if (blockedChores && blockedChores.size > 0) {
6322
+ lines.push('');
6323
+ lines.push(`🚫 Blocked Chores (${blockedChores.size} items):`);
6324
+ Array.from(blockedChores).forEach((chore, index) => {
6325
+ const type = debugChoreTypeToString(chore.$type$);
6326
+ const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
6327
+ const qrlTarget = chore.$target$?.$symbol$;
6328
+ const target = chore.$type$ === 1 /* ChoreType.QRL_RESOLVE */ || chore.$type$ === 2 /* ChoreType.RUN_QRL */
6329
+ ? qrlTarget
6330
+ : host;
6331
+ lines.push(` ${index + 1}. ${type} ${target} ${chore.$idx$}`);
6332
+ });
6333
+ }
6334
+ // Footer
6335
+ lines.push('');
6336
+ lines.push('─'.repeat(60));
5835
6337
  // eslint-disable-next-line no-console
5836
6338
  console.log(lines.join('\n') + '\n');
5837
6339
  }
@@ -5849,7 +6351,8 @@
5849
6351
  $currentUniqueId$ = 0;
5850
6352
  $instanceHash$ = null;
5851
6353
  $buildBase$ = null;
5852
- constructor(scheduleDrain, journalFlush, serverData, locale) {
6354
+ $flushEpoch$ = 0;
6355
+ constructor(journalFlush, serverData, locale) {
5853
6356
  this.$serverData$ = serverData;
5854
6357
  this.$locale$ = locale;
5855
6358
  this.$version$ = version;
@@ -5857,7 +6360,7 @@
5857
6360
  this.$getObjectById$ = (_id) => {
5858
6361
  throw Error('Not implemented');
5859
6362
  };
5860
- this.$scheduler$ = createScheduler(this, scheduleDrain, journalFlush);
6363
+ this.$scheduler$ = createScheduler(this, journalFlush);
5861
6364
  }
5862
6365
  trackSignalValue(signal, subscriber, property, data) {
5863
6366
  return trackSignalAndAssignHost(signal, subscriber, property, this, data);
@@ -6166,9 +6669,10 @@
6166
6669
  const shadowRootContainer = node;
6167
6670
  const shadowRoot = shadowRootContainer?.shadowRoot;
6168
6671
  if (shadowRoot) {
6672
+ const firstShadowRootChild = firstChild(shadowRoot);
6169
6673
  walkContainer(
6170
6674
  // we need to create a new walker for the shadow root
6171
- document.createTreeWalker(shadowRoot, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */), null, firstChild(shadowRoot), null, '', null);
6675
+ document.createTreeWalker(firstShadowRootChild, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */), null, firstShadowRootChild, null, '', null);
6172
6676
  }
6173
6677
  }
6174
6678
  if ((nodeType & 2 /* NodeType.ELEMENT */) === 2 /* NodeType.ELEMENT */) {
@@ -6244,7 +6748,7 @@
6244
6748
  /** @internal */
6245
6749
  function _getQContainerElement(element) {
6246
6750
  const qContainerElement = Array.isArray(element)
6247
- ? vnode_getDomParent(element)
6751
+ ? vnode_getDomParent(element, true)
6248
6752
  : element;
6249
6753
  return qContainerElement.closest(QContainerSelector);
6250
6754
  }
@@ -6259,7 +6763,6 @@
6259
6763
  rootVNode;
6260
6764
  document;
6261
6765
  $journal$;
6262
- renderDone = null;
6263
6766
  $rawStateData$;
6264
6767
  $storeProxyMap$ = new WeakMap();
6265
6768
  $qFuncs$;
@@ -6269,9 +6772,11 @@
6269
6772
  vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
6270
6773
  $stateData$;
6271
6774
  $styleIds$ = null;
6272
- $renderCount$ = 0;
6273
6775
  constructor(element) {
6274
- super(() => this.scheduleRender(), () => vnode_applyJournal(this.$journal$), {}, element.getAttribute(QLocaleAttr));
6776
+ super(() => {
6777
+ this.$flushEpoch$++;
6778
+ vnode_applyJournal(this.$journal$);
6779
+ }, {}, element.getAttribute(QLocaleAttr));
6275
6780
  this.qContainer = element.getAttribute(QContainerAttr);
6276
6781
  if (!this.qContainer) {
6277
6782
  throw qError(25 /* QError.elementWithoutContainer */);
@@ -6313,20 +6818,17 @@
6313
6818
  }
6314
6819
  handleError(err, host) {
6315
6820
  if (qDev && host) {
6316
- // Clean vdom
6317
6821
  if (typeof document !== 'undefined') {
6318
6822
  const vHost = host;
6319
- const errorDiv = document.createElement('errored-host');
6320
- if (err && err instanceof Error) {
6321
- errorDiv.props = { error: err };
6322
- }
6323
- errorDiv.setAttribute('q:key', '_error_');
6324
6823
  const journal = [];
6325
- const vErrorDiv = vnode_newElement(errorDiv, 'errored-host');
6326
- vnode_getDOMChildNodes(journal, vHost, true).forEach((child) => {
6327
- vnode_insertBefore(journal, vErrorDiv, child, null);
6328
- });
6329
- vnode_insertBefore(journal, vHost, vErrorDiv, null);
6824
+ const vHostParent = vnode_getParent(vHost);
6825
+ const vHostNextSibling = vnode_getNextSibling(vHost);
6826
+ const vErrorDiv = vnode_createErrorDiv(document, vHost, err, journal);
6827
+ // If the host is an element node, we need to insert the error div into its parent.
6828
+ const insertHost = vnode_isElementVNode(vHost) ? vHostParent || vHost : vHost;
6829
+ // If the host is different then we need to insert errored-host in the same position as the host.
6830
+ const insertBefore = insertHost === vHost ? null : vHostNextSibling;
6831
+ vnode_insertBefore(journal, insertHost, vErrorDiv, insertBefore);
6330
6832
  vnode_applyJournal(journal);
6331
6833
  }
6332
6834
  if (err && err instanceof Error) {
@@ -6346,19 +6848,16 @@
6346
6848
  }
6347
6849
  setContext(host, context, value) {
6348
6850
  let ctx = this.getHostProp(host, QCtxAttr);
6349
- if (!ctx) {
6851
+ if (ctx == null) {
6350
6852
  this.setHostProp(host, QCtxAttr, (ctx = []));
6351
6853
  }
6352
- mapArray_set(ctx, context.id, value, 0);
6854
+ mapArray_set(ctx, context.id, value, 0, true);
6353
6855
  }
6354
6856
  resolveContext(host, contextId) {
6355
6857
  while (host) {
6356
6858
  const ctx = this.getHostProp(host, QCtxAttr);
6357
- if (ctx) {
6358
- const value = mapArray_get(ctx, contextId.id, 0);
6359
- if (value) {
6360
- return value;
6361
- }
6859
+ if (ctx != null && mapArray_has(ctx, contextId.id, 0)) {
6860
+ return mapArray_get(ctx, contextId.id, 0);
6362
6861
  }
6363
6862
  host = this.getParentHost(host);
6364
6863
  }
@@ -6404,29 +6903,6 @@
6404
6903
  }
6405
6904
  return vnode_getProp(vNode, name, getObjectById);
6406
6905
  }
6407
- scheduleRender() {
6408
- this.$renderCount$++;
6409
- this.renderDone ||= getPlatform().nextTick(() => this.processChores());
6410
- return this.renderDone.finally(() => emitEvent('qrender', { instanceHash: this.$instanceHash$, renderCount: this.$renderCount$ }));
6411
- }
6412
- processChores() {
6413
- let renderCount = this.$renderCount$;
6414
- const result = this.$scheduler$(255 /* ChoreType.WAIT_FOR_ALL */);
6415
- if (isPromise(result)) {
6416
- return result.then(async () => {
6417
- while (renderCount !== this.$renderCount$) {
6418
- renderCount = this.$renderCount$;
6419
- await this.$scheduler$(255 /* ChoreType.WAIT_FOR_ALL */);
6420
- }
6421
- this.renderDone = null;
6422
- });
6423
- }
6424
- if (renderCount !== this.$renderCount$) {
6425
- this.processChores();
6426
- return;
6427
- }
6428
- this.renderDone = null;
6429
- }
6430
6906
  ensureProjectionResolved(vNode) {
6431
6907
  if ((vNode[0 /* VNodeProps.flags */] & 16 /* VNodeFlags.Resolved */) === 0) {
6432
6908
  vNode[0 /* VNodeProps.flags */] |= 16 /* VNodeFlags.Resolved */;
@@ -6436,7 +6912,9 @@
6436
6912
  if (isSlotProp(prop)) {
6437
6913
  const value = props[i + 1];
6438
6914
  if (typeof value == 'string') {
6439
- props[i + 1] = this.vNodeLocate(value);
6915
+ const projection = this.vNodeLocate(value);
6916
+ props[i + 1] = projection;
6917
+ vnode_getProp(projection, QSlotParent, (id) => this.vNodeLocate(id));
6440
6918
  }
6441
6919
  }
6442
6920
  }
@@ -6557,7 +7035,7 @@
6557
7035
  }
6558
7036
  };
6559
7037
  const triggerEffects = (container, signal, effects) => {
6560
- const isBrowser = isDomContainer(container);
7038
+ const isBrowser = !isServerPlatform();
6561
7039
  if (effects) {
6562
7040
  const scheduleEffect = (effectSubscription) => {
6563
7041
  const consumer = effectSubscription[0 /* EffectSubscriptionProp.CONSUMER */];
@@ -6567,7 +7045,7 @@
6567
7045
  consumer.$flags$ |= 8 /* TaskFlags.DIRTY */;
6568
7046
  let choreType = 3 /* ChoreType.TASK */;
6569
7047
  if (consumer.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */) {
6570
- choreType = 32 /* ChoreType.VISIBLE */;
7048
+ choreType = 16 /* ChoreType.VISIBLE */;
6571
7049
  }
6572
7050
  container.$scheduler$(choreType, consumer);
6573
7051
  }
@@ -6590,22 +7068,22 @@
6590
7068
  const props = container.getHostProp(host, ELEMENT_PROPS);
6591
7069
  container.$scheduler$(6 /* ChoreType.COMPONENT */, host, qrl, props);
6592
7070
  }
6593
- else if (isBrowser) {
6594
- if (property === "." /* EffectProperty.VNODE */) {
7071
+ else if (property === "." /* EffectProperty.VNODE */) {
7072
+ if (isBrowser) {
6595
7073
  const host = consumer;
6596
7074
  container.$scheduler$(4 /* ChoreType.NODE_DIFF */, host, host, signal);
6597
7075
  }
6598
- else {
6599
- const host = consumer;
6600
- const effectData = effectSubscription[3 /* EffectSubscriptionProp.DATA */];
6601
- if (effectData instanceof SubscriptionData) {
6602
- const data = effectData.data;
6603
- const payload = {
6604
- ...data,
6605
- $value$: signal,
6606
- };
6607
- container.$scheduler$(5 /* ChoreType.NODE_PROP */, host, property, payload);
6608
- }
7076
+ }
7077
+ else {
7078
+ const host = consumer;
7079
+ const effectData = effectSubscription[3 /* EffectSubscriptionProp.DATA */];
7080
+ if (effectData instanceof SubscriptionData) {
7081
+ const data = effectData.data;
7082
+ const payload = {
7083
+ ...data,
7084
+ $value$: signal,
7085
+ };
7086
+ container.$scheduler$(5 /* ChoreType.NODE_PROP */, host, property, payload);
6609
7087
  }
6610
7088
  }
6611
7089
  };
@@ -6626,10 +7104,10 @@
6626
7104
  // flags |= ComputedSignalFlags.SERIALIZATION_STRATEGY_AUTO;
6627
7105
  // break;
6628
7106
  case 'never':
6629
- flags |= 8 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
7107
+ flags |= 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
6630
7108
  break;
6631
7109
  case 'always':
6632
- flags |= 16 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
7110
+ flags |= 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
6633
7111
  break;
6634
7112
  }
6635
7113
  return flags;
@@ -7021,6 +7499,9 @@
7021
7499
  }
7022
7500
  let vParent = null;
7023
7501
  do {
7502
+ if (callback?.(vCursor, vParent)) {
7503
+ return;
7504
+ }
7024
7505
  const vFirstChild = vnode_getFirstChild(vCursor);
7025
7506
  if (vFirstChild) {
7026
7507
  vCursor = vFirstChild;
@@ -7189,6 +7670,7 @@
7189
7670
  const flags = textVNode[0 /* VNodeProps.flags */];
7190
7671
  if ((flags & 8 /* VNodeFlags.Inflated */) === 0) {
7191
7672
  const parentNode = vnode_getDomParent(vnode);
7673
+ assertDefined(parentNode, 'Missing parent node.');
7192
7674
  const sharedTextNode = textVNode[4 /* TextVNodeProps.node */];
7193
7675
  const doc = parentNode.ownerDocument;
7194
7676
  // Walk the previous siblings and inflate them.
@@ -7347,6 +7829,18 @@
7347
7829
  }
7348
7830
  return length;
7349
7831
  };
7832
+ const vnode_createErrorDiv = (document, host, err, journal) => {
7833
+ const errorDiv = document.createElement('errored-host');
7834
+ if (err && err instanceof Error) {
7835
+ errorDiv.props = { error: err };
7836
+ }
7837
+ errorDiv.setAttribute('q:key', '_error_');
7838
+ const vErrorDiv = vnode_newElement(errorDiv, 'errored-host');
7839
+ vnode_getDOMChildNodes(journal, host, true).forEach((child) => {
7840
+ vnode_insertBefore(journal, vErrorDiv, child, null);
7841
+ });
7842
+ return vErrorDiv;
7843
+ };
7350
7844
  const parseBoolean = (value) => {
7351
7845
  if (value === 'false') {
7352
7846
  return false;
@@ -7485,7 +7979,7 @@
7485
7979
  * unlink the previous or next sibling, we don't know that after "a" node is "b". So we need to
7486
7980
  * find children first (and inflate them).
7487
7981
  */
7488
- const domParentVNode = vnode_getDomParentVNode(parent);
7982
+ const domParentVNode = vnode_getDomParentVNode(parent, false);
7489
7983
  const parentNode = domParentVNode && domParentVNode[6 /* ElementVNodeProps.element */];
7490
7984
  let domChildren = null;
7491
7985
  if (domParentVNode) {
@@ -7534,27 +8028,31 @@
7534
8028
  newChildCurrentParent !== parent)) {
7535
8029
  vnode_remove(journal, newChildCurrentParent, newChild, false);
7536
8030
  }
7537
- let adjustedInsertBefore = null;
7538
- if (insertBefore == null) {
7539
- if (vnode_isVirtualVNode(parent)) {
7540
- // If `insertBefore` is null, than we need to insert at the end of the list.
7541
- // Well, not quite. If the parent is a virtual node, our "last node" is not the same
7542
- // as the DOM "last node". So in that case we need to look for the "next node" from
7543
- // our parent.
7544
- adjustedInsertBefore = vnode_getDomSibling(parent, true, false);
8031
+ const parentIsDeleted = parent[0 /* VNodeProps.flags */] & 32 /* VNodeFlags.Deleted */;
8032
+ // if the parent is deleted, then we don't need to insert the new child
8033
+ if (!parentIsDeleted) {
8034
+ let adjustedInsertBefore = null;
8035
+ if (insertBefore == null) {
8036
+ if (vnode_isVirtualVNode(parent)) {
8037
+ // If `insertBefore` is null, than we need to insert at the end of the list.
8038
+ // Well, not quite. If the parent is a virtual node, our "last node" is not the same
8039
+ // as the DOM "last node". So in that case we need to look for the "next node" from
8040
+ // our parent.
8041
+ adjustedInsertBefore = vnode_getDomSibling(parent, true, false);
8042
+ }
8043
+ }
8044
+ else if (vnode_isVirtualVNode(insertBefore)) {
8045
+ // If the `insertBefore` is virtual, than we need to descend into the virtual and find e actual
8046
+ adjustedInsertBefore = vnode_getDomSibling(insertBefore, true, true);
8047
+ }
8048
+ else {
8049
+ adjustedInsertBefore = insertBefore;
8050
+ }
8051
+ adjustedInsertBefore && vnode_ensureInflatedIfText(journal, adjustedInsertBefore);
8052
+ // Here we know the insertBefore node
8053
+ if (domChildren && domChildren.length) {
8054
+ journal.push(5 /* VNodeJournalOpCode.Insert */, parentNode, vnode_getNode(adjustedInsertBefore), ...domChildren);
7545
8055
  }
7546
- }
7547
- else if (vnode_isVirtualVNode(insertBefore)) {
7548
- // If the `insertBefore` is virtual, than we need to descend into the virtual and find e actual
7549
- adjustedInsertBefore = vnode_getDomSibling(insertBefore, true, true);
7550
- }
7551
- else {
7552
- adjustedInsertBefore = insertBefore;
7553
- }
7554
- adjustedInsertBefore && vnode_ensureInflatedIfText(journal, adjustedInsertBefore);
7555
- // Here we know the insertBefore node
7556
- if (domChildren && domChildren.length) {
7557
- journal.push(5 /* VNodeJournalOpCode.Insert */, parentNode, vnode_getNode(adjustedInsertBefore), ...domChildren);
7558
8056
  }
7559
8057
  // link newChild into the previous/next list
7560
8058
  const vNext = insertBefore;
@@ -7576,14 +8074,22 @@
7576
8074
  newChild[2 /* VNodeProps.previousSibling */] = vPrevious;
7577
8075
  newChild[3 /* VNodeProps.nextSibling */] = vNext;
7578
8076
  newChild[1 /* VNodeProps.parent */] = parent;
8077
+ if (parentIsDeleted) {
8078
+ // if the parent is deleted, then the new child is also deleted
8079
+ newChild[0 /* VNodeProps.flags */] |= 32 /* VNodeFlags.Deleted */;
8080
+ }
7579
8081
  };
7580
- const vnode_getDomParent = (vnode) => {
7581
- vnode = vnode_getDomParentVNode(vnode);
8082
+ const vnode_getDomParent = (vnode, includeProjection = true) => {
8083
+ vnode = vnode_getDomParentVNode(vnode, includeProjection);
7582
8084
  return (vnode && vnode[6 /* ElementVNodeProps.element */]);
7583
8085
  };
7584
- const vnode_getDomParentVNode = (vnode) => {
8086
+ const vnode_getDomParentVNode = (vnode, includeProjection = true) => {
7585
8087
  while (vnode && !vnode_isElementVNode(vnode)) {
7586
- vnode = vnode[1 /* VNodeProps.parent */];
8088
+ vnode =
8089
+ vnode[1 /* VNodeProps.parent */] ||
8090
+ (includeProjection
8091
+ ? vnode_getProp(vnode, QSlotParent, (id) => (vnode_isVNode(id) ? id : null))
8092
+ : null);
7587
8093
  }
7588
8094
  return vnode;
7589
8095
  };
@@ -7593,7 +8099,7 @@
7593
8099
  vnode_ensureTextInflated(journal, vToRemove);
7594
8100
  }
7595
8101
  if (removeDOM) {
7596
- const domParent = vnode_getDomParent(vParent);
8102
+ const domParent = vnode_getDomParent(vParent, false);
7597
8103
  const isInnerHTMLParent = vnode_getAttr(vParent, dangerouslySetInnerHTML);
7598
8104
  if (isInnerHTMLParent) {
7599
8105
  // ignore children, as they are inserted via innerHTML
@@ -8074,6 +8580,26 @@
8074
8580
  const vnode_getParent = (vnode) => {
8075
8581
  return vnode[1 /* VNodeProps.parent */] || null;
8076
8582
  };
8583
+ const vnode_isDescendantOf = (vnode, ancestor, rootVNode) => {
8584
+ let parent = vnode_getParentOrProjectionParent(vnode, rootVNode);
8585
+ while (parent) {
8586
+ if (parent === ancestor) {
8587
+ return true;
8588
+ }
8589
+ parent = vnode_getParentOrProjectionParent(parent, rootVNode);
8590
+ }
8591
+ return false;
8592
+ };
8593
+ const vnode_getParentOrProjectionParent = (vnode, rootVNode) => {
8594
+ if (rootVNode) {
8595
+ const parentProjection = vnode_getProp(vnode, QSlotParent, (id) => vnode_locate(rootVNode, id));
8596
+ if (parentProjection) {
8597
+ // This is a projection, so we need to check the parent of the projection
8598
+ return parentProjection;
8599
+ }
8600
+ }
8601
+ return vnode_getParent(vnode);
8602
+ };
8077
8603
  const vnode_getNode = (vnode) => {
8078
8604
  if (vnode === null || vnode_isVirtualVNode(vnode)) {
8079
8605
  return null;
@@ -8433,13 +8959,15 @@
8433
8959
  }
8434
8960
  const container = this.$container$;
8435
8961
  let propValue = allocate(container, typeId, value);
8962
+ Reflect.set(target, property, propValue);
8963
+ this.$data$[idx] = undefined;
8964
+ this.$data$[idx + 1] = propValue;
8436
8965
  /** We stored the reference, so now we can inflate, allowing cycles. */
8437
8966
  if (typeId >= 14 /* TypeIds.Error */) {
8438
8967
  propValue = inflate(container, propValue, typeId, value);
8968
+ Reflect.set(target, property, propValue);
8969
+ this.$data$[idx + 1] = propValue;
8439
8970
  }
8440
- Reflect.set(target, property, propValue);
8441
- this.$data$[idx] = undefined;
8442
- this.$data$[idx + 1] = propValue;
8443
8971
  return propValue;
8444
8972
  }
8445
8973
  has(target, property) {
@@ -8618,7 +9146,7 @@
8618
9146
  */
8619
9147
  // try to download qrl in this tick
8620
9148
  computed.$computeQrl$.resolve();
8621
- container.$scheduler$?.(1 /* ChoreType.QRL_RESOLVE */, null, computed.$computeQrl$);
9149
+ container.$scheduler$(1 /* ChoreType.QRL_RESOLVE */, null, computed.$computeQrl$);
8622
9150
  }
8623
9151
  break;
8624
9152
  }
@@ -9406,8 +9934,8 @@
9406
9934
  }
9407
9935
  else if (value instanceof ComputedSignalImpl) {
9408
9936
  let v = value.$untrackedValue$;
9409
- const shouldAlwaysSerialize = value.$flags$ & 16 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
9410
- const shouldNeverSerialize = value.$flags$ & 8 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
9937
+ const shouldAlwaysSerialize = value.$flags$ & 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
9938
+ const shouldNeverSerialize = value.$flags$ & 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
9411
9939
  const isInvalid = value.$flags$ & 1 /* SignalFlags.INVALID */;
9412
9940
  const isSkippable = fastSkipSerialize(value.$untrackedValue$);
9413
9941
  if (shouldAlwaysSerialize) {
@@ -10749,7 +11277,7 @@
10749
11277
  container.$serverData$ = opts.serverData || {};
10750
11278
  const host = container.rootVNode;
10751
11279
  container.$scheduler$(4 /* ChoreType.NODE_DIFF */, host, host, jsxNode);
10752
- await container.$scheduler$(255 /* ChoreType.WAIT_FOR_ALL */);
11280
+ await container.$scheduler$(255 /* ChoreType.WAIT_FOR_QUEUE */).$returnValue$;
10753
11281
  return {
10754
11282
  cleanup: () => {
10755
11283
  cleanup(container, container.rootVNode);
@@ -11587,7 +12115,7 @@
11587
12115
  useRunTask(task, eagerness);
11588
12116
  if (!isServerPlatform()) {
11589
12117
  qrl.resolve(iCtx.$element$);
11590
- iCtx.$container$.$scheduler$(32 /* ChoreType.VISIBLE */, task);
12118
+ iCtx.$container$.$scheduler$(16 /* ChoreType.VISIBLE */, task);
11591
12119
  }
11592
12120
  };
11593
12121
  const useRunTask = (task, eagerness) => {
@@ -11816,7 +12344,7 @@
11816
12344
  // the file 'qwik-prefetch-service-worker.js' is not located in /build/
11817
12345
  resolvedOpts.path = baseUrl + resolvedOpts.path;
11818
12346
  }
11819
- let code = PREFETCH_CODE.replace("'_URL_'", JSON.stringify(resolvedOpts.path));
12347
+ let code = PREFETCH_CODE.replace('"_URL_"', JSON.stringify(resolvedOpts.path.split('/').pop()));
11820
12348
  if (!build.isDev) {
11821
12349
  // consecutive spaces are indentation
11822
12350
  code = code.replaceAll(/\s\s+/gm, '');
@@ -11846,6 +12374,17 @@
11846
12374
  });
11847
12375
  });
11848
12376
  }
12377
+ if ('caches' in window) {
12378
+ caches
12379
+ .keys()
12380
+ .then((names) => {
12381
+ const cacheName = names.find((name) => name.startsWith('QwikBundles'));
12382
+ if (cacheName) {
12383
+ caches.delete(cacheName).catch(console.error);
12384
+ }
12385
+ })
12386
+ .catch(console.error);
12387
+ }
11849
12388
  }).toString();
11850
12389
  /**
11851
12390
  * @deprecated This is no longer needed as the preloading happens automatically in qrl-class. You
@@ -11854,6 +12393,16 @@
11854
12393
  */
11855
12394
  const PrefetchGraph = (_opts = {}) => null;
11856
12395
 
12396
+ //////////////////////////////////////////////////////////////////////////////////////////
12397
+ // Protect against duplicate imports
12398
+ //////////////////////////////////////////////////////////////////////////////////////////
12399
+ if (globalThis.__qwik) {
12400
+ console.error(`==============================================\n` +
12401
+ `Qwik version ${globalThis.__qwik} already imported while importing ${version}. Verify external vs bundled imports etc. This can lead to issues due to duplicated shared structures.\n` +
12402
+ `==============================================\n`);
12403
+ }
12404
+ globalThis.__qwik = version;
12405
+
11857
12406
  Object.defineProperty(exports, "isBrowser", {
11858
12407
  enumerable: true,
11859
12408
  get: function () { return build.isBrowser; }
@@ -11892,11 +12441,14 @@
11892
12441
  exports._deserialize = _deserialize;
11893
12442
  exports._dumpState = dumpState;
11894
12443
  exports._fnSignal = _fnSignal;
12444
+ exports._getConstProps = _getConstProps;
11895
12445
  exports._getContextContainer = _getContextContainer;
11896
12446
  exports._getContextElement = _getContextElement;
11897
12447
  exports._getContextEvent = _getContextEvent;
11898
12448
  exports._getDomContainer = getDomContainer;
11899
12449
  exports._getQContainerElement = _getQContainerElement;
12450
+ exports._getVarProps = _getVarProps;
12451
+ exports._hasStoreEffects = _hasStoreEffects;
11900
12452
  exports._isJSXNode = isJSXNode;
11901
12453
  exports._isStore = isStore;
11902
12454
  exports._isStringifiable = isStringifiable;
@@ -11917,7 +12469,7 @@
11917
12469
  exports._regSymbol = _regSymbol;
11918
12470
  exports._resolveContextWithoutSequentialScope = _resolveContextWithoutSequentialScope;
11919
12471
  exports._restProps = _restProps;
11920
- exports._run = queueQRL;
12472
+ exports._run = _run;
11921
12473
  exports._serializationWeakRef = _serializationWeakRef;
11922
12474
  exports._serialize = _serialize;
11923
12475
  exports._task = scheduleTask;
@@ -11950,6 +12502,7 @@
11950
12502
  exports.createSignal = createSignal;
11951
12503
  exports.event$ = event$;
11952
12504
  exports.eventQrl = eventQrl;
12505
+ exports.forceStoreEffects = forceStoreEffects;
11953
12506
  exports.getDomContainer = getDomContainer;
11954
12507
  exports.getLocale = getLocale;
11955
12508
  exports.getPlatform = getPlatform;