@qwik.dev/core 2.0.0-beta.1 → 2.0.0-beta.10

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 (99) 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 +3031 -763
  14. package/dist/core-internal.d.ts +253 -78
  15. package/dist/core.cjs +1770 -957
  16. package/dist/core.cjs.map +1 -1
  17. package/dist/core.min.mjs +1 -1
  18. package/dist/core.mjs +1745 -955
  19. package/dist/core.mjs.map +1 -1
  20. package/dist/core.prod.cjs +1368 -949
  21. package/dist/core.prod.mjs +1435 -951
  22. package/dist/insights/vite/index.cjs +1 -1
  23. package/dist/insights/vite/index.mjs +10 -10
  24. package/dist/loader/index.cjs +2 -2
  25. package/dist/loader/index.mjs +2 -2
  26. package/dist/loader/package.json +1 -1
  27. package/dist/optimizer.cjs +201 -4016
  28. package/dist/optimizer.d.ts +12 -37
  29. package/dist/optimizer.mjs +2539 -3715
  30. package/dist/preloader.cjs +8 -11
  31. package/dist/preloader.mjs +8 -11
  32. package/dist/qwikloader.debug.js +1 -15
  33. package/dist/qwikloader.js +1 -1
  34. package/dist/server.cjs +309 -116
  35. package/dist/server.d.ts +17 -2
  36. package/dist/server.mjs +291 -103
  37. package/dist/starters/adapters/aws-lambda/adapters/aws-lambda/vite.config.mts +1 -1
  38. package/dist/starters/adapters/aws-lambda/package.json +1 -1
  39. package/dist/starters/adapters/aws-lambda/src/entry_aws-lambda.tsx +2 -6
  40. package/dist/starters/adapters/azure-swa/adapters/azure-swa/vite.config.mts +1 -1
  41. package/dist/starters/adapters/azure-swa/package.json +1 -1
  42. package/dist/starters/adapters/azure-swa/src/entry.azure-swa.tsx +2 -3
  43. package/dist/starters/adapters/bun/adapters/bun/vite.config.mts +2 -2
  44. package/dist/starters/adapters/bun/package.json +1 -1
  45. package/dist/starters/adapters/bun/src/entry.bun.ts +0 -2
  46. package/dist/starters/adapters/cloud-run/adapters/cloud-run/vite.config.mts +1 -1
  47. package/dist/starters/adapters/cloud-run/package.json +1 -1
  48. package/dist/starters/adapters/cloud-run/src/entry.cloud-run.tsx +1 -3
  49. package/dist/starters/adapters/cloudflare-pages/adapters/cloudflare-pages/vite.config.mts +1 -1
  50. package/dist/starters/adapters/cloudflare-pages/package.json +1 -1
  51. package/dist/starters/adapters/cloudflare-pages/src/entry.cloudflare-pages.tsx +2 -3
  52. package/dist/starters/adapters/deno/adapters/deno/vite.config.mts +1 -1
  53. package/dist/starters/adapters/deno/package.json +1 -1
  54. package/dist/starters/adapters/deno/src/entry.deno.ts +0 -2
  55. package/dist/starters/adapters/express/adapters/express/vite.config.mts +1 -1
  56. package/dist/starters/adapters/express/package.json +1 -1
  57. package/dist/starters/adapters/express/src/entry.express.tsx +1 -3
  58. package/dist/starters/adapters/fastify/adapters/fastify/vite.config.mts +1 -1
  59. package/dist/starters/adapters/fastify/package.json +1 -1
  60. package/dist/starters/adapters/fastify/src/entry.fastify.tsx +1 -1
  61. package/dist/starters/adapters/fastify/src/plugins/fastify-qwik.ts +1 -2
  62. package/dist/starters/adapters/firebase/adapters/firebase/vite.config.mts +1 -1
  63. package/dist/starters/adapters/firebase/package.json +1 -1
  64. package/dist/starters/adapters/firebase/src/entry-firebase.tsx +2 -3
  65. package/dist/starters/adapters/netlify-edge/adapters/netlify-edge/vite.config.mts +1 -1
  66. package/dist/starters/adapters/netlify-edge/package.json +1 -1
  67. package/dist/starters/adapters/netlify-edge/src/entry.netlify-edge.tsx +2 -3
  68. package/dist/starters/adapters/node-server/adapters/node-server/vite.config.mts +1 -1
  69. package/dist/starters/adapters/node-server/package.json +1 -1
  70. package/dist/starters/adapters/node-server/src/entry.node-server.tsx +0 -2
  71. package/dist/starters/adapters/{static/adapters/static → ssg/adapters/ssg}/vite.config.mts +2 -2
  72. package/dist/starters/adapters/ssg/package.json +19 -0
  73. package/dist/starters/adapters/vercel-edge/adapters/vercel-edge/vite.config.mts +1 -1
  74. package/dist/starters/adapters/vercel-edge/package.json +1 -1
  75. package/dist/starters/adapters/vercel-edge/src/entry.vercel-edge.tsx +2 -3
  76. package/dist/starters/adapters/vercel-edge/vercel.json +1 -1
  77. package/dist/starters/features/auth/package.json +1 -1
  78. package/dist/starters/features/csr/index.html +23 -0
  79. package/dist/starters/features/csr/package.json +29 -0
  80. package/dist/starters/features/csr/src/root.tsx +15 -0
  81. package/dist/starters/features/csr/vite.config.mts +13 -0
  82. package/dist/starters/features/localize/package.json +3 -3
  83. package/dist/starters/features/localize/src/entry.ssr.tsx +17 -21
  84. package/dist/starters/features/pandacss/package.json +1 -1
  85. package/dist/starters/features/playwright/playwright-report/index.html +953 -911
  86. package/dist/starters/features/postcss/postcss.config.js +1 -1
  87. package/dist/starters/features/tailwind/package.json +2 -2
  88. package/dist/starters/features/tailwind/prettier.config.js +10 -0
  89. package/dist/starters/features/tailwind-v3/package.json +1 -1
  90. package/dist/starters/features/tailwind-v3/prettier.config.js +10 -0
  91. package/dist/testing/index.cjs +7556 -5229
  92. package/dist/testing/index.d.ts +972 -1
  93. package/dist/testing/index.mjs +7456 -5140
  94. package/dist/testing/package.json +1 -1
  95. package/package.json +16 -10
  96. package/public.d.ts +1 -0
  97. package/dist/starters/adapters/static/package.json +0 -19
  98. package/dist/starters/features/tailwind/.prettierrc.js +0 -3
  99. /package/dist/starters/adapters/{static → ssg}/README.md +0 -0
package/dist/core.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * @qwik.dev/core 2.0.0-beta.1-dev+495e8d9
3
+ * @qwik.dev/core 2.0.0-beta.10-dev+4669425
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.10-dev+4669425";
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;
@@ -74,6 +81,30 @@
74
81
  return err;
75
82
  };
76
83
 
84
+ /** @private */
85
+ const isSerializableObject = (v) => {
86
+ const proto = Object.getPrototypeOf(v);
87
+ return proto === Object.prototype || proto === Array.prototype || proto === null;
88
+ };
89
+ const isObject = (v) => {
90
+ return typeof v === 'object' && v !== null;
91
+ };
92
+ const isArray = (v) => {
93
+ return Array.isArray(v);
94
+ };
95
+ const isString = (v) => {
96
+ return typeof v === 'string';
97
+ };
98
+ const isNumber$1 = (v) => {
99
+ return typeof v === 'number';
100
+ };
101
+ const isFunction = (v) => {
102
+ return typeof v === 'function';
103
+ };
104
+ const isPrimitive = (v) => {
105
+ return typeof v !== 'object' && typeof v !== 'function' && v !== null && v !== undefined;
106
+ };
107
+
77
108
  const codeToText = (code, ...parts) => {
78
109
  if (qDev) {
79
110
  // Keep one error, one line to make it easier to search for the error message.
@@ -118,7 +149,7 @@
118
149
  if (parts.length) {
119
150
  text = text.replaceAll(/{{(\d+)}}/g, (_, index) => {
120
151
  let v = parts[index];
121
- if (v && typeof v === 'object' && v.constructor === Object) {
152
+ if (v && isObject(v) && v.constructor === Object) {
122
153
  v = JSON.stringify(v).slice(0, 50);
123
154
  }
124
155
  return v;
@@ -197,9 +228,13 @@
197
228
  ']):not([q\\:container=' +
198
229
  "text" /* QContainerValue.TEXT */ +
199
230
  '])';
231
+ // Node namespaces
200
232
  const HTML_NS = 'http://www.w3.org/1999/xhtml';
201
233
  const SVG_NS = 'http://www.w3.org/2000/svg';
202
234
  const MATH_NS = 'http://www.w3.org/1998/Math/MathML';
235
+ // Attributes namespaces
236
+ const XLINK_NS = 'http://www.w3.org/1999/xlink';
237
+ const XML_NS = 'http://www.w3.org/XML/1998/namespace';
203
238
  const ResourceEvent = 'qResource';
204
239
  const RenderEvent = 'qRender';
205
240
  const TaskEvent = 'qTask';
@@ -265,13 +300,6 @@
265
300
  });
266
301
  });
267
302
  },
268
- nextTick: (fn) => {
269
- return new Promise((resolve) => {
270
- setTimeout(() => {
271
- resolve(fn());
272
- });
273
- });
274
- },
275
303
  chunkForSymbol(symbolName, chunk) {
276
304
  return [symbolName, chunk ?? '_'];
277
305
  },
@@ -379,7 +407,7 @@
379
407
  setTimeout(resolve, timeout);
380
408
  });
381
409
  };
382
- // Retries a function that throws a promise.
410
+ /** Retries a function that throws a promise. */
383
411
  function retryOnPromise(fn, retryCount = 0) {
384
412
  const retryOrThrow = (e) => {
385
413
  if (isPromise(e) && retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
@@ -404,24 +432,6 @@
404
432
  }
405
433
  }
406
434
 
407
- /** @private */
408
- const isSerializableObject = (v) => {
409
- const proto = Object.getPrototypeOf(v);
410
- return proto === Object.prototype || proto === Array.prototype || proto === null;
411
- };
412
- const isObject = (v) => {
413
- return !!v && typeof v === 'object';
414
- };
415
- const isArray = (v) => {
416
- return Array.isArray(v);
417
- };
418
- const isString = (v) => {
419
- return typeof v === 'string';
420
- };
421
- const isFunction = (v) => {
422
- return typeof v === 'function';
423
- };
424
-
425
435
  const ASSERT_DISCLAIMER = 'Internal assert, this is likely caused by a bug in Qwik: ';
426
436
  function assertDefined(value, text, ...parts) {
427
437
  if (qDev) {
@@ -547,6 +557,13 @@
547
557
  this.$container$ = container;
548
558
  this.$untrackedValue$ = value;
549
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
+ }
550
567
  get untrackedValue() {
551
568
  return this.$untrackedValue$;
552
569
  }
@@ -560,14 +577,7 @@
560
577
  set value(value) {
561
578
  if (value !== this.$untrackedValue$) {
562
579
  this.$untrackedValue$ = value;
563
- // TODO: move this to the scheduler
564
- triggerEffects(this.$container$, this, this.$effects$);
565
- // this.$container$?.$scheduler$(
566
- // ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS,
567
- // null,
568
- // this,
569
- // this.$effects$
570
- // );
580
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
571
581
  }
572
582
  }
573
583
  // prevent accidental use as value
@@ -629,23 +639,19 @@
629
639
  $funcStr$;
630
640
  $flags$;
631
641
  $hostElement$ = null;
632
- $forceRunEffects$ = false;
633
642
  [_EFFECT_BACK_REF] = null;
634
643
  constructor(container, fn, args, fnStr,
635
644
  // We need a separate flag to know when the computation needs running because
636
645
  // we need the old value to know if effects need running after computation
637
- flags = 1 /* SignalFlags.INVALID */ | 2 /* WrappedSignalFlags.UNWRAP */) {
646
+ flags = 1 /* SignalFlags.INVALID */ | 4 /* WrappedSignalFlags.UNWRAP */) {
638
647
  super(container, NEEDS_COMPUTATION);
639
648
  this.$args$ = args;
640
649
  this.$func$ = fn;
641
650
  this.$funcStr$ = fnStr;
642
651
  this.$flags$ = flags;
643
652
  }
644
- $invalidate$() {
653
+ invalidate() {
645
654
  this.$flags$ |= 1 /* SignalFlags.INVALID */;
646
- this.$forceRunEffects$ = false;
647
- // We should only call subscribers if the calculation actually changed.
648
- // Therefore, we need to calculate the value now.
649
655
  this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, this.$hostElement$, this, this.$effects$);
650
656
  }
651
657
  /**
@@ -653,30 +659,26 @@
653
659
  * remained the same object.
654
660
  */
655
661
  force() {
656
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
657
- this.$forceRunEffects$ = false;
658
- triggerEffects(this.$container$, this, this.$effects$);
662
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
663
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, this.$hostElement$, this, this.$effects$);
659
664
  }
660
665
  get untrackedValue() {
661
- const didChange = this.$computeIfNeeded$();
662
- if (didChange) {
663
- this.$forceRunEffects$ = didChange;
664
- }
666
+ this.$computeIfNeeded$();
665
667
  assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
666
668
  return this.$untrackedValue$;
667
669
  }
668
670
  $computeIfNeeded$() {
669
671
  if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
670
- return false;
672
+ return;
671
673
  }
672
674
  const untrackedValue = trackSignal(() => this.$func$(...this.$args$), this, "." /* EffectProperty.VNODE */, this.$container$);
673
- // TODO: we should remove invalid flag here
675
+ // TODO: we should remove invalid flag here, but some tests are failing
674
676
  // this.$flags$ &= ~SignalFlags.INVALID;
675
677
  const didChange = untrackedValue !== this.$untrackedValue$;
676
678
  if (didChange) {
679
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
677
680
  this.$untrackedValue$ = untrackedValue;
678
681
  }
679
- return didChange;
680
682
  }
681
683
  // Make this signal read-only
682
684
  set value(_) {
@@ -713,7 +715,6 @@
713
715
  }
714
716
 
715
717
  let _context;
716
- /** @public */
717
718
  const tryGetInvokeContext = () => {
718
719
  if (!_context) {
719
720
  const context = typeof document !== 'undefined' && document && document.__q_context__;
@@ -734,6 +735,7 @@
734
735
  }
735
736
  return ctx;
736
737
  };
738
+ /** @internal */
737
739
  const useInvokeContext = () => {
738
740
  const ctx = tryGetInvokeContext();
739
741
  if (!ctx || ctx.$event$ !== RenderEvent) {
@@ -779,7 +781,7 @@
779
781
  // TODO how about putting url and locale (and event/custom?) in to a "static" object
780
782
  const newInvokeContext = (locale, hostElement, element, event, url) => {
781
783
  // ServerRequestEvent has .locale, but it's not always defined.
782
- const $locale$ = locale || (typeof event === 'object' && event && 'locale' in event ? event.locale : undefined);
784
+ const $locale$ = locale || (event && isObject(event) && 'locale' in event ? event.locale : undefined);
783
785
  const ctx = {
784
786
  $url$: url,
785
787
  $i$: 0,
@@ -859,17 +861,38 @@
859
861
  }
860
862
  };
861
863
  /** @internal */
864
+ const _getContextContainer = () => {
865
+ const iCtx = tryGetInvokeContext();
866
+ if (iCtx) {
867
+ return iCtx.$container$;
868
+ }
869
+ };
870
+ /** @internal */
862
871
  const _jsxBranch = (input) => {
863
872
  return input;
864
873
  };
865
874
  /** @internal */
866
875
  const _waitUntilRendered = (elm) => {
867
- const containerEl = _getQContainerElement(elm);
868
- if (!containerEl) {
876
+ const container = _getQContainerElement(elm)?.qContainer;
877
+ if (!container) {
869
878
  return Promise.resolve();
870
879
  }
871
- const container = containerEl.qContainer;
872
- 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
+ })();
873
896
  };
874
897
 
875
898
  /**
@@ -947,7 +970,7 @@
947
970
  }
948
971
  return null; // Return null if not matching expected format
949
972
  }
950
- function eventNameToJsxEvent(eventName, prefix, startIdx = 0) {
973
+ function eventNameToJsxEvent(eventName, prefix) {
951
974
  eventName = eventName.charAt(0).toUpperCase() + eventName.substring(1);
952
975
  return prefix + eventName + EVENT_SUFFIX;
953
976
  }
@@ -1216,6 +1239,15 @@
1216
1239
  throw qError(11 /* QError.invalidContext */, [context]);
1217
1240
  }
1218
1241
  };
1242
+ /** @internal */
1243
+ const _resolveContextWithoutSequentialScope = (context) => {
1244
+ const iCtx = getInvokeContext();
1245
+ const hostElement = iCtx.$hostElement$;
1246
+ if (!hostElement) {
1247
+ return undefined;
1248
+ }
1249
+ return iCtx.$container$?.resolveContext(hostElement, context);
1250
+ };
1219
1251
 
1220
1252
  const ERROR_CONTEXT = /*#__PURE__*/ createContextId('qk-error');
1221
1253
  const isRecoverable = (err) => {
@@ -1227,13 +1259,6 @@
1227
1259
  return true;
1228
1260
  };
1229
1261
 
1230
- /**
1231
- * QWIK_VERSION
1232
- *
1233
- * @public
1234
- */
1235
- const version = "2.0.0-beta.1-dev+495e8d9";
1236
-
1237
1262
  /** @internal */
1238
1263
  const EMPTY_ARRAY = [];
1239
1264
  const EMPTY_OBJ = {};
@@ -1374,7 +1399,7 @@
1374
1399
  *
1375
1400
  * @internal
1376
1401
  */
1377
- const queueQRL = (...args) => {
1402
+ const _run = (...args) => {
1378
1403
  // This will already check container
1379
1404
  const [runQrl] = useLexicalScope();
1380
1405
  const context = getInvokeContext();
@@ -1388,7 +1413,58 @@
1388
1413
  if (!scheduler) {
1389
1414
  throw qError(1 /* QError.schedulerNotFound */);
1390
1415
  }
1391
- 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);
1419
+ };
1420
+
1421
+ /** @internal */
1422
+ const mapApp_findIndx = (array, key, start) => {
1423
+ assertTrue(start % 2 === 0, 'Expecting even number.');
1424
+ let bottom = start >> 1;
1425
+ let top = (array.length - 2) >> 1;
1426
+ while (bottom <= top) {
1427
+ const mid = bottom + ((top - bottom) >> 1);
1428
+ const midKey = array[mid << 1];
1429
+ if (midKey === key) {
1430
+ return mid << 1;
1431
+ }
1432
+ if (midKey < key) {
1433
+ bottom = mid + 1;
1434
+ }
1435
+ else {
1436
+ top = mid - 1;
1437
+ }
1438
+ }
1439
+ return (bottom << 1) ^ -1;
1440
+ };
1441
+ /** @internal */
1442
+ const mapArray_set = (array, key, value, start, allowNullValue = false) => {
1443
+ const indx = mapApp_findIndx(array, key, start);
1444
+ if (indx >= 0) {
1445
+ if (value == null && !allowNullValue) {
1446
+ array.splice(indx, 2);
1447
+ }
1448
+ else {
1449
+ array[indx + 1] = value;
1450
+ }
1451
+ }
1452
+ else if (value != null || allowNullValue) {
1453
+ array.splice(indx ^ -1, 0, key, value);
1454
+ }
1455
+ };
1456
+ /** @internal */
1457
+ const mapArray_get = (array, key, start) => {
1458
+ const indx = mapApp_findIndx(array, key, start);
1459
+ if (indx >= 0) {
1460
+ return array[indx + 1];
1461
+ }
1462
+ else {
1463
+ return null;
1464
+ }
1465
+ };
1466
+ const mapArray_has = (array, key, start) => {
1467
+ return mapApp_findIndx(array, key, start) >= 0;
1392
1468
  };
1393
1469
 
1394
1470
  /** @internal */
@@ -1397,6 +1473,8 @@
1397
1473
  const _VAR_PROPS = Symbol('VAR');
1398
1474
  /** @internal @deprecated v1 compat */
1399
1475
  const _IMMUTABLE = Symbol('IMMUTABLE');
1476
+ /** @internal */
1477
+ const _UNINITIALIZED = Symbol('UNINITIALIZED');
1400
1478
 
1401
1479
  // <docs markdown="../../readme.md#implicit$FirstArg">
1402
1480
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
@@ -1461,21 +1539,20 @@
1461
1539
  */
1462
1540
  $computeQrl$;
1463
1541
  $flags$;
1464
- $forceRunEffects$ = false;
1465
1542
  [_EFFECT_BACK_REF] = null;
1466
1543
  constructor(container, fn,
1467
1544
  // We need a separate flag to know when the computation needs running because
1468
1545
  // we need the old value to know if effects need running after computation
1469
- flags = 1 /* SignalFlags.INVALID */) {
1546
+ flags = 1 /* SignalFlags.INVALID */ |
1547
+ 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */) {
1470
1548
  // The value is used for comparison when signals trigger, which can only happen
1471
1549
  // when it was calculated before. Therefore we can pass whatever we like.
1472
1550
  super(container, NEEDS_COMPUTATION);
1473
1551
  this.$computeQrl$ = fn;
1474
1552
  this.$flags$ = flags;
1475
1553
  }
1476
- $invalidate$() {
1554
+ invalidate() {
1477
1555
  this.$flags$ |= 1 /* SignalFlags.INVALID */;
1478
- this.$forceRunEffects$ = false;
1479
1556
  this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
1480
1557
  }
1481
1558
  /**
@@ -1483,20 +1560,17 @@
1483
1560
  * remained the same object
1484
1561
  */
1485
1562
  force() {
1486
- this.$forceRunEffects$ = true;
1487
- this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
1563
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
1564
+ super.force();
1488
1565
  }
1489
1566
  get untrackedValue() {
1490
- const didChange = this.$computeIfNeeded$();
1491
- if (didChange) {
1492
- this.$forceRunEffects$ = didChange;
1493
- }
1567
+ this.$computeIfNeeded$();
1494
1568
  assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
1495
1569
  return this.$untrackedValue$;
1496
1570
  }
1497
1571
  $computeIfNeeded$() {
1498
1572
  if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
1499
- return false;
1573
+ return;
1500
1574
  }
1501
1575
  const computeQrl = this.$computeQrl$;
1502
1576
  throwIfQRLNotResolved(computeQrl);
@@ -1515,9 +1589,12 @@
1515
1589
  this.$flags$ &= ~1 /* SignalFlags.INVALID */;
1516
1590
  const didChange = untrackedValue !== this.$untrackedValue$;
1517
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
+ }
1518
1596
  this.$untrackedValue$ = untrackedValue;
1519
1597
  }
1520
- return didChange;
1521
1598
  }
1522
1599
  finally {
1523
1600
  if (ctx) {
@@ -1543,12 +1620,12 @@
1543
1620
  */
1544
1621
  class SerializerSignalImpl extends ComputedSignalImpl {
1545
1622
  constructor(container, argQrl) {
1546
- super(container, argQrl);
1623
+ super(container, argQrl, 1 /* SignalFlags.INVALID */ | 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
1547
1624
  }
1548
1625
  $didInitialize$ = false;
1549
1626
  $computeIfNeeded$() {
1550
1627
  if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
1551
- return false;
1628
+ return;
1552
1629
  }
1553
1630
  throwIfQRLNotResolved(this.$computeQrl$);
1554
1631
  let arg = this.$computeQrl$.resolved;
@@ -1559,16 +1636,16 @@
1559
1636
  const update = arg.update;
1560
1637
  const currentValue = this.$untrackedValue$ === NEEDS_COMPUTATION ? initial : this.$untrackedValue$;
1561
1638
  const untrackedValue = trackSignal(() => this.$didInitialize$
1562
- ? update?.(currentValue)
1639
+ ? update?.(currentValue) || currentValue
1563
1640
  : deserialize(currentValue), this, "." /* EffectProperty.VNODE */, this.$container$);
1564
1641
  const didChange = (this.$didInitialize$ && untrackedValue !== 'undefined') ||
1565
1642
  untrackedValue !== this.$untrackedValue$;
1566
1643
  this.$flags$ &= -2 /* SignalFlags.INVALID */;
1567
1644
  this.$didInitialize$ = true;
1568
1645
  if (didChange) {
1646
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
1569
1647
  this.$untrackedValue$ = untrackedValue;
1570
1648
  }
1571
- return didChange;
1572
1649
  }
1573
1650
  }
1574
1651
 
@@ -1578,6 +1655,28 @@
1578
1655
  const getStoreTarget = (value) => {
1579
1656
  return value?.[STORE_TARGET] || null;
1580
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
+ };
1581
1680
  /**
1582
1681
  * Get the original object that was wrapped by the store. Useful if you want to clone a store
1583
1682
  * (structuredClone, IndexedDB,...)
@@ -1587,6 +1686,7 @@
1587
1686
  const unwrapStore = (value) => {
1588
1687
  return getStoreTarget(value) || value;
1589
1688
  };
1689
+ /** @internal */
1590
1690
  const isStore = (value) => {
1591
1691
  return STORE_TARGET in value;
1592
1692
  };
@@ -1615,7 +1715,12 @@
1615
1715
  toString() {
1616
1716
  return '[Store]';
1617
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
+ }
1618
1722
  get(target, prop) {
1723
+ // TODO(perf): handle better `slice` calls
1619
1724
  if (typeof prop === 'symbol') {
1620
1725
  if (prop === STORE_TARGET) {
1621
1726
  return target;
@@ -1648,8 +1753,7 @@
1648
1753
  }
1649
1754
  const flags = this.$flags$;
1650
1755
  if (flags & 1 /* StoreFlags.RECURSIVE */ &&
1651
- typeof value === 'object' &&
1652
- value !== null &&
1756
+ isObject(value) &&
1653
1757
  !Object.isFrozen(value) &&
1654
1758
  !isStore(value) &&
1655
1759
  !Object.isFrozen(target)) {
@@ -1679,7 +1783,11 @@
1679
1783
  if (typeof prop != 'string' || !delete target[prop]) {
1680
1784
  return false;
1681
1785
  }
1682
- 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
+ }
1683
1791
  return true;
1684
1792
  }
1685
1793
  has(target, prop) {
@@ -1738,8 +1846,10 @@
1738
1846
  }
1739
1847
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
1740
1848
  target[prop] = value;
1741
- // TODO: trigger effects through the scheduler
1742
- 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
+ }
1743
1853
  }
1744
1854
  function getEffects(target, prop, storeEffects) {
1745
1855
  let effectsToTrigger;
@@ -1827,7 +1937,7 @@
1827
1937
  $loadingEffects$ = null;
1828
1938
  $errorEffects$ = null;
1829
1939
  $destroy$;
1830
- $promiseValue$ = null;
1940
+ $promiseValue$ = NEEDS_COMPUTATION;
1831
1941
  [_EFFECT_BACK_REF] = null;
1832
1942
  constructor(container, fn, flags = 1 /* SignalFlags.INVALID */) {
1833
1943
  super(container, fn, flags);
@@ -1861,18 +1971,23 @@
1861
1971
  get untrackedError() {
1862
1972
  return this.$untrackedError$;
1863
1973
  }
1974
+ invalidate() {
1975
+ super.invalidate();
1976
+ this.$promiseValue$ = NEEDS_COMPUTATION;
1977
+ }
1864
1978
  $computeIfNeeded$() {
1865
1979
  if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
1866
- return false;
1980
+ return;
1867
1981
  }
1868
1982
  const computeQrl = this.$computeQrl$;
1869
1983
  throwIfQRLNotResolved(computeQrl);
1870
1984
  const [cleanup] = cleanupFn(this, (err) => this.$container$?.handleError(err, null));
1871
- const untrackedValue = this.$promiseValue$ ??
1872
- computeQrl.getFn()({
1985
+ const untrackedValue = this.$promiseValue$ === NEEDS_COMPUTATION
1986
+ ? computeQrl.getFn()({
1873
1987
  track: trackFn(this, this.$container$),
1874
1988
  cleanup,
1875
- });
1989
+ })
1990
+ : this.$promiseValue$;
1876
1991
  if (isPromise(untrackedValue)) {
1877
1992
  this.untrackedLoading = true;
1878
1993
  this.untrackedError = null;
@@ -1883,14 +1998,16 @@
1883
1998
  this.untrackedError = null;
1884
1999
  })
1885
2000
  .catch((err) => {
2001
+ this.$promiseValue$ = err;
1886
2002
  this.untrackedLoading = false;
1887
2003
  this.untrackedError = err;
1888
2004
  });
1889
2005
  }
1890
- this.$promiseValue$ = null;
2006
+ this.$promiseValue$ = NEEDS_COMPUTATION;
1891
2007
  this.$flags$ &= -2 /* SignalFlags.INVALID */;
1892
2008
  const didChange = untrackedValue !== this.$untrackedValue$;
1893
2009
  if (didChange) {
2010
+ this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
1894
2011
  this.$untrackedValue$ = untrackedValue;
1895
2012
  }
1896
2013
  return didChange;
@@ -1902,13 +2019,15 @@
1902
2019
  return new SignalImpl(null, value);
1903
2020
  };
1904
2021
  /** @internal */
1905
- const createComputedSignal = (qrl) => {
1906
- throwIfQRLNotResolved(qrl);
1907
- return new ComputedSignalImpl(null, qrl);
2022
+ const createComputedSignal = (qrl, options) => {
2023
+ return new ComputedSignalImpl(options?.container || null, qrl, getComputedSignalFlags(options?.serializationStrategy || 'always'));
2024
+ };
2025
+ /** @internal */
2026
+ const createAsyncComputedSignal = (qrl, options) => {
2027
+ return new AsyncComputedSignalImpl(options?.container || null, qrl, getComputedSignalFlags(options?.serializationStrategy || 'never'));
1908
2028
  };
1909
2029
  /** @internal */
1910
2030
  const createSerializerSignal = (arg) => {
1911
- throwIfQRLNotResolved(arg);
1912
2031
  return new SerializerSignalImpl(null, arg);
1913
2032
  };
1914
2033
 
@@ -1927,11 +2046,22 @@
1927
2046
  * The QRL must be a function which returns the value of the signal. The function must not have side
1928
2047
  * effects, and it must be synchronous.
1929
2048
  *
1930
- * If you need the function to be async, use `useSignal` and `useTask$` instead.
2049
+ * If you need the function to be async, use `useAsyncComputed$` instead.
1931
2050
  *
1932
2051
  * @public
1933
2052
  */
1934
2053
  const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedSignal);
2054
+ /**
2055
+ * Create an async computed signal which is calculated from the given QRL. A computed signal is a
2056
+ * signal which is calculated from other signals or async operation. When the signals change, the
2057
+ * computed signal is recalculated.
2058
+ *
2059
+ * The QRL must be a function which returns the value of the signal. The function must not have side
2060
+ * effects, and it can be async.
2061
+ *
2062
+ * @public
2063
+ */
2064
+ const createAsyncComputed$ = /*#__PURE__*/ implicit$FirstArg(createAsyncComputedSignal);
1935
2065
  /**
1936
2066
  * Create a signal that holds a custom serializable value. See {@link useSerializer$} for more
1937
2067
  * details.
@@ -1965,7 +2095,7 @@
1965
2095
  if (!(obj instanceof AsyncComputedSignalImpl)) {
1966
2096
  assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
1967
2097
  }
1968
- if (obj instanceof WrappedSignalImpl && obj.flags & 2 /* WrappedSignalFlags.UNWRAP */) {
2098
+ if (obj instanceof WrappedSignalImpl && obj.flags & 4 /* WrappedSignalFlags.UNWRAP */) {
1969
2099
  return obj;
1970
2100
  }
1971
2101
  return getWrapped(args);
@@ -1991,17 +2121,6 @@
1991
2121
  // the object is not reactive, so we can just return the value
1992
2122
  return obj[prop];
1993
2123
  };
1994
- /** @internal */
1995
- const _wrapStore = (obj, prop) => {
1996
- const target = getStoreTarget(obj);
1997
- const value = target[prop];
1998
- if (isSignal(value)) {
1999
- return value;
2000
- }
2001
- else {
2002
- return new WrappedSignalImpl(null, getProp, [obj, prop], null, 1 /* SignalFlags.INVALID */);
2003
- }
2004
- };
2005
2124
  /** @internal @deprecated v1 compat */
2006
2125
  const _wrapSignal = (obj, prop) => {
2007
2126
  const r = _wrapProp(obj, prop);
@@ -2037,7 +2156,7 @@
2037
2156
  return !prop.startsWith('q:') && !prop.startsWith(NON_SERIALIZABLE_MARKER_PREFIX);
2038
2157
  }
2039
2158
  /** @internal */
2040
- const _restProps = (props, omit, target = {}) => {
2159
+ const _restProps = (props, omit = [], target = {}) => {
2041
2160
  let constPropsTarget = null;
2042
2161
  const constProps = props[_CONST_PROPS];
2043
2162
  if (constProps) {
@@ -2423,7 +2542,6 @@
2423
2542
  if (!isInlineComponent) {
2424
2543
  container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
2425
2544
  container.setHostProp(renderHost, USE_ON_LOCAL_SEQ_IDX, null);
2426
- container.setHostProp(renderHost, ELEMENT_PROPS, props);
2427
2545
  }
2428
2546
  if (vnode_isVNode(renderHost)) {
2429
2547
  clearAllEffects(container, renderHost);
@@ -2437,75 +2555,83 @@
2437
2555
  return jsx;
2438
2556
  }, (err) => {
2439
2557
  if (isPromise(err) && retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
2440
- return err.then(() => executeComponentWithPromiseExceptionRetry(retryCount++));
2558
+ return err.then(() => executeComponentWithPromiseExceptionRetry(++retryCount));
2441
2559
  }
2442
2560
  else {
2561
+ if (retryCount >= MAX_RETRY_ON_PROMISE_COUNT) {
2562
+ throw new Error(`Max retry count of component execution reached`);
2563
+ }
2443
2564
  throw err;
2444
2565
  }
2445
2566
  });
2446
2567
  return executeComponentWithPromiseExceptionRetry();
2447
2568
  };
2448
2569
  /**
2449
- * Stores the JSX output of the last execution of the component.
2450
- *
2451
- * Component can execute multiple times because:
2452
- *
2453
- * - Component can have multiple tasks
2454
- * - Tasks can track signals
2455
- * - Task A can change signal which causes Task B to rerun.
2570
+ * Adds `useOn` events to the JSX output.
2456
2571
  *
2457
- * So when executing a component we only care about its last JSX Output.
2572
+ * @param jsx The JSX output to modify.
2573
+ * @param useOnEvents The `useOn` events to add.
2574
+ * @returns The modified JSX output.
2458
2575
  */
2459
2576
  function addUseOnEvents(jsx, useOnEvents) {
2460
- const jsxElement = findFirstStringJSX(jsx);
2577
+ const jsxElement = findFirstElementNode(jsx);
2461
2578
  let jsxResult = jsx;
2579
+ const qVisibleEvent = 'onQvisible$';
2462
2580
  return maybeThen(jsxElement, (jsxElement) => {
2463
- let isInvisibleComponent = false;
2464
- if (!jsxElement) {
2465
- /**
2466
- * We did not find any jsx node with a string tag. This means that we should append:
2467
- *
2468
- * ```html
2469
- * <script type="placeholder" hidden on-document:qinit="..."></script>
2470
- * ```
2471
- *
2472
- * This is needed because use on events should have a node to attach them to.
2473
- */
2474
- isInvisibleComponent = true;
2475
- }
2581
+ // headless components are components that don't render a real DOM element
2582
+ const isHeadless = !jsxElement;
2583
+ // placeholder element is a <script> element that is used to add events to the document or window
2584
+ let placeholderElement = null;
2476
2585
  for (const key in useOnEvents) {
2477
2586
  if (Object.prototype.hasOwnProperty.call(useOnEvents, key)) {
2478
- if (isInvisibleComponent) {
2479
- if (key === 'onQvisible$') {
2480
- const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2481
- jsxResult = jsx;
2482
- if (jsxElement) {
2483
- addUseOnEvent(jsxElement, 'document:onQinit$', useOnEvents[key]);
2587
+ let targetElement = jsxElement;
2588
+ let eventKey = key;
2589
+ if (isHeadless) {
2590
+ // if the component is headless, we need to add the event to the placeholder element
2591
+ if (key === qVisibleEvent ||
2592
+ key.startsWith("document:on" /* EventNameJSXScope.document */) ||
2593
+ key.startsWith("window:on" /* EventNameJSXScope.window */)) {
2594
+ if (!placeholderElement) {
2595
+ const [createdElement, newJsx] = injectPlaceholderElement(jsxResult);
2596
+ jsxResult = newJsx;
2597
+ placeholderElement = createdElement;
2484
2598
  }
2599
+ targetElement = placeholderElement;
2485
2600
  }
2486
- else if (key.startsWith('document:') || key.startsWith('window:')) {
2487
- const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2488
- jsxResult = jsx;
2489
- if (jsxElement) {
2490
- addUseOnEvent(jsxElement, key, useOnEvents[key]);
2601
+ else {
2602
+ if (build.isDev) {
2603
+ logWarn('You are trying to add an event "' +
2604
+ key +
2605
+ '" using `useOn` hook, ' +
2606
+ 'but a node to which you can add an event is not found. ' +
2607
+ 'Please make sure that the component has a valid element node. ');
2491
2608
  }
2609
+ continue;
2492
2610
  }
2493
- else if (build.isDev) {
2611
+ }
2612
+ if (targetElement) {
2613
+ if (targetElement.type === 'script' && key === qVisibleEvent) {
2614
+ eventKey = 'document:onQInit$';
2494
2615
  logWarn('You are trying to add an event "' +
2495
2616
  key +
2496
- '" using `useOn` hook, ' +
2617
+ '" using `useVisibleTask$` hook, ' +
2497
2618
  'but a node to which you can add an event is not found. ' +
2498
- 'Please make sure that the component has a valid element node. ');
2619
+ 'Using document:onQInit$ instead.');
2499
2620
  }
2500
- }
2501
- else if (jsxElement) {
2502
- addUseOnEvent(jsxElement, key, useOnEvents[key]);
2621
+ addUseOnEvent(targetElement, eventKey, useOnEvents[key]);
2503
2622
  }
2504
2623
  }
2505
2624
  }
2506
2625
  return jsxResult;
2507
2626
  });
2508
2627
  }
2628
+ /**
2629
+ * Adds an event to the JSX element.
2630
+ *
2631
+ * @param jsxElement The JSX element to add the event to.
2632
+ * @param key The event name.
2633
+ * @param value The event value.
2634
+ */
2509
2635
  function addUseOnEvent(jsxElement, key, value) {
2510
2636
  let props = jsxElement.props;
2511
2637
  if (props === EMPTY_OBJ) {
@@ -2521,7 +2647,13 @@
2521
2647
  propValue.push(...value);
2522
2648
  props[key] = propValue;
2523
2649
  }
2524
- function findFirstStringJSX(jsx) {
2650
+ /**
2651
+ * Finds the first element node in the JSX output.
2652
+ *
2653
+ * @param jsx The JSX output to search.
2654
+ * @returns The first element node or null if no element node is found.
2655
+ */
2656
+ function findFirstElementNode(jsx) {
2525
2657
  const queue = [jsx];
2526
2658
  while (queue.length) {
2527
2659
  const jsx = queue.shift();
@@ -2531,44 +2663,69 @@
2531
2663
  }
2532
2664
  queue.push(jsx.children);
2533
2665
  }
2534
- else if (Array.isArray(jsx)) {
2666
+ else if (isArray(jsx)) {
2535
2667
  queue.push(...jsx);
2536
2668
  }
2537
2669
  else if (isPromise(jsx)) {
2538
- return maybeThen(jsx, (jsx) => findFirstStringJSX(jsx));
2670
+ return maybeThen(jsx, (jsx) => findFirstElementNode(jsx));
2539
2671
  }
2540
2672
  else if (isSignal(jsx)) {
2541
- return findFirstStringJSX(untrack(() => jsx.value));
2673
+ return findFirstElementNode(untrack(() => jsx.value));
2542
2674
  }
2543
2675
  }
2544
2676
  return null;
2545
2677
  }
2546
- function addScriptNodeForInvisibleComponents(jsx) {
2678
+ /**
2679
+ * Injects a placeholder <script> element into the JSX output.
2680
+ *
2681
+ * This is necessary for headless components (components that don't render a real DOM element) to
2682
+ * have an anchor point for `useOn` event listeners that target the document or window.
2683
+ *
2684
+ * @param jsx The JSX output to modify.
2685
+ * @returns A tuple containing the created placeholder element and the modified JSX output.
2686
+ */
2687
+ function injectPlaceholderElement(jsx) {
2688
+ // For regular JSX nodes, we can append the placeholder to its children.
2547
2689
  if (isJSXNode(jsx)) {
2548
- const jsxElement = new JSXNodeImpl('script', {}, {
2549
- type: 'placeholder',
2550
- hidden: '',
2551
- }, null, 3);
2690
+ const placeholder = createPlaceholderScriptNode();
2691
+ // For slots, we can't add children, so we wrap them in a fragment.
2552
2692
  if (jsx.type === Slot) {
2553
- return [jsxElement, _jsxSorted(Fragment, null, null, [jsx, jsxElement], 0, null)];
2693
+ return [placeholder, _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null)];
2554
2694
  }
2555
2695
  if (jsx.children == null) {
2556
- jsx.children = jsxElement;
2696
+ jsx.children = placeholder;
2557
2697
  }
2558
- else if (Array.isArray(jsx.children)) {
2559
- jsx.children.push(jsxElement);
2698
+ else if (isArray(jsx.children)) {
2699
+ jsx.children.push(placeholder);
2560
2700
  }
2561
2701
  else {
2562
- jsx.children = [jsx.children, jsxElement];
2702
+ jsx.children = [jsx.children, placeholder];
2563
2703
  }
2564
- return [jsxElement, jsx];
2704
+ return [placeholder, jsx];
2705
+ }
2706
+ // For primitives, we can't add children, so we wrap them in a fragment.
2707
+ if (isPrimitive(jsx)) {
2708
+ const placeholder = createPlaceholderScriptNode();
2709
+ return [placeholder, _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null)];
2565
2710
  }
2566
- else if (Array.isArray(jsx) && jsx.length) {
2567
- // get first element
2568
- const [jsxElement, _] = addScriptNodeForInvisibleComponents(jsx[0]);
2569
- return [jsxElement, jsx];
2711
+ // For an array of nodes, we inject the placeholder into the first element.
2712
+ if (isArray(jsx) && jsx.length > 0) {
2713
+ const [createdElement, _] = injectPlaceholderElement(jsx[0]);
2714
+ return [createdElement, jsx];
2570
2715
  }
2571
- return [null, null];
2716
+ // For anything else we do nothing.
2717
+ return [null, jsx];
2718
+ }
2719
+ /**
2720
+ * Creates a <script> element with a placeholder type.
2721
+ *
2722
+ * @returns A <script> element with a placeholder type.
2723
+ */
2724
+ function createPlaceholderScriptNode() {
2725
+ return new JSXNodeImpl('script', {}, {
2726
+ type: 'placeholder',
2727
+ hidden: '',
2728
+ }, null, 3);
2572
2729
  }
2573
2730
 
2574
2731
  const applyInlineComponent = (ssr, componentHost, inlineComponentFunction, jsx) => {
@@ -2582,13 +2739,13 @@
2582
2739
  if (srcProps && srcProps.children) {
2583
2740
  delete srcProps.children;
2584
2741
  }
2585
- const scheduler = ssr.$scheduler$;
2586
2742
  host.setProp(OnRenderProp, componentQrl);
2587
2743
  host.setProp(ELEMENT_PROPS, srcProps);
2588
2744
  if (jsx.key !== null) {
2589
2745
  host.setProp(ELEMENT_KEY, jsx.key);
2590
2746
  }
2591
- return scheduler(6 /* ChoreType.COMPONENT */, host, componentQrl, srcProps);
2747
+ const componentChore = ssr.$scheduler$(6 /* ChoreType.COMPONENT */, host, componentQrl, srcProps);
2748
+ return getChorePromise(componentChore);
2592
2749
  };
2593
2750
 
2594
2751
  class ParentComponentData {
@@ -2696,7 +2853,14 @@
2696
2853
  appendQwikInspectorAttribute(jsx, qwikInspectorAttrValue);
2697
2854
  }
2698
2855
  }
2699
- 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);
2700
2864
  if (innerHTML) {
2701
2865
  ssr.htmlNode(innerHTML);
2702
2866
  }
@@ -2812,16 +2976,17 @@
2812
2976
  }
2813
2977
  }
2814
2978
  }
2815
- function varPropsToSsrAttrs(varProps, constProps, serializationCtx, styleScopedId, key) {
2816
- return toSsrAttrs(varProps, constProps, serializationCtx, true, styleScopedId, key);
2979
+ function varPropsToSsrAttrs(varProps, constProps, options) {
2980
+ return toSsrAttrs(varProps, constProps, false, options);
2817
2981
  }
2818
- function constPropsToSsrAttrs(constProps, varProps, serializationCtx, styleScopedId) {
2819
- return toSsrAttrs(constProps, varProps, serializationCtx, false, styleScopedId);
2982
+ function constPropsToSsrAttrs(constProps, varProps, options) {
2983
+ return toSsrAttrs(constProps, varProps, true, options);
2820
2984
  }
2821
- function toSsrAttrs(record, anotherRecord, serializationCtx, pushMergedEventProps, styleScopedId, key) {
2985
+ function toSsrAttrs(record, anotherRecord, isConst, options) {
2822
2986
  if (record == null) {
2823
2987
  return null;
2824
2988
  }
2989
+ const pushMergedEventProps = !isConst;
2825
2990
  const ssrAttrs = [];
2826
2991
  for (const key in record) {
2827
2992
  let value = record[key];
@@ -2858,7 +3023,7 @@
2858
3023
  }
2859
3024
  }
2860
3025
  }
2861
- const eventValue = setEvent(serializationCtx, key, value);
3026
+ const eventValue = setEvent(options.serializationCtx, key, value);
2862
3027
  if (eventValue) {
2863
3028
  ssrAttrs.push(jsxEventToHtmlAttribute(key), eventValue);
2864
3029
  }
@@ -2868,7 +3033,7 @@
2868
3033
  // write signal as is. We will track this signal inside `writeAttrs`
2869
3034
  if (isClassAttr(key)) {
2870
3035
  // additionally append styleScopedId for class attr
2871
- ssrAttrs.push(key, [value, styleScopedId]);
3036
+ ssrAttrs.push(key, [value, options.styleScopedId]);
2872
3037
  }
2873
3038
  else {
2874
3039
  ssrAttrs.push(key, value);
@@ -2876,13 +3041,13 @@
2876
3041
  continue;
2877
3042
  }
2878
3043
  if (isPreventDefault(key)) {
2879
- addPreventDefaultEventToSerializationContext(serializationCtx, key);
3044
+ addPreventDefaultEventToSerializationContext(options.serializationCtx, key);
2880
3045
  }
2881
- value = serializeAttribute(key, value, styleScopedId);
3046
+ value = serializeAttribute(key, value, options.styleScopedId);
2882
3047
  ssrAttrs.push(key, value);
2883
3048
  }
2884
- if (key != null) {
2885
- ssrAttrs.push(ELEMENT_KEY, key);
3049
+ if (options.key != null) {
3050
+ ssrAttrs.push(ELEMENT_KEY, options.key);
2886
3051
  }
2887
3052
  return ssrAttrs;
2888
3053
  }
@@ -2931,7 +3096,7 @@
2931
3096
  * For internal qrls (starting with `_`) we assume that they do the right thing.
2932
3097
  */
2933
3098
  if (!qrl.$symbol$.startsWith('_') && (qrl.$captureRef$ || qrl.$capture$)) {
2934
- qrl = createQRL(null, '_run', queueQRL, null, null, [qrl]);
3099
+ qrl = createQRL(null, '_run', _run, null, null, [qrl]);
2935
3100
  }
2936
3101
  return qrlToString(serializationCtx, qrl);
2937
3102
  };
@@ -3011,10 +3176,9 @@
3011
3176
  // deleted and we need to be able to release the task subscriptions.
3012
3177
  set(task);
3013
3178
  const container = iCtx.$container$;
3014
- const promise = container.$scheduler$(3 /* ChoreType.TASK */, task);
3015
- if (isPromise(promise)) {
3016
- // TODO: should we handle this differently?
3017
- promise.catch(() => { });
3179
+ const result = runTask(task, container, iCtx.$hostElement$);
3180
+ if (isPromise(result)) {
3181
+ throw result;
3018
3182
  }
3019
3183
  };
3020
3184
  const runTask = (task, container, host) => {
@@ -3026,7 +3190,7 @@
3026
3190
  const track = trackFn(task, container);
3027
3191
  const [cleanup] = cleanupFn(task, (reason) => container.handleError(reason, host));
3028
3192
  const taskApi = { track, cleanup };
3029
- const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
3193
+ return safeCall(() => taskFn(taskApi), cleanup, (err) => {
3030
3194
  // If a Promise is thrown, that means we need to re-run the task.
3031
3195
  if (isPromise(err)) {
3032
3196
  return err.then(() => runTask(task, container, host));
@@ -3035,7 +3199,6 @@
3035
3199
  throw err;
3036
3200
  }
3037
3201
  });
3038
- return result;
3039
3202
  };
3040
3203
  const cleanupTask = (task) => {
3041
3204
  const destroy = task.$destroy$;
@@ -3066,6 +3229,7 @@
3066
3229
  this.$destroy$ = $destroy$;
3067
3230
  }
3068
3231
  }
3232
+ /** @internal */
3069
3233
  const isTask = (value) => {
3070
3234
  return value instanceof Task;
3071
3235
  };
@@ -3076,7 +3240,7 @@
3076
3240
  */
3077
3241
  const scheduleTask = (_event, element) => {
3078
3242
  const [task] = useLexicalScope();
3079
- 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 */;
3080
3244
  const container = getDomContainer(element);
3081
3245
  container.$scheduler$(type, task);
3082
3246
  };
@@ -3328,7 +3492,7 @@
3328
3492
  ? this.$constProps$[prop]
3329
3493
  : this.$varProps$[prop];
3330
3494
  // a proxied value that the optimizer made
3331
- return value instanceof WrappedSignalImpl && value.$flags$ & 2 /* WrappedSignalFlags.UNWRAP */
3495
+ return value instanceof WrappedSignalImpl && value.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */
3332
3496
  ? value.value
3333
3497
  : value;
3334
3498
  }
@@ -3404,48 +3568,23 @@
3404
3568
  const directGetPropsProxyProp = (jsx, prop) => {
3405
3569
  return (jsx.constProps && prop in jsx.constProps ? jsx.constProps[prop] : jsx.varProps[prop]);
3406
3570
  };
3407
-
3408
- const mapApp_findIndx = (array, key, start) => {
3409
- assertTrue(start % 2 === 0, 'Expecting even number.');
3410
- let bottom = start >> 1;
3411
- let top = (array.length - 2) >> 1;
3412
- while (bottom <= top) {
3413
- const mid = bottom + ((top - bottom) >> 1);
3414
- const midKey = array[mid << 1];
3415
- if (midKey === key) {
3416
- return mid << 1;
3417
- }
3418
- if (midKey < key) {
3419
- bottom = mid + 1;
3420
- }
3421
- else {
3422
- top = mid - 1;
3423
- }
3424
- }
3425
- return (bottom << 1) ^ -1;
3426
- };
3427
- const mapArray_set = (array, key, value, start) => {
3428
- const indx = mapApp_findIndx(array, key, start);
3429
- if (indx >= 0) {
3430
- if (value == null) {
3431
- array.splice(indx, 2);
3432
- }
3433
- else {
3434
- array[indx + 1] = value;
3435
- }
3436
- }
3437
- else if (value != null) {
3438
- array.splice(indx ^ -1, 0, key, value);
3571
+ /** @internal */
3572
+ const _getVarProps = (props) => {
3573
+ if (!props) {
3574
+ return null;
3439
3575
  }
3576
+ return _VAR_PROPS in props
3577
+ ? 'children' in props
3578
+ ? { ...props[_VAR_PROPS], children: props.children }
3579
+ : props[_VAR_PROPS]
3580
+ : props;
3440
3581
  };
3441
- const mapArray_get = (array, key, start) => {
3442
- const indx = mapApp_findIndx(array, key, start);
3443
- if (indx >= 0) {
3444
- return array[indx + 1];
3445
- }
3446
- else {
3582
+ /** @internal */
3583
+ const _getConstProps = (props) => {
3584
+ if (!props) {
3447
3585
  return null;
3448
3586
  }
3587
+ return _CONST_PROPS in props ? props[_CONST_PROPS] : null;
3449
3588
  };
3450
3589
 
3451
3590
  const isForeignObjectElement = (elementName) => {
@@ -3651,6 +3790,24 @@
3651
3790
  elementNamespaceFlag,
3652
3791
  };
3653
3792
  }
3793
+ function getAttributeNamespace(attributeName) {
3794
+ switch (attributeName) {
3795
+ case 'xlink:href':
3796
+ case 'xlink:actuate':
3797
+ case 'xlink:arcrole':
3798
+ case 'xlink:role':
3799
+ case 'xlink:show':
3800
+ case 'xlink:title':
3801
+ case 'xlink:type':
3802
+ return XLINK_NS;
3803
+ case 'xml:base':
3804
+ case 'xml:lang':
3805
+ case 'xml:space':
3806
+ return XML_NS;
3807
+ default:
3808
+ return null;
3809
+ }
3810
+ }
3654
3811
 
3655
3812
  function escapeHTML(html) {
3656
3813
  let escapedHTML = '';
@@ -3749,6 +3906,10 @@
3749
3906
  vNewNode = null;
3750
3907
  vCurrent = vnode_getFirstChild(vStartNode);
3751
3908
  stackPush(jsxNode, true);
3909
+ if (vParent[0 /* VNodeProps.flags */] & 32 /* VNodeFlags.Deleted */) {
3910
+ // Ignore diff if the parent is deleted.
3911
+ return;
3912
+ }
3752
3913
  while (stack.length) {
3753
3914
  while (jsxIdx < jsxCount) {
3754
3915
  assertFalse(vParent === vCurrent, "Parent and current can't be the same");
@@ -4177,8 +4338,10 @@
4177
4338
  value = trackSignalAndAssignHost(value, vNewNode, key, container, signalData);
4178
4339
  }
4179
4340
  if (key === dangerouslySetInnerHTML) {
4180
- element.innerHTML = value;
4181
- element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
4341
+ if (value) {
4342
+ element.innerHTML = String(value);
4343
+ element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
4344
+ }
4182
4345
  continue;
4183
4346
  }
4184
4347
  if (elementName === 'textarea' && key === 'value') {
@@ -4193,6 +4356,14 @@
4193
4356
  }
4194
4357
  value = serializeAttribute(key, value, scopedStyleIdPrefix);
4195
4358
  if (value != null) {
4359
+ if (vNewNode[0 /* VNodeProps.flags */] & 64 /* VNodeFlags.NS_svg */) {
4360
+ // only svg elements can have namespace attributes
4361
+ const namespace = getAttributeNamespace(key);
4362
+ if (namespace) {
4363
+ element.setAttributeNS(namespace, key, String(value));
4364
+ continue;
4365
+ }
4366
+ }
4196
4367
  element.setAttribute(key, String(value));
4197
4368
  }
4198
4369
  }
@@ -4271,8 +4442,13 @@
4271
4442
  let returnValue = false;
4272
4443
  qrls.flat(2).forEach((qrl) => {
4273
4444
  if (qrl) {
4274
- const value = container.$scheduler$(2 /* ChoreType.RUN_QRL */, vNode, qrl, [event, element]);
4275
- 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
+ }
4276
4452
  }
4277
4453
  });
4278
4454
  return returnValue;
@@ -4542,16 +4718,36 @@
4542
4718
  shouldRender = true;
4543
4719
  }
4544
4720
  if (host) {
4545
- const vNodeProps = vnode_getProp(host, ELEMENT_PROPS, container.$getObjectById$);
4546
- shouldRender = shouldRender || propsDiffer(jsxProps, vNodeProps);
4721
+ let vNodeProps = vnode_getProp(host, ELEMENT_PROPS, container.$getObjectById$);
4722
+ const propsAreDifferent = propsDiffer(jsxProps, vNodeProps);
4723
+ shouldRender = shouldRender || propsAreDifferent;
4547
4724
  if (shouldRender) {
4725
+ if (propsAreDifferent) {
4726
+ if (vNodeProps) {
4727
+ // Reuse the same props instance, qrls can use the current props instance
4728
+ // as a capture ref, so we can't change it.
4729
+ // We need to do this directly, because normally we would subscribe to the signals
4730
+ // if any signal is there.
4731
+ vNodeProps[_CONST_PROPS] = jsxProps[_CONST_PROPS];
4732
+ vNodeProps[_VAR_PROPS] = jsxProps[_VAR_PROPS];
4733
+ }
4734
+ else if (jsxProps) {
4735
+ // If there is no props instance, create a new one.
4736
+ // We can do this because we are not using the props instance for anything else.
4737
+ vnode_setProp(host, ELEMENT_PROPS, jsxProps);
4738
+ vNodeProps = jsxProps;
4739
+ }
4740
+ }
4741
+ // Assign the new QRL instance to the host.
4742
+ // Unfortunately it is created every time, something to fix in the optimizer.
4743
+ vnode_setProp(host, OnRenderProp, componentQRL);
4548
4744
  /**
4549
4745
  * Mark host as not deleted. The host could have been marked as deleted if it there was a
4550
4746
  * cleanup run. Now we found it and want to reuse it, so we need to mark it as not
4551
4747
  * deleted.
4552
4748
  */
4553
4749
  host[0 /* VNodeProps.flags */] &= -33 /* VNodeFlags.Deleted */;
4554
- container.$scheduler$(6 /* ChoreType.COMPONENT */, host, componentQRL, jsxProps);
4750
+ container.$scheduler$(6 /* ChoreType.COMPONENT */, host, componentQRL, vNodeProps);
4555
4751
  }
4556
4752
  }
4557
4753
  descendContentToProject(jsxNode.children, host);
@@ -4754,7 +4950,7 @@
4754
4950
  const task = obj;
4755
4951
  clearAllEffects(container, task);
4756
4952
  if (task.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */) {
4757
- container.$scheduler$(48 /* ChoreType.CLEANUP_VISIBLE */, task);
4953
+ container.$scheduler$(32 /* ChoreType.CLEANUP_VISIBLE */, task);
4758
4954
  }
4759
4955
  else {
4760
4956
  cleanupTask(task);
@@ -4805,7 +5001,16 @@
4805
5001
  */
4806
5002
  const vFirstChild = vnode_getFirstChild(vCursor);
4807
5003
  if (vFirstChild) {
4808
- 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
+ });
4809
5014
  return;
4810
5015
  }
4811
5016
  }
@@ -4882,8 +5087,8 @@
4882
5087
  const resource = createResourceReturn(container, opts);
4883
5088
  const el = iCtx.$hostElement$;
4884
5089
  const task = new Task(8 /* TaskFlags.DIRTY */ | 4 /* TaskFlags.RESOURCE */, i, el, qrl, resource, null);
4885
- container.$scheduler$(3 /* ChoreType.TASK */, task);
4886
5090
  set(resource);
5091
+ runResource(task, container, el);
4887
5092
  return resource;
4888
5093
  };
4889
5094
  // <docs markdown="../readme.md#useResource">
@@ -4951,10 +5156,10 @@
4951
5156
  function getResourceValueAsPromise(props) {
4952
5157
  const resource = props.value;
4953
5158
  if (isResourceReturn(resource)) {
5159
+ // create a subscription for the resource._state changes
5160
+ const state = resource._state;
4954
5161
  const isBrowser = !isServerPlatform();
4955
5162
  if (isBrowser) {
4956
- // create a subscription for the resource._state changes
4957
- const state = resource._state;
4958
5163
  if (state === 'pending' && props.onPending) {
4959
5164
  return Promise.resolve().then(useBindInvokeContext(props.onPending));
4960
5165
  }
@@ -4969,30 +5174,25 @@
4969
5174
  }
4970
5175
  }
4971
5176
  }
4972
- const value = resource.value;
4973
- if (value) {
4974
- return value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
4975
- }
4976
- else {
4977
- // this is temporary value until the `runResource` is executed and promise is assigned to the value
4978
- return Promise.resolve(undefined);
4979
- }
5177
+ return untrack(() => resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
4980
5178
  }
4981
5179
  else if (isPromise(resource)) {
4982
5180
  return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
4983
5181
  }
4984
5182
  else if (isSignal(resource)) {
4985
- return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
5183
+ const value = retryOnPromise(() => resource.value);
5184
+ const promise = isPromise(value) ? value : Promise.resolve(value);
5185
+ return promise.then(useBindInvokeContext(props.onResolved));
4986
5186
  }
4987
5187
  else {
4988
- return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
5188
+ return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved));
4989
5189
  }
4990
5190
  }
4991
5191
  const _createResourceReturn = (opts) => {
4992
5192
  const resource = {
4993
5193
  __brand: 'resource',
4994
5194
  value: undefined,
4995
- loading: isServerPlatform() ? false : true,
5195
+ loading: !isServerPlatform(),
4996
5196
  _resolved: undefined,
4997
5197
  _error: undefined,
4998
5198
  _state: 'pending',
@@ -5043,19 +5243,22 @@
5043
5243
  done = true;
5044
5244
  if (resolved) {
5045
5245
  done = true;
5046
- resource.loading = false;
5047
- resource._state = 'resolved';
5048
- resource._resolved = value;
5049
- resource._error = undefined;
5246
+ resourceTarget.loading = false;
5247
+ resourceTarget._state = 'resolved';
5248
+ resourceTarget._resolved = value;
5249
+ resourceTarget._error = undefined;
5050
5250
  resolve(value);
5051
5251
  }
5052
5252
  else {
5053
5253
  done = true;
5054
- resource.loading = false;
5055
- resource._state = 'rejected';
5056
- resource._error = value;
5254
+ resourceTarget.loading = false;
5255
+ resourceTarget._state = 'rejected';
5256
+ resourceTarget._error = value;
5057
5257
  reject(value);
5058
5258
  }
5259
+ if (!isServerPlatform()) {
5260
+ forceStoreEffects(resource, '_state');
5261
+ }
5059
5262
  return true;
5060
5263
  }
5061
5264
  return false;
@@ -5071,17 +5274,17 @@
5071
5274
  }
5072
5275
  });
5073
5276
  // Execute mutation inside empty invocation
5277
+ // TODO: is it right? why we need to invoke inside context and trigger effects?
5074
5278
  invoke(iCtx, () => {
5075
5279
  // console.log('RESOURCE.pending: ');
5076
5280
  resource._state = 'pending';
5077
5281
  resource.loading = !isServerPlatform();
5078
- const promise = (resource.value = new Promise((r, re) => {
5282
+ resource.value = new Promise((r, re) => {
5079
5283
  resolve = r;
5080
5284
  reject = re;
5081
- }));
5082
- promise.catch(ignoreErrorToPreventNodeFromCrashing);
5285
+ });
5083
5286
  });
5084
- const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
5287
+ const promise = safeCall(() => taskFn(opts), (value) => {
5085
5288
  setState(true, value);
5086
5289
  }, (err) => {
5087
5290
  if (isPromise(err)) {
@@ -5104,10 +5307,6 @@
5104
5307
  }
5105
5308
  return promise;
5106
5309
  };
5107
- const ignoreErrorToPreventNodeFromCrashing = (err) => {
5108
- // ignore error to prevent node from crashing
5109
- // node will crash in promise is rejected and no one is listening to the rejection.
5110
- };
5111
5310
 
5112
5311
  /// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
5113
5312
  const aVNodePath = [];
@@ -5190,11 +5389,11 @@
5190
5389
  let bDepth = -1;
5191
5390
  while (a) {
5192
5391
  const ssrNode = (aSsrNodePath[++aDepth] = a);
5193
- a = ssrNode.parentSsrNode;
5392
+ a = ssrNode.parentComponent;
5194
5393
  }
5195
5394
  while (b) {
5196
5395
  const ssrNode = (bSsrNodePath[++bDepth] = b);
5197
- b = ssrNode.parentSsrNode;
5396
+ b = ssrNode.parentComponent;
5198
5397
  }
5199
5398
  while (aDepth >= 0 && bDepth >= 0) {
5200
5399
  a = aSsrNodePath[aDepth];
@@ -5211,6 +5410,210 @@
5211
5410
  return aDepth < bDepth ? -1 : 1;
5212
5411
  };
5213
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
+
5214
5617
  /**
5215
5618
  * Scheduler is responsible for running application code in predictable order.
5216
5619
  *
@@ -5294,29 +5697,43 @@
5294
5697
  */
5295
5698
  // Turn this on to get debug output of what the scheduler is doing.
5296
5699
  const DEBUG = false;
5297
- const getPromise = (chore) => (chore.$promise$ ||= new Promise((resolve) => {
5298
- chore.$resolve$ = resolve;
5299
- }));
5300
- const createScheduler = (container, scheduleDrain, journalFlush) => {
5301
- const choreQueue = [];
5302
- const qrlRuns = [];
5303
- 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;
5304
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);
5305
5730
  return schedule;
5306
5731
  ///// IMPLEMENTATION /////
5307
5732
  function schedule(type, hostOrTask = null, targetOrQrl = null, payload = null) {
5308
- const isServer = !isDomContainer(container);
5309
- const isComponentSsr = isServer && type === 6 /* ChoreType.COMPONENT */;
5310
- const runLater = type !== 255 /* ChoreType.WAIT_FOR_ALL */ && !isComponentSsr && type !== 2 /* ChoreType.RUN_QRL */;
5311
- const isTask = type === 3 /* ChoreType.TASK */ || type === 32 /* ChoreType.VISIBLE */ || type === 48 /* ChoreType.CLEANUP_VISIBLE */;
5312
- const isClientOnly = type === 16 /* ChoreType.JOURNAL_FLUSH */ ||
5313
- type === 4 /* ChoreType.NODE_DIFF */ ||
5314
- type === 5 /* ChoreType.NODE_PROP */ ||
5315
- type === 1 /* ChoreType.QRL_RESOLVE */ ||
5316
- type === 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */;
5317
- if (isServer && isClientOnly) {
5318
- return;
5733
+ if (type === 255 /* ChoreType.WAIT_FOR_QUEUE */ && drainChore) {
5734
+ return drainChore;
5319
5735
  }
5736
+ const isTask = type === 3 /* ChoreType.TASK */ || type === 16 /* ChoreType.VISIBLE */ || type === 32 /* ChoreType.CLEANUP_VISIBLE */;
5320
5737
  if (isTask) {
5321
5738
  hostOrTask.$flags$ |= 8 /* TaskFlags.DIRTY */;
5322
5739
  }
@@ -5330,167 +5747,310 @@
5330
5747
  $host$: isTask ? hostOrTask.$el$ : hostOrTask,
5331
5748
  $target$: targetOrQrl,
5332
5749
  $payload$: isTask ? hostOrTask : payload,
5333
- $resolve$: null,
5334
- $promise$: null,
5750
+ $state$: ChoreState.NONE,
5751
+ $blockedChores$: null,
5752
+ $startTime$: undefined,
5753
+ $endTime$: undefined,
5754
+ $resolve$: undefined,
5755
+ $reject$: undefined,
5335
5756
  $returnValue$: null,
5336
- $executed$: false,
5337
5757
  };
5338
- chore = sortedInsert(choreQueue, chore, container.rootVNode || null);
5339
- if (!drainScheduled && runLater) {
5340
- // If we are not currently draining, we need to schedule a drain.
5341
- drainScheduled = true;
5342
- schedule(16 /* ChoreType.JOURNAL_FLUSH */);
5343
- // Catch here to avoid unhandled promise rejection
5344
- 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
+ }
5345
5797
  }
5346
- // TODO figure out what to do with chore errors
5347
- if (runLater) {
5348
- return getPromise(chore);
5798
+ const blockingChore = findBlockingChore(chore, choreQueue, blockedChores, runningChores, container);
5799
+ if (blockingChore) {
5800
+ addBlockedChore(chore, blockingChore, blockedChores);
5801
+ return chore;
5802
+ }
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();
5349
5807
  }
5350
5808
  else {
5351
- return drainUpTo(chore, isServer);
5352
- }
5353
- }
5354
- /** Execute all of the chores up to and including the given chore. */
5355
- function drainUpTo(runUptoChore, isServer) {
5356
- let maxRetries = 5000;
5357
- while (choreQueue.length) {
5358
- if (maxRetries-- < 0) {
5359
- throw new Error('drainUpTo: max retries reached');
5360
- }
5361
- if (currentChore) {
5362
- // Already running chore, which means we're waiting for async completion
5363
- return getPromise(currentChore)
5364
- .then(() => drainUpTo(runUptoChore, isServer))
5365
- .catch((e) => {
5366
- container.handleError(e, currentChore?.$host$);
5367
- });
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;
5368
5846
  }
5369
- const nextChore = choreQueue[0];
5370
- if (nextChore.$executed$) {
5371
- choreQueue.shift();
5372
- if (nextChore === runUptoChore) {
5373
- 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();
5374
5860
  }
5375
- continue;
5861
+ return false;
5376
5862
  }
5377
- if (vNodeAlreadyDeleted(nextChore) &&
5378
- // we need to process cleanup tasks for deleted nodes
5379
- nextChore.$type$ !== 48 /* ChoreType.CLEANUP_VISIBLE */) {
5380
- choreQueue.shift();
5381
- 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;
5382
5884
  }
5383
- executeChore(nextChore, isServer);
5384
- }
5385
- return runUptoChore.$returnValue$;
5386
- }
5387
- function executeChore(chore, isServer) {
5388
- const host = chore.$host$;
5389
- assertEqual(currentChore, null, 'Chore already running.');
5390
- currentChore = chore;
5391
- let returnValue = null;
5885
+ if (blockedChoresScheduled && !isDraining) {
5886
+ drainInNextTick();
5887
+ }
5888
+ };
5889
+ let currentChore = null;
5392
5890
  try {
5393
- switch (chore.$type$) {
5394
- case 255 /* ChoreType.WAIT_FOR_ALL */:
5395
- {
5396
- if (isServer) {
5397
- drainScheduled = false;
5398
- }
5399
- }
5400
- break;
5401
- case 16 /* ChoreType.JOURNAL_FLUSH */:
5402
- {
5403
- returnValue = journalFlush();
5404
- 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;
5405
5912
  }
5406
- break;
5407
- case 6 /* ChoreType.COMPONENT */:
5408
- {
5409
- returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
5410
- if (isServer) {
5411
- 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();
5412
5943
  }
5413
- else {
5414
- const styleScopedId = container.getHostProp(host, QScopedStyle);
5415
- return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
5944
+ if (!finished && !isDraining) {
5945
+ // if finished, then journal flush is already applied
5946
+ applyJournalFlush();
5416
5947
  }
5417
- }, (err) => container.handleError(err, host));
5418
- }
5419
- break;
5420
- case 2 /* ChoreType.RUN_QRL */:
5421
- {
5422
- const fn = chore.$target$.getFn();
5423
- const result = retryOnPromise(() => fn(...chore.$payload$));
5424
- if (isPromise(result)) {
5425
- const handled = result
5426
- .finally(() => {
5427
- qrlRuns.splice(qrlRuns.indexOf(handled), 1);
5428
- })
5429
- .catch((error) => {
5430
- container.handleError(error, chore.$host$);
5431
- });
5432
- // Don't wait for the promise to resolve
5433
- // TODO come up with a better solution, we also want concurrent signal handling with tasks but serial tasks
5434
- qrlRuns.push(handled);
5435
- DEBUG &&
5436
- debugTrace('execute.DONE (but still running)', chore, currentChore, choreQueue);
5437
- chore.$returnValue$ = handled;
5438
- chore.$resolve$?.(handled);
5439
- currentChore = null;
5440
- chore.$executed$ = true;
5441
- // early out so we don't call after()
5442
- return;
5443
5948
  }
5444
- returnValue = null;
5445
- }
5446
- break;
5447
- case 3 /* ChoreType.TASK */:
5448
- case 32 /* ChoreType.VISIBLE */:
5449
- {
5450
- const payload = chore.$payload$;
5451
- if (payload.$flags$ & 4 /* TaskFlags.RESOURCE */) {
5452
- const result = runResource(payload, container, host);
5453
- // Don't await the return value of the resource, because async resources should not be awaited.
5454
- // The reason for this is that we should be able to update for example a node with loading
5455
- // text. If we await the resource, the loading text will not be displayed until the resource
5456
- // is loaded.
5457
- // Awaiting on the client also causes a deadlock.
5458
- // In any case, the resource will never throw.
5459
- 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;
5460
5993
  }
5461
5994
  else {
5462
- returnValue = runTask(payload, container, host);
5995
+ const styleScopedId = container.getHostProp(host, QScopedStyle);
5996
+ return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
5463
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);
5464
6015
  }
5465
- break;
5466
- case 48 /* ChoreType.CLEANUP_VISIBLE */:
5467
- {
5468
- const task = chore.$payload$;
5469
- cleanupTask(task);
6016
+ else {
6017
+ returnValue = runTask(payload, container, host);
5470
6018
  }
5471
- break;
5472
- case 4 /* ChoreType.NODE_DIFF */:
5473
- {
5474
- const parentVirtualNode = chore.$target$;
5475
- let jsx = chore.$payload$;
5476
- if (isSignal(jsx)) {
5477
- jsx = jsx.value;
5478
- }
5479
- 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;
5480
6033
  }
5481
- break;
5482
- case 5 /* ChoreType.NODE_PROP */:
5483
- {
5484
- const virtualNode = chore.$host$;
5485
- const payload = chore.$payload$;
5486
- let value = payload.$value$;
5487
- if (isSignal(value)) {
5488
- value = value.value;
5489
- }
5490
- const isConst = payload.$isConst$;
5491
- const journal = container.$journal$;
5492
- const property = chore.$idx$;
5493
- 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 {
5494
6054
  if (isConst) {
5495
6055
  const element = virtualNode[6 /* ElementVNodeProps.element */];
5496
6056
  journal.push(2 /* VNodeJournalOpCode.SetAttribute */, element, property, serializedValue);
@@ -5498,63 +6058,49 @@
5498
6058
  else {
5499
6059
  vnode_setAttr(journal, virtualNode, property, serializedValue);
5500
6060
  }
6061
+ returnValue = undefined;
5501
6062
  }
5502
- break;
5503
- case 1 /* ChoreType.QRL_RESOLVE */: {
5504
- {
5505
- const target = chore.$target$;
5506
- returnValue = !target.resolved ? target.resolve() : null;
5507
- }
5508
- break;
5509
6063
  }
5510
- case 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */: {
5511
- {
5512
- const target = chore.$target$;
5513
- const effects = chore.$payload$;
5514
- if (target instanceof ComputedSignalImpl || target instanceof WrappedSignalImpl) {
5515
- const forceRunEffects = target.$forceRunEffects$;
5516
- target.$forceRunEffects$ = false;
5517
- if (!target.$effects$?.size) {
5518
- break;
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) {
6084
+ const ctx = newInvokeContext();
6085
+ ctx.$container$ = container;
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));
5519
6091
  }
5520
- returnValue = retryOnPromise(() => {
5521
- if (target.$computeIfNeeded$() || forceRunEffects) {
5522
- triggerEffects(container, target, effects);
5523
- }
5524
- });
5525
- }
5526
- else {
5527
- returnValue = retryOnPromise(() => {
5528
- triggerEffects(container, target, effects);
5529
- });
5530
- }
6092
+ });
6093
+ }
6094
+ else {
6095
+ returnValue = retryOnPromise(() => {
6096
+ triggerEffects(container, target, effects);
6097
+ });
5531
6098
  }
5532
- break;
5533
6099
  }
6100
+ break;
5534
6101
  }
5535
6102
  }
5536
- catch (e) {
5537
- returnValue = Promise.reject(e);
5538
- }
5539
- const after = (value, error) => {
5540
- currentChore = null;
5541
- chore.$executed$ = true;
5542
- if (error) {
5543
- container.handleError(error, host);
5544
- }
5545
- else {
5546
- chore.$returnValue$ = value;
5547
- chore.$resolve$?.(value);
5548
- }
5549
- };
5550
- if (isPromise(returnValue)) {
5551
- chore.$promise$ = returnValue.then(after, (error) => after(undefined, error));
5552
- chore.$resolve$?.(chore.$promise$);
5553
- chore.$resolve$ = undefined;
5554
- }
5555
- else {
5556
- after(returnValue);
5557
- }
6103
+ return returnValue;
5558
6104
  }
5559
6105
  /**
5560
6106
  * Compares two chores to determine their execution order in the scheduler's queue.
@@ -5583,15 +6129,6 @@
5583
6129
  else {
5584
6130
  assertFalse(vnode_isVNode(aHost), 'expected aHost to be SSRNode but it is a VNode');
5585
6131
  assertFalse(vnode_isVNode(bHost), 'expected bHost to be SSRNode but it is a VNode');
5586
- // we are running on the server.
5587
- // On server we can't schedule task for a different host!
5588
- // Server is SSR, and therefore scheduling for anything but the current host
5589
- // implies that things need to be re-run nad that is not supported because of streaming.
5590
- const errorMessage = `SERVER: during HTML streaming, re-running tasks on a different host is not allowed.
5591
- You are attempting to change a state that has already been streamed to the client.
5592
- This can lead to inconsistencies between Server-Side Rendering (SSR) and Client-Side Rendering (CSR).
5593
- Problematic Node: ${aHost.toString()}`;
5594
- logWarn(errorMessage);
5595
6132
  const hostDiff = ssrNodeDocumentPosition(aHost, bHost);
5596
6133
  if (hostDiff !== 0) {
5597
6134
  return hostDiff;
@@ -5608,12 +6145,21 @@
5608
6145
  return idxDiff;
5609
6146
  }
5610
6147
  // If the host is the same (or missing), and the type is the same, we need to compare the target.
5611
- if (a.$target$ !== b.$target$ || a.$payload$ !== b.$payload$) {
6148
+ if (a.$target$ !== b.$target$) {
6149
+ if (isQrl(a.$target$) && isQrl(b.$target$) && a.$target$.$hash$ === b.$target$.$hash$) {
6150
+ return 0;
6151
+ }
5612
6152
  // 1 means that we are going to process chores as FIFO
5613
6153
  return 1;
5614
6154
  }
5615
- // If the chore is the same as the current chore, we will run it again
5616
- 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$) {
5617
6163
  return 1;
5618
6164
  }
5619
6165
  // The chores are the same and will run only once
@@ -5645,6 +6191,15 @@
5645
6191
  /// We need to ensure that the `queue` is sorted by priority.
5646
6192
  /// 1. Find a place where to insert into.
5647
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
+ }
5648
6203
  if (idx < 0) {
5649
6204
  /// 2. Insert the chore into the queue.
5650
6205
  sortedArray.splice(~idx, 0, value);
@@ -5656,12 +6211,9 @@
5656
6211
  * multiple times during component execution. For this reason it is necessary for us to update
5657
6212
  * the chore with the latest result of the signal.
5658
6213
  */
5659
- if (existing.$type$ === 4 /* ChoreType.NODE_DIFF */) {
6214
+ if (existing.$payload$ !== value.$payload$) {
5660
6215
  existing.$payload$ = value.$payload$;
5661
6216
  }
5662
- if (existing.$executed$) {
5663
- existing.$executed$ = false;
5664
- }
5665
6217
  return existing;
5666
6218
  }
5667
6219
  };
@@ -5673,6 +6225,25 @@
5673
6225
  vnode_isVNode(chore.$host$) &&
5674
6226
  chore.$host$[0 /* VNodeProps.flags */] & 32 /* VNodeFlags.Deleted */);
5675
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
+ }
5676
6247
  function debugChoreTypeToString(type) {
5677
6248
  return ({
5678
6249
  [1 /* ChoreType.QRL_RESOLVE */]: 'QRL_RESOLVE',
@@ -5682,31 +6253,87 @@
5682
6253
  [5 /* ChoreType.NODE_PROP */]: 'NODE_PROP',
5683
6254
  [6 /* ChoreType.COMPONENT */]: 'COMPONENT',
5684
6255
  [7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */]: 'RECOMPUTE_SIGNAL',
5685
- [16 /* ChoreType.JOURNAL_FLUSH */]: 'JOURNAL_FLUSH',
5686
- [32 /* ChoreType.VISIBLE */]: 'VISIBLE',
5687
- [48 /* ChoreType.CLEANUP_VISIBLE */]: 'CLEANUP_VISIBLE',
5688
- [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',
5689
6259
  }[type] || 'UNKNOWN: ' + type);
5690
6260
  }
5691
- function debugChoreToString(chore) {
5692
- const type = debugChoreTypeToString(chore.$type$);
5693
- const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
5694
- const qrlTarget = chore.$target$?.$symbol$;
5695
- return `Chore(${type} ${chore.$type$ === 1 /* ChoreType.QRL_RESOLVE */ || chore.$type$ === 2 /* ChoreType.RUN_QRL */ ? qrlTarget : host} ${chore.$idx$})`;
5696
- }
5697
- function debugTrace(action, arg, currentChore, queue) {
5698
- const lines = ['===========================\nScheduler: ' + action];
5699
- if (arg && !('$type$' in arg)) {
5700
- lines.push(' arg: ' + String(arg).replaceAll(/\n.*/gim, ''));
5701
- }
5702
- if (queue) {
5703
- queue.forEach((chore) => {
5704
- const active = chore === arg ? '>>>' : ' ';
5705
- lines.push(` ${active} > ` +
5706
- (chore === currentChore ? '[running] ' : '') +
5707
- 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);
5708
6318
  });
5709
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));
5710
6337
  // eslint-disable-next-line no-console
5711
6338
  console.log(lines.join('\n') + '\n');
5712
6339
  }
@@ -5724,7 +6351,8 @@
5724
6351
  $currentUniqueId$ = 0;
5725
6352
  $instanceHash$ = null;
5726
6353
  $buildBase$ = null;
5727
- constructor(scheduleDrain, journalFlush, serverData, locale) {
6354
+ $flushEpoch$ = 0;
6355
+ constructor(journalFlush, serverData, locale) {
5728
6356
  this.$serverData$ = serverData;
5729
6357
  this.$locale$ = locale;
5730
6358
  this.$version$ = version;
@@ -5732,7 +6360,7 @@
5732
6360
  this.$getObjectById$ = (_id) => {
5733
6361
  throw Error('Not implemented');
5734
6362
  };
5735
- this.$scheduler$ = createScheduler(this, scheduleDrain, journalFlush);
6363
+ this.$scheduler$ = createScheduler(this, journalFlush);
5736
6364
  }
5737
6365
  trackSignalValue(signal, subscriber, property, data) {
5738
6366
  return trackSignalAndAssignHost(signal, subscriber, property, this, data);
@@ -5880,7 +6508,7 @@
5880
6508
  const qContainer = getAttribute.call(node, Q_CONTAINER);
5881
6509
  if (qContainer === null) {
5882
6510
  if (hasAttribute.call(node, Q_SHADOW_ROOT)) {
5883
- return 6 /* NodeType.ELEMENT_SHADOW_ROOT */;
6511
+ return 6 /* NodeType.ELEMENT_SHADOW_ROOT_WRAPPER */;
5884
6512
  }
5885
6513
  const isQElement = hasAttribute.call(node, Q_PROPS_SEPARATOR);
5886
6514
  return isQElement ? 2 /* NodeType.ELEMENT */ : 0 /* NodeType.OTHER */;
@@ -5898,10 +6526,10 @@
5898
6526
  return 16 /* NodeType.COMMENT_IGNORE_START */;
5899
6527
  }
5900
6528
  else if (nodeValue.startsWith(Q_CONTAINER)) {
5901
- return 5 /* NodeType.COMMENT_SKIP_START */;
6529
+ return 9 /* NodeType.COMMENT_SKIP_START */;
5902
6530
  }
5903
6531
  else if (nodeValue.startsWith(Q_CONTAINER_ISLAND_END)) {
5904
- return 128 /* NodeType.COMMENT_ISLAND_END */;
6532
+ return 64 /* NodeType.COMMENT_ISLAND_END */;
5905
6533
  }
5906
6534
  else if (nodeValue.startsWith(Q_IGNORE_END)) {
5907
6535
  return 32 /* NodeType.COMMENT_IGNORE_END */;
@@ -5948,11 +6576,6 @@
5948
6576
  while (node && (node = node.nextSibling) && getFastNodeType(node) === 0 /* NodeType.OTHER */) { }
5949
6577
  return node;
5950
6578
  };
5951
- const firstChild = (node) => {
5952
- // eslint-disable-next-line no-empty
5953
- while (node && (node = node.firstChild) && getFastNodeType(node) === 0 /* NodeType.OTHER */) { }
5954
- return node;
5955
- };
5956
6579
  /**
5957
6580
  * Process the container
5958
6581
  *
@@ -6013,7 +6636,7 @@
6013
6636
  } while (getFastNodeType(islandNode) !== 65 /* NodeType.COMMENT_ISLAND_START */);
6014
6637
  nextNode = null;
6015
6638
  }
6016
- else if (nodeType === 128 /* NodeType.COMMENT_ISLAND_END */) {
6639
+ else if (nodeType === 64 /* NodeType.COMMENT_ISLAND_END */) {
6017
6640
  nextNode = node;
6018
6641
  do {
6019
6642
  nextNode = walker.nextNode();
@@ -6023,7 +6646,7 @@
6023
6646
  } while (getFastNodeType(nextNode) !== 32 /* NodeType.COMMENT_IGNORE_END */);
6024
6647
  nextNode = null;
6025
6648
  }
6026
- else if (nodeType === 5 /* NodeType.COMMENT_SKIP_START */) {
6649
+ else if (nodeType === 9 /* NodeType.COMMENT_SKIP_START */) {
6027
6650
  // If we are in a container, we need to skip the children.
6028
6651
  nextNode = node;
6029
6652
  do {
@@ -6035,7 +6658,7 @@
6035
6658
  // console.log('EXIT', nextNode?.outerHTML);
6036
6659
  walkContainer(walker, node, node, nextNode, '', null);
6037
6660
  }
6038
- else if (nodeType === 6 /* NodeType.ELEMENT_SHADOW_ROOT */) {
6661
+ else if (nodeType === 6 /* NodeType.ELEMENT_SHADOW_ROOT_WRAPPER */) {
6039
6662
  // If we are in a shadow root, we need to get the shadow root element.
6040
6663
  nextNode = nextSibling(node);
6041
6664
  const shadowRootContainer = node;
@@ -6043,7 +6666,7 @@
6043
6666
  if (shadowRoot) {
6044
6667
  walkContainer(
6045
6668
  // we need to create a new walker for the shadow root
6046
- document.createTreeWalker(shadowRoot, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */), null, firstChild(shadowRoot), null, '', null);
6669
+ document.createTreeWalker(shadowRoot, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */), null, shadowRoot, null, '', null);
6047
6670
  }
6048
6671
  }
6049
6672
  if ((nodeType & 2 /* NodeType.ELEMENT */) === 2 /* NodeType.ELEMENT */) {
@@ -6100,6 +6723,7 @@
6100
6723
  }
6101
6724
 
6102
6725
  /** @file Public APIs for the SSR */
6726
+ // @ts-expect-error we don't have types for the preloader
6103
6727
  /** @public */
6104
6728
  function getDomContainer(element) {
6105
6729
  const qContainerElement = _getQContainerElement(element);
@@ -6119,7 +6743,7 @@
6119
6743
  /** @internal */
6120
6744
  function _getQContainerElement(element) {
6121
6745
  const qContainerElement = Array.isArray(element)
6122
- ? vnode_getDomParent(element)
6746
+ ? vnode_getDomParent(element, true)
6123
6747
  : element;
6124
6748
  return qContainerElement.closest(QContainerSelector);
6125
6749
  }
@@ -6134,19 +6758,20 @@
6134
6758
  rootVNode;
6135
6759
  document;
6136
6760
  $journal$;
6137
- renderDone = null;
6138
6761
  $rawStateData$;
6139
6762
  $storeProxyMap$ = new WeakMap();
6140
6763
  $qFuncs$;
6141
6764
  $instanceHash$;
6142
6765
  $forwardRefs$ = null;
6143
- $initialQRLsIndexes$ = null;
6766
+ $initialQRLs$ = null;
6144
6767
  vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
6145
6768
  $stateData$;
6146
6769
  $styleIds$ = null;
6147
- $renderCount$ = 0;
6148
6770
  constructor(element) {
6149
- super(() => this.scheduleRender(), () => vnode_applyJournal(this.$journal$), {}, element.getAttribute(QLocaleAttr));
6771
+ super(() => {
6772
+ this.$flushEpoch$++;
6773
+ vnode_applyJournal(this.$journal$);
6774
+ }, {}, element.getAttribute(QLocaleAttr));
6150
6775
  this.qContainer = element.getAttribute(QContainerAttr);
6151
6776
  if (!this.qContainer) {
6152
6777
  throw qError(25 /* QError.elementWithoutContainer */);
@@ -6188,20 +6813,17 @@
6188
6813
  }
6189
6814
  handleError(err, host) {
6190
6815
  if (qDev && host) {
6191
- // Clean vdom
6192
6816
  if (typeof document !== 'undefined') {
6193
6817
  const vHost = host;
6194
- const errorDiv = document.createElement('errored-host');
6195
- if (err && err instanceof Error) {
6196
- errorDiv.props = { error: err };
6197
- }
6198
- errorDiv.setAttribute('q:key', '_error_');
6199
6818
  const journal = [];
6200
- const vErrorDiv = vnode_newElement(errorDiv, 'errored-host');
6201
- vnode_getDOMChildNodes(journal, vHost, true).forEach((child) => {
6202
- vnode_insertBefore(journal, vErrorDiv, child, null);
6203
- });
6204
- vnode_insertBefore(journal, vHost, vErrorDiv, null);
6819
+ const vHostParent = vnode_getParent(vHost);
6820
+ const vHostNextSibling = vnode_getNextSibling(vHost);
6821
+ const vErrorDiv = vnode_createErrorDiv(document, vHost, err, journal);
6822
+ // If the host is an element node, we need to insert the error div into its parent.
6823
+ const insertHost = vnode_isElementVNode(vHost) ? vHostParent || vHost : vHost;
6824
+ // If the host is different then we need to insert errored-host in the same position as the host.
6825
+ const insertBefore = insertHost === vHost ? null : vHostNextSibling;
6826
+ vnode_insertBefore(journal, insertHost, vErrorDiv, insertBefore);
6205
6827
  vnode_applyJournal(journal);
6206
6828
  }
6207
6829
  if (err && err instanceof Error) {
@@ -6221,19 +6843,16 @@
6221
6843
  }
6222
6844
  setContext(host, context, value) {
6223
6845
  let ctx = this.getHostProp(host, QCtxAttr);
6224
- if (!ctx) {
6846
+ if (ctx == null) {
6225
6847
  this.setHostProp(host, QCtxAttr, (ctx = []));
6226
6848
  }
6227
- mapArray_set(ctx, context.id, value, 0);
6849
+ mapArray_set(ctx, context.id, value, 0, true);
6228
6850
  }
6229
6851
  resolveContext(host, contextId) {
6230
6852
  while (host) {
6231
6853
  const ctx = this.getHostProp(host, QCtxAttr);
6232
- if (ctx) {
6233
- const value = mapArray_get(ctx, contextId.id, 0);
6234
- if (value) {
6235
- return value;
6236
- }
6854
+ if (ctx != null && mapArray_has(ctx, contextId.id, 0)) {
6855
+ return mapArray_get(ctx, contextId.id, 0);
6237
6856
  }
6238
6857
  host = this.getParentHost(host);
6239
6858
  }
@@ -6279,29 +6898,6 @@
6279
6898
  }
6280
6899
  return vnode_getProp(vNode, name, getObjectById);
6281
6900
  }
6282
- scheduleRender() {
6283
- this.$renderCount$++;
6284
- this.renderDone ||= getPlatform().nextTick(() => this.processChores());
6285
- return this.renderDone.finally(() => emitEvent('qrender', { instanceHash: this.$instanceHash$, renderCount: this.$renderCount$ }));
6286
- }
6287
- processChores() {
6288
- let renderCount = this.$renderCount$;
6289
- const result = this.$scheduler$(255 /* ChoreType.WAIT_FOR_ALL */);
6290
- if (isPromise(result)) {
6291
- return result.then(async () => {
6292
- while (renderCount !== this.$renderCount$) {
6293
- renderCount = this.$renderCount$;
6294
- await this.$scheduler$(255 /* ChoreType.WAIT_FOR_ALL */);
6295
- }
6296
- this.renderDone = null;
6297
- });
6298
- }
6299
- if (renderCount !== this.$renderCount$) {
6300
- this.processChores();
6301
- return;
6302
- }
6303
- this.renderDone = null;
6304
- }
6305
6901
  ensureProjectionResolved(vNode) {
6306
6902
  if ((vNode[0 /* VNodeProps.flags */] & 16 /* VNodeFlags.Resolved */) === 0) {
6307
6903
  vNode[0 /* VNodeProps.flags */] |= 16 /* VNodeFlags.Resolved */;
@@ -6311,7 +6907,9 @@
6311
6907
  if (isSlotProp(prop)) {
6312
6908
  const value = props[i + 1];
6313
6909
  if (typeof value == 'string') {
6314
- props[i + 1] = this.vNodeLocate(value);
6910
+ const projection = this.vNodeLocate(value);
6911
+ props[i + 1] = projection;
6912
+ vnode_getProp(projection, QSlotParent, (id) => this.vNodeLocate(id));
6315
6913
  }
6316
6914
  }
6317
6915
  }
@@ -6380,11 +6978,14 @@
6380
6978
  * ```
6381
6979
  */
6382
6980
  $scheduleInitialQRLs$() {
6383
- if (this.$initialQRLsIndexes$) {
6384
- for (const index of this.$initialQRLsIndexes$) {
6385
- this.$scheduler$(1 /* ChoreType.QRL_RESOLVE */, null, this.$getObjectById$(index));
6981
+ if (this.$initialQRLs$) {
6982
+ for (const qrl of this.$initialQRLs$) {
6983
+ const match = /#(.*)_([a-zA-Z0-9]+)(\[|$)/.exec(qrl);
6984
+ if (match) {
6985
+ preloader.p(match[2], 0.3);
6986
+ }
6386
6987
  }
6387
- this.$initialQRLsIndexes$ = null;
6988
+ this.$initialQRLs$ = null;
6388
6989
  }
6389
6990
  }
6390
6991
  }
@@ -6432,7 +7033,7 @@
6432
7033
  }
6433
7034
  };
6434
7035
  const triggerEffects = (container, signal, effects) => {
6435
- const isBrowser = isDomContainer(container);
7036
+ const isBrowser = !isServerPlatform();
6436
7037
  if (effects) {
6437
7038
  const scheduleEffect = (effectSubscription) => {
6438
7039
  const consumer = effectSubscription[0 /* EffectSubscriptionProp.CONSUMER */];
@@ -6442,7 +7043,7 @@
6442
7043
  consumer.$flags$ |= 8 /* TaskFlags.DIRTY */;
6443
7044
  let choreType = 3 /* ChoreType.TASK */;
6444
7045
  if (consumer.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */) {
6445
- choreType = 32 /* ChoreType.VISIBLE */;
7046
+ choreType = 16 /* ChoreType.VISIBLE */;
6446
7047
  }
6447
7048
  container.$scheduler$(choreType, consumer);
6448
7049
  }
@@ -6456,7 +7057,7 @@
6456
7057
  container.$scheduler$(1 /* ChoreType.QRL_RESOLVE */, null, consumer.$computeQrl$);
6457
7058
  }
6458
7059
  }
6459
- consumer.$invalidate$();
7060
+ consumer.invalidate();
6460
7061
  }
6461
7062
  else if (property === ":" /* EffectProperty.COMPONENT */) {
6462
7063
  const host = consumer;
@@ -6465,22 +7066,22 @@
6465
7066
  const props = container.getHostProp(host, ELEMENT_PROPS);
6466
7067
  container.$scheduler$(6 /* ChoreType.COMPONENT */, host, qrl, props);
6467
7068
  }
6468
- else if (isBrowser) {
6469
- if (property === "." /* EffectProperty.VNODE */) {
7069
+ else if (property === "." /* EffectProperty.VNODE */) {
7070
+ if (isBrowser) {
6470
7071
  const host = consumer;
6471
7072
  container.$scheduler$(4 /* ChoreType.NODE_DIFF */, host, host, signal);
6472
7073
  }
6473
- else {
6474
- const host = consumer;
6475
- const effectData = effectSubscription[3 /* EffectSubscriptionProp.DATA */];
6476
- if (effectData instanceof SubscriptionData) {
6477
- const data = effectData.data;
6478
- const payload = {
6479
- ...data,
6480
- $value$: signal,
6481
- };
6482
- container.$scheduler$(5 /* ChoreType.NODE_PROP */, host, property, payload);
6483
- }
7074
+ }
7075
+ else {
7076
+ const host = consumer;
7077
+ const effectData = effectSubscription[3 /* EffectSubscriptionProp.DATA */];
7078
+ if (effectData instanceof SubscriptionData) {
7079
+ const data = effectData.data;
7080
+ const payload = {
7081
+ ...data,
7082
+ $value$: signal,
7083
+ };
7084
+ container.$scheduler$(5 /* ChoreType.NODE_PROP */, host, property, payload);
6484
7085
  }
6485
7086
  }
6486
7087
  };
@@ -6491,7 +7092,23 @@
6491
7092
  };
6492
7093
  /** @internal */
6493
7094
  const isSerializerObj = (obj) => {
6494
- return (typeof obj === 'object' && obj !== null && typeof obj[SerializerSymbol] === 'function');
7095
+ return isObject(obj) && typeof obj[SerializerSymbol] === 'function';
7096
+ };
7097
+ const getComputedSignalFlags = (serializationStrategy) => {
7098
+ let flags = 1 /* SignalFlags.INVALID */;
7099
+ switch (serializationStrategy) {
7100
+ // TODO: implement this in the future
7101
+ // case 'auto':
7102
+ // flags |= ComputedSignalFlags.SERIALIZATION_STRATEGY_AUTO;
7103
+ // break;
7104
+ case 'never':
7105
+ flags |= 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
7106
+ break;
7107
+ case 'always':
7108
+ flags |= 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
7109
+ break;
7110
+ }
7111
+ return flags;
6495
7112
  };
6496
7113
 
6497
7114
  const stringifyPath = [];
@@ -6786,6 +7403,7 @@
6786
7403
  const flag = vNode[0 /* VNodeProps.flags */];
6787
7404
  return (flag & 5 /* VNodeFlags.ELEMENT_OR_TEXT_MASK */) !== 0;
6788
7405
  };
7406
+ /** @internal */
6789
7407
  const vnode_isMaterialized = (vNode) => {
6790
7408
  assertDefined(vNode, 'Missing vNode');
6791
7409
  const flag = vNode[0 /* VNodeProps.flags */];
@@ -6793,11 +7411,13 @@
6793
7411
  vNode[4 /* ElementVNodeProps.firstChild */] !== undefined &&
6794
7412
  vNode[5 /* ElementVNodeProps.lastChild */] !== undefined);
6795
7413
  };
7414
+ /** @internal */
6796
7415
  const vnode_isTextVNode = (vNode) => {
6797
7416
  assertDefined(vNode, 'Missing vNode');
6798
7417
  const flag = vNode[0 /* VNodeProps.flags */];
6799
7418
  return (flag & 4 /* VNodeFlags.Text */) === 4 /* VNodeFlags.Text */;
6800
7419
  };
7420
+ /** @internal */
6801
7421
  const vnode_isVirtualVNode = (vNode) => {
6802
7422
  assertDefined(vNode, 'Missing vNode');
6803
7423
  const flag = vNode[0 /* VNodeProps.flags */];
@@ -6835,6 +7455,7 @@
6835
7455
  }
6836
7456
  return '<unknown>';
6837
7457
  };
7458
+ /** @internal */
6838
7459
  const vnode_ensureElementInflated = (vnode) => {
6839
7460
  const flags = vnode[0 /* VNodeProps.flags */];
6840
7461
  if ((flags & 15 /* VNodeFlags.INFLATED_TYPE_MASK */) === 1 /* VNodeFlags.Element */) {
@@ -6876,6 +7497,9 @@
6876
7497
  }
6877
7498
  let vParent = null;
6878
7499
  do {
7500
+ if (callback?.(vCursor, vParent)) {
7501
+ return;
7502
+ }
6879
7503
  const vFirstChild = vnode_getFirstChild(vCursor);
6880
7504
  if (vFirstChild) {
6881
7505
  vCursor = vFirstChild;
@@ -7044,6 +7668,7 @@
7044
7668
  const flags = textVNode[0 /* VNodeProps.flags */];
7045
7669
  if ((flags & 8 /* VNodeFlags.Inflated */) === 0) {
7046
7670
  const parentNode = vnode_getDomParent(vnode);
7671
+ assertDefined(parentNode, 'Missing parent node.');
7047
7672
  const sharedTextNode = textVNode[4 /* TextVNodeProps.node */];
7048
7673
  const doc = parentNode.ownerDocument;
7049
7674
  // Walk the previous siblings and inflate them.
@@ -7202,6 +7827,18 @@
7202
7827
  }
7203
7828
  return length;
7204
7829
  };
7830
+ const vnode_createErrorDiv = (document, host, err, journal) => {
7831
+ const errorDiv = document.createElement('errored-host');
7832
+ if (err && err instanceof Error) {
7833
+ errorDiv.props = { error: err };
7834
+ }
7835
+ errorDiv.setAttribute('q:key', '_error_');
7836
+ const vErrorDiv = vnode_newElement(errorDiv, 'errored-host');
7837
+ vnode_getDOMChildNodes(journal, host, true).forEach((child) => {
7838
+ vnode_insertBefore(journal, vErrorDiv, child, null);
7839
+ });
7840
+ return vErrorDiv;
7841
+ };
7205
7842
  const parseBoolean = (value) => {
7206
7843
  if (value === 'false') {
7207
7844
  return false;
@@ -7340,7 +7977,7 @@
7340
7977
  * unlink the previous or next sibling, we don't know that after "a" node is "b". So we need to
7341
7978
  * find children first (and inflate them).
7342
7979
  */
7343
- const domParentVNode = vnode_getDomParentVNode(parent);
7980
+ const domParentVNode = vnode_getDomParentVNode(parent, false);
7344
7981
  const parentNode = domParentVNode && domParentVNode[6 /* ElementVNodeProps.element */];
7345
7982
  let domChildren = null;
7346
7983
  if (domParentVNode) {
@@ -7389,27 +8026,31 @@
7389
8026
  newChildCurrentParent !== parent)) {
7390
8027
  vnode_remove(journal, newChildCurrentParent, newChild, false);
7391
8028
  }
7392
- let adjustedInsertBefore = null;
7393
- if (insertBefore == null) {
7394
- if (vnode_isVirtualVNode(parent)) {
7395
- // If `insertBefore` is null, than we need to insert at the end of the list.
7396
- // Well, not quite. If the parent is a virtual node, our "last node" is not the same
7397
- // as the DOM "last node". So in that case we need to look for the "next node" from
7398
- // our parent.
7399
- adjustedInsertBefore = vnode_getDomSibling(parent, true, false);
8029
+ const parentIsDeleted = parent[0 /* VNodeProps.flags */] & 32 /* VNodeFlags.Deleted */;
8030
+ // if the parent is deleted, then we don't need to insert the new child
8031
+ if (!parentIsDeleted) {
8032
+ let adjustedInsertBefore = null;
8033
+ if (insertBefore == null) {
8034
+ if (vnode_isVirtualVNode(parent)) {
8035
+ // If `insertBefore` is null, than we need to insert at the end of the list.
8036
+ // Well, not quite. If the parent is a virtual node, our "last node" is not the same
8037
+ // as the DOM "last node". So in that case we need to look for the "next node" from
8038
+ // our parent.
8039
+ adjustedInsertBefore = vnode_getDomSibling(parent, true, false);
8040
+ }
8041
+ }
8042
+ else if (vnode_isVirtualVNode(insertBefore)) {
8043
+ // If the `insertBefore` is virtual, than we need to descend into the virtual and find e actual
8044
+ adjustedInsertBefore = vnode_getDomSibling(insertBefore, true, true);
8045
+ }
8046
+ else {
8047
+ adjustedInsertBefore = insertBefore;
8048
+ }
8049
+ adjustedInsertBefore && vnode_ensureInflatedIfText(journal, adjustedInsertBefore);
8050
+ // Here we know the insertBefore node
8051
+ if (domChildren && domChildren.length) {
8052
+ journal.push(5 /* VNodeJournalOpCode.Insert */, parentNode, vnode_getNode(adjustedInsertBefore), ...domChildren);
7400
8053
  }
7401
- }
7402
- else if (vnode_isVirtualVNode(insertBefore)) {
7403
- // If the `insertBefore` is virtual, than we need to descend into the virtual and find e actual
7404
- adjustedInsertBefore = vnode_getDomSibling(insertBefore, true, true);
7405
- }
7406
- else {
7407
- adjustedInsertBefore = insertBefore;
7408
- }
7409
- adjustedInsertBefore && vnode_ensureInflatedIfText(journal, adjustedInsertBefore);
7410
- // Here we know the insertBefore node
7411
- if (domChildren && domChildren.length) {
7412
- journal.push(5 /* VNodeJournalOpCode.Insert */, parentNode, vnode_getNode(adjustedInsertBefore), ...domChildren);
7413
8054
  }
7414
8055
  // link newChild into the previous/next list
7415
8056
  const vNext = insertBefore;
@@ -7431,14 +8072,22 @@
7431
8072
  newChild[2 /* VNodeProps.previousSibling */] = vPrevious;
7432
8073
  newChild[3 /* VNodeProps.nextSibling */] = vNext;
7433
8074
  newChild[1 /* VNodeProps.parent */] = parent;
8075
+ if (parentIsDeleted) {
8076
+ // if the parent is deleted, then the new child is also deleted
8077
+ newChild[0 /* VNodeProps.flags */] |= 32 /* VNodeFlags.Deleted */;
8078
+ }
7434
8079
  };
7435
- const vnode_getDomParent = (vnode) => {
7436
- vnode = vnode_getDomParentVNode(vnode);
8080
+ const vnode_getDomParent = (vnode, includeProjection = true) => {
8081
+ vnode = vnode_getDomParentVNode(vnode, includeProjection);
7437
8082
  return (vnode && vnode[6 /* ElementVNodeProps.element */]);
7438
8083
  };
7439
- const vnode_getDomParentVNode = (vnode) => {
8084
+ const vnode_getDomParentVNode = (vnode, includeProjection = true) => {
7440
8085
  while (vnode && !vnode_isElementVNode(vnode)) {
7441
- vnode = vnode[1 /* VNodeProps.parent */];
8086
+ vnode =
8087
+ vnode[1 /* VNodeProps.parent */] ||
8088
+ (includeProjection
8089
+ ? vnode_getProp(vnode, QSlotParent, (id) => (vnode_isVNode(id) ? id : null))
8090
+ : null);
7442
8091
  }
7443
8092
  return vnode;
7444
8093
  };
@@ -7448,7 +8097,7 @@
7448
8097
  vnode_ensureTextInflated(journal, vToRemove);
7449
8098
  }
7450
8099
  if (removeDOM) {
7451
- const domParent = vnode_getDomParent(vParent);
8100
+ const domParent = vnode_getDomParent(vParent, false);
7452
8101
  const isInnerHTMLParent = vnode_getAttr(vParent, dangerouslySetInnerHTML);
7453
8102
  if (isInnerHTMLParent) {
7454
8103
  // ignore children, as they are inserted via innerHTML
@@ -7513,6 +8162,7 @@
7513
8162
  const textNode = textVNode[4 /* TextVNodeProps.node */];
7514
8163
  journal.push(1 /* VNodeJournalOpCode.SetText */, textNode, (textVNode[5 /* TextVNodeProps.text */] = text));
7515
8164
  };
8165
+ /** @internal */
7516
8166
  const vnode_getFirstChild = (vnode) => {
7517
8167
  if (vnode_isTextVNode(vnode)) {
7518
8168
  return null;
@@ -7773,27 +8423,46 @@
7773
8423
  return vFirstChild;
7774
8424
  };
7775
8425
  function setEffectBackRefFromVNodeData(vParent, value, container) {
7776
- const deserializedSubMap = container.$getObjectById$(value);
7777
8426
  if (!vParent[_EFFECT_BACK_REF]) {
8427
+ // get data lazily
8428
+ // this is because effects back refs can point to vnodes which are not yet materialized
8429
+ // (are after the current vnode)
7778
8430
  Object.defineProperty(vParent, _EFFECT_BACK_REF, {
7779
- value: deserializedSubMap,
8431
+ get() {
8432
+ const subMap = container.$getObjectById$(value);
8433
+ vParent[_EFFECT_BACK_REF] = subMap;
8434
+ return subMap;
8435
+ },
8436
+ set(value) {
8437
+ Object.defineProperty(vParent, _EFFECT_BACK_REF, {
8438
+ value,
8439
+ writable: true,
8440
+ enumerable: true,
8441
+ configurable: true,
8442
+ });
8443
+ },
8444
+ enumerable: true,
8445
+ configurable: true,
7780
8446
  });
7781
8447
  }
7782
8448
  else {
7783
8449
  const subMap = vParent[_EFFECT_BACK_REF];
7784
- mergeMaps(subMap, deserializedSubMap);
8450
+ mergeMaps(subMap, container.$getObjectById$(value));
7785
8451
  }
7786
8452
  }
7787
8453
  const processVNodeData = (vData, callback) => {
7788
8454
  let nextToConsumeIdx = 0;
7789
8455
  let ch = 0;
7790
8456
  let peekCh = 0;
8457
+ const getChar = (idx) => {
8458
+ return idx < vData.length ? vData.charCodeAt(idx) : 0;
8459
+ };
7791
8460
  const peek = () => {
7792
8461
  if (peekCh !== 0) {
7793
8462
  return peekCh;
7794
8463
  }
7795
8464
  else {
7796
- return (peekCh = nextToConsumeIdx < vData.length ? vData.charCodeAt(nextToConsumeIdx) : 0);
8465
+ return (peekCh = getChar(nextToConsumeIdx));
7797
8466
  }
7798
8467
  };
7799
8468
  const consume = () => {
@@ -7814,15 +8483,17 @@
7814
8483
  return vData.substring(start, nextToConsumeIdx);
7815
8484
  };
7816
8485
  while (peek() !== 0) {
7817
- callback(peek, consumeValue, consume, nextToConsumeIdx);
8486
+ callback(peek, consumeValue, consume, getChar, nextToConsumeIdx);
7818
8487
  }
7819
8488
  };
8489
+ /** @internal */
7820
8490
  const vnode_getNextSibling = (vnode) => {
7821
8491
  return vnode[3 /* VNodeProps.nextSibling */];
7822
8492
  };
7823
8493
  const vnode_getPreviousSibling = (vnode) => {
7824
8494
  return vnode[2 /* VNodeProps.previousSibling */];
7825
8495
  };
8496
+ /** @internal */
7826
8497
  const vnode_getAttrKeys = (vnode) => {
7827
8498
  const type = vnode[0 /* VNodeProps.flags */];
7828
8499
  if ((type & 3 /* VNodeFlags.ELEMENT_OR_VIRTUAL_MASK */) !== 0) {
@@ -7868,6 +8539,7 @@
7868
8539
  }
7869
8540
  }
7870
8541
  };
8542
+ /** @internal */
7871
8543
  const vnode_getAttr = (vnode, key) => {
7872
8544
  const type = vnode[0 /* VNodeProps.flags */];
7873
8545
  if ((type & 3 /* VNodeFlags.ELEMENT_OR_VIRTUAL_MASK */) !== 0) {
@@ -7904,6 +8576,7 @@
7904
8576
  props.splice(idx ^ -1, 0, key, value);
7905
8577
  }
7906
8578
  };
8579
+ /** @internal */
7907
8580
  const vnode_getPropStartIndex = (vnode) => {
7908
8581
  const type = vnode[0 /* VNodeProps.flags */] & 7 /* VNodeFlags.TYPE_MASK */;
7909
8582
  if (type === 1 /* VNodeFlags.Element */) {
@@ -7914,12 +8587,33 @@
7914
8587
  }
7915
8588
  throw qError(26 /* QError.invalidVNodeType */, [type]);
7916
8589
  };
8590
+ /** @internal */
7917
8591
  const vnode_getProps = (vnode) => {
7918
8592
  return vnode[vnode_getPropStartIndex(vnode)];
7919
8593
  };
7920
8594
  const vnode_getParent = (vnode) => {
7921
8595
  return vnode[1 /* VNodeProps.parent */] || null;
7922
8596
  };
8597
+ const vnode_isDescendantOf = (vnode, ancestor, rootVNode) => {
8598
+ let parent = vnode_getParentOrProjectionParent(vnode, rootVNode);
8599
+ while (parent) {
8600
+ if (parent === ancestor) {
8601
+ return true;
8602
+ }
8603
+ parent = vnode_getParentOrProjectionParent(parent, rootVNode);
8604
+ }
8605
+ return false;
8606
+ };
8607
+ const vnode_getParentOrProjectionParent = (vnode, rootVNode) => {
8608
+ if (rootVNode) {
8609
+ const parentProjection = vnode_getProp(vnode, QSlotParent, (id) => vnode_locate(rootVNode, id));
8610
+ if (parentProjection) {
8611
+ // This is a projection, so we need to check the parent of the projection
8612
+ return parentProjection;
8613
+ }
8614
+ }
8615
+ return vnode_getParent(vnode);
8616
+ };
7923
8617
  const vnode_getNode = (vnode) => {
7924
8618
  if (vnode === null || vnode_isVirtualVNode(vnode)) {
7925
8619
  return null;
@@ -8028,21 +8722,19 @@
8028
8722
  let textIdx = 0;
8029
8723
  let combinedText = null;
8030
8724
  let container = null;
8031
- processVNodeData(vData, (peek, consumeValue, consume, nextToConsumeIdx) => {
8725
+ processVNodeData(vData, (peek, consumeValue, consume, getChar, nextToConsumeIdx) => {
8032
8726
  if (isNumber(peek())) {
8033
8727
  // Element counts get encoded as numbers.
8034
- while (!isElement(child)) {
8728
+ while (!isElement(child) ||
8729
+ // We pretend that style element's don't exist as they can get moved out.
8730
+ // skip over style elements, as those need to be moved to the head
8731
+ // and are not included in the counts.
8732
+ isQStyleElement(child)) {
8035
8733
  child = fastNextSibling(child);
8036
8734
  if (!child) {
8037
8735
  throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), nextToConsumeIdx]);
8038
8736
  }
8039
8737
  }
8040
- // We pretend that style element's don't exist as they can get moved out.
8041
- while (isQStyleElement(child)) {
8042
- // skip over style elements, as those need to be moved to the head
8043
- // and are not included in the counts.
8044
- child = fastNextSibling(child);
8045
- }
8046
8738
  combinedText = null;
8047
8739
  previousTextNode = null;
8048
8740
  let value = 0;
@@ -8074,7 +8766,17 @@
8074
8766
  vnode_setAttr(null, vParent, ELEMENT_PROPS, consumeValue());
8075
8767
  }
8076
8768
  else if (peek() === VNodeDataChar.KEY) {
8077
- vnode_setAttr(null, vParent, ELEMENT_KEY, consumeValue());
8769
+ const isEscapedValue = getChar(nextToConsumeIdx + 1) === VNodeDataChar.SEPARATOR;
8770
+ let value;
8771
+ if (isEscapedValue) {
8772
+ consume();
8773
+ value = decodeURI(consumeValue());
8774
+ consume();
8775
+ }
8776
+ else {
8777
+ value = consumeValue();
8778
+ }
8779
+ vnode_setAttr(null, vParent, ELEMENT_KEY, value);
8078
8780
  }
8079
8781
  else if (peek() === VNodeDataChar.SEQ) {
8080
8782
  vnode_setAttr(null, vParent, ELEMENT_SEQ, consumeValue());
@@ -8120,6 +8822,10 @@
8120
8822
  vnode_setAttr(null, vParent, QSlot, consumeValue());
8121
8823
  }
8122
8824
  else {
8825
+ // skip over style elements in front of text nodes, where text node is the first child (except the style node)
8826
+ while (isQStyleElement(child)) {
8827
+ child = fastNextSibling(child);
8828
+ }
8123
8829
  const textNode = child && fastNodeType(child) === /* Node.TEXT_NODE */ 3 ? child : null;
8124
8830
  // must be alphanumeric
8125
8831
  if (combinedText === null) {
@@ -8214,9 +8920,11 @@
8214
8920
  };
8215
8921
 
8216
8922
  /** There's [documentation](./serialization.md) */
8923
+ /** Arrays/Objects are special-cased so their identifiers is a single digit. */
8924
+ const needsInflation = (typeId) => typeId >= 14 /* TypeIds.Error */ || typeId === 4 /* TypeIds.Array */ || typeId === 5 /* TypeIds.Object */;
8217
8925
  const deserializedProxyMap = new WeakMap();
8218
8926
  const isDeserializerProxy = (value) => {
8219
- return typeof value === 'object' && value !== null && SERIALIZER_PROXY_UNWRAP in value;
8927
+ return isObject(value) && SERIALIZER_PROXY_UNWRAP in value;
8220
8928
  };
8221
8929
  const SERIALIZER_PROXY_UNWRAP = Symbol('UNWRAP');
8222
8930
  /** Call this on the serialized root state */
@@ -8261,19 +8969,19 @@
8261
8969
  const idx = i * 2;
8262
8970
  const typeId = this.$data$[idx];
8263
8971
  const value = this.$data$[idx + 1];
8264
- if (typeId === undefined) {
8972
+ if (typeId === 0 /* TypeIds.Plain */) {
8265
8973
  // The value is already cached
8266
8974
  return value;
8267
8975
  }
8268
8976
  const container = this.$container$;
8269
- let propValue = allocate(container, typeId, value);
8270
- /** We stored the reference, so now we can inflate, allowing cycles. */
8271
- if (typeId >= 14 /* TypeIds.Error */) {
8272
- propValue = inflate(container, propValue, typeId, value);
8273
- }
8977
+ const propValue = allocate(container, typeId, value);
8274
8978
  Reflect.set(target, property, propValue);
8275
- this.$data$[idx] = undefined;
8979
+ this.$data$[idx] = 0 /* TypeIds.Plain */;
8276
8980
  this.$data$[idx + 1] = propValue;
8981
+ /** We stored the reference, so now we can inflate, allowing cycles */
8982
+ if (needsInflation(typeId)) {
8983
+ inflate(container, propValue, typeId, value);
8984
+ }
8277
8985
  return propValue;
8278
8986
  }
8279
8987
  has(target, property) {
@@ -8292,7 +9000,7 @@
8292
9000
  return out;
8293
9001
  }
8294
9002
  const idx = i * 2;
8295
- this.$data$[idx] = undefined;
9003
+ this.$data$[idx] = 0 /* TypeIds.Plain */;
8296
9004
  this.$data$[idx + 1] = value;
8297
9005
  return true;
8298
9006
  }
@@ -8310,51 +9018,33 @@
8310
9018
  };
8311
9019
  const resolvers = new WeakMap();
8312
9020
  const inflate = (container, target, typeId, data) => {
8313
- if (typeId === undefined) {
9021
+ if (typeId === 0 /* TypeIds.Plain */) {
8314
9022
  // Already processed
8315
- return target;
9023
+ return;
8316
9024
  }
8317
- // restore the complex data, except for plain objects
8318
- if (typeId !== 15 /* TypeIds.Object */ && Array.isArray(data)) {
9025
+ // Restore the complex data
9026
+ if (Array.isArray(data)) {
8319
9027
  data = _eagerDeserializeArray(container, data);
8320
9028
  }
8321
9029
  switch (typeId) {
8322
- case 15 /* TypeIds.Object */:
8323
- // We use getters for making complex values lazy
8324
- for (let i = 0; i < data.length; i += 4) {
8325
- const key = deserializeData(container, data[i], data[i + 1]);
8326
- const valType = data[i + 2];
8327
- const valData = data[i + 3];
8328
- if (valType === 0 /* TypeIds.RootRef */ || valType >= 14 /* TypeIds.Error */) {
8329
- Object.defineProperty(target, key, {
8330
- get() {
8331
- const value = deserializeData(container, valType, valData);
8332
- // after first deserialize, we can replace the Object.defineProperty with the value
8333
- target[key] = value;
8334
- return value;
8335
- },
8336
- set(value) {
8337
- Object.defineProperty(target, key, {
8338
- value,
8339
- writable: true,
8340
- enumerable: true,
8341
- configurable: true,
8342
- });
8343
- },
8344
- enumerable: true,
8345
- configurable: true,
8346
- });
8347
- }
8348
- else {
8349
- target[key] = deserializeData(container, valType, valData);
8350
- }
9030
+ case 4 /* TypeIds.Array */:
9031
+ for (let i = 0; i < target.length; i++) {
9032
+ // read the value to trigger lazy deserialization
9033
+ target[i];
9034
+ }
9035
+ break;
9036
+ case 5 /* TypeIds.Object */:
9037
+ for (let i = 0; i < data.length; i += 2) {
9038
+ const key = data[i];
9039
+ const value = data[i + 1];
9040
+ target[key] = value;
8351
9041
  }
8352
9042
  break;
8353
- case 20 /* TypeIds.QRL */:
8354
- case 21 /* TypeIds.PreloadQRL */:
9043
+ case 19 /* TypeIds.QRL */:
9044
+ case 20 /* TypeIds.PreloadQRL */:
8355
9045
  inflateQRL(container, target);
8356
9046
  break;
8357
- case 22 /* TypeIds.Task */:
9047
+ case 21 /* TypeIds.Task */:
8358
9048
  const task = target;
8359
9049
  const v = data;
8360
9050
  task.$qrl$ = inflateQRL(container, v[0]);
@@ -8364,7 +9054,7 @@
8364
9054
  task[_EFFECT_BACK_REF] = v[4];
8365
9055
  task.$state$ = v[5];
8366
9056
  break;
8367
- case 23 /* TypeIds.Resource */:
9057
+ case 22 /* TypeIds.Resource */:
8368
9058
  const [resolved, result, effects] = data;
8369
9059
  const resource = target;
8370
9060
  if (resolved) {
@@ -8379,26 +9069,31 @@
8379
9069
  }
8380
9070
  getStoreHandler(target).$effects$ = effects;
8381
9071
  break;
8382
- case 24 /* TypeIds.Component */:
9072
+ case 23 /* TypeIds.Component */:
8383
9073
  target[SERIALIZABLE_STATE][0] = data[0];
8384
9074
  break;
8385
- case 30 /* TypeIds.Store */:
8386
- case 31 /* TypeIds.StoreArray */: {
8387
- const [value, flags, effects] = data;
8388
- const store = getOrCreateStore(value, flags, container);
8389
- const storeHandler = getStoreHandler(store);
9075
+ case 29 /* TypeIds.Store */: {
9076
+ /**
9077
+ * Note that cycles between stores and their targets can cause this inflation to happen on
9078
+ * already inflated stores, but that's ok because the flags and effects are still the same.
9079
+ *
9080
+ * Also note that we don't do anything with the innerstores we added during serialization,
9081
+ * because they are already inflated in the first step of inflate().
9082
+ */
9083
+ const [, flags, effects] = data;
9084
+ const storeHandler = getStoreHandler(target);
9085
+ storeHandler.$flags$ = flags;
8390
9086
  storeHandler.$effects$ = effects;
8391
- target = store;
8392
9087
  break;
8393
9088
  }
8394
- case 25 /* TypeIds.Signal */: {
9089
+ case 24 /* TypeIds.Signal */: {
8395
9090
  const signal = target;
8396
9091
  const d = data;
8397
9092
  signal.$untrackedValue$ = d[0];
8398
9093
  signal.$effects$ = new Set(d.slice(1));
8399
9094
  break;
8400
9095
  }
8401
- case 26 /* TypeIds.WrappedSignal */: {
9096
+ case 25 /* TypeIds.WrappedSignal */: {
8402
9097
  const signal = target;
8403
9098
  const d = data;
8404
9099
  signal.$func$ = container.getSyncFn(d[0]);
@@ -8411,7 +9106,7 @@
8411
9106
  signal.$effects$ = new Set(d.slice(5));
8412
9107
  break;
8413
9108
  }
8414
- case 28 /* TypeIds.AsyncComputedSignal */: {
9109
+ case 27 /* TypeIds.AsyncComputedSignal */: {
8415
9110
  const asyncComputed = target;
8416
9111
  const d = data;
8417
9112
  asyncComputed.$computeQrl$ = d[0];
@@ -8424,14 +9119,12 @@
8424
9119
  if (hasValue) {
8425
9120
  asyncComputed.$untrackedValue$ = d[6];
8426
9121
  }
8427
- else {
8428
- asyncComputed.$flags$ |= 1 /* SignalFlags.INVALID */;
8429
- }
9122
+ asyncComputed.$flags$ |= 1 /* SignalFlags.INVALID */;
8430
9123
  break;
8431
9124
  }
8432
9125
  // Inflating a SerializerSignal is the same as inflating a ComputedSignal
8433
- case 29 /* TypeIds.SerializerSignal */:
8434
- case 27 /* TypeIds.ComputedSignal */: {
9126
+ case 28 /* TypeIds.SerializerSignal */:
9127
+ case 26 /* TypeIds.ComputedSignal */: {
8435
9128
  const computed = target;
8436
9129
  const d = data;
8437
9130
  computed.$computeQrl$ = d[0];
@@ -8440,7 +9133,7 @@
8440
9133
  if (hasValue) {
8441
9134
  computed.$untrackedValue$ = d[2];
8442
9135
  // The serialized signal is always invalid so it can recreate the custom object
8443
- if (typeId === 29 /* TypeIds.SerializerSignal */) {
9136
+ if (typeId === 28 /* TypeIds.SerializerSignal */) {
8444
9137
  computed.$flags$ |= 1 /* SignalFlags.INVALID */;
8445
9138
  }
8446
9139
  }
@@ -8454,7 +9147,7 @@
8454
9147
  */
8455
9148
  // try to download qrl in this tick
8456
9149
  computed.$computeQrl$.resolve();
8457
- container.$scheduler$?.(1 /* ChoreType.QRL_RESOLVE */, null, computed.$computeQrl$);
9150
+ container.$scheduler$(1 /* ChoreType.QRL_RESOLVE */, null, computed.$computeQrl$);
8458
9151
  }
8459
9152
  break;
8460
9153
  }
@@ -8466,7 +9159,7 @@
8466
9159
  }
8467
9160
  break;
8468
9161
  }
8469
- case 32 /* TypeIds.FormData */: {
9162
+ case 30 /* TypeIds.FormData */: {
8470
9163
  const formData = target;
8471
9164
  const d = data;
8472
9165
  for (let i = 0; i < d.length; i++) {
@@ -8474,7 +9167,7 @@
8474
9167
  }
8475
9168
  break;
8476
9169
  }
8477
- case 33 /* TypeIds.JSXNode */: {
9170
+ case 31 /* TypeIds.JSXNode */: {
8478
9171
  const jsx = target;
8479
9172
  const [type, varProps, constProps, children, flags, key] = data;
8480
9173
  jsx.type = type;
@@ -8485,7 +9178,7 @@
8485
9178
  jsx.key = key;
8486
9179
  break;
8487
9180
  }
8488
- case 17 /* TypeIds.Set */: {
9181
+ case 16 /* TypeIds.Set */: {
8489
9182
  const set = target;
8490
9183
  const d = data;
8491
9184
  for (let i = 0; i < d.length; i++) {
@@ -8493,7 +9186,7 @@
8493
9186
  }
8494
9187
  break;
8495
9188
  }
8496
- case 18 /* TypeIds.Map */: {
9189
+ case 17 /* TypeIds.Map */: {
8497
9190
  const map = target;
8498
9191
  const d = data;
8499
9192
  for (let i = 0; i < d.length; i++) {
@@ -8501,7 +9194,7 @@
8501
9194
  }
8502
9195
  break;
8503
9196
  }
8504
- case 16 /* TypeIds.Promise */: {
9197
+ case 15 /* TypeIds.Promise */: {
8505
9198
  const promise = target;
8506
9199
  const [resolved, result] = data;
8507
9200
  const [resolve, reject] = resolvers.get(promise);
@@ -8513,7 +9206,7 @@
8513
9206
  }
8514
9207
  break;
8515
9208
  }
8516
- case 19 /* TypeIds.Uint8Array */:
9209
+ case 18 /* TypeIds.Uint8Array */:
8517
9210
  const bytes = target;
8518
9211
  const buf = atob(data);
8519
9212
  let i = 0;
@@ -8521,12 +9214,12 @@
8521
9214
  bytes[i++] = s.charCodeAt(0);
8522
9215
  }
8523
9216
  break;
8524
- case 34 /* TypeIds.PropsProxy */:
9217
+ case 32 /* TypeIds.PropsProxy */:
8525
9218
  const propsProxy = target;
8526
9219
  propsProxy[_VAR_PROPS] = data === 0 ? {} : data[0];
8527
9220
  propsProxy[_CONST_PROPS] = data[1];
8528
9221
  break;
8529
- case 35 /* TypeIds.EffectData */: {
9222
+ case 33 /* TypeIds.SubscriptionData */: {
8530
9223
  const effectData = target;
8531
9224
  effectData.data.$scopedStyleIdPrefix$ = data[0];
8532
9225
  effectData.data.$isConst$ = data[1];
@@ -8535,7 +9228,6 @@
8535
9228
  default:
8536
9229
  throw qError(16 /* QError.serializeErrorNotImplemented */, [typeId]);
8537
9230
  }
8538
- return target;
8539
9231
  };
8540
9232
  const _constants = [
8541
9233
  undefined,
@@ -8547,6 +9239,7 @@
8547
9239
  EMPTY_OBJ,
8548
9240
  NEEDS_COMPUTATION,
8549
9241
  STORE_ALL_PROPS,
9242
+ _UNINITIALIZED,
8550
9243
  Slot,
8551
9244
  Fragment,
8552
9245
  NaN,
@@ -8566,6 +9259,7 @@
8566
9259
  'EMPTY_OBJ',
8567
9260
  'NEEDS_COMPUTATION',
8568
9261
  'STORE_ALL_PROPS',
9262
+ '_UNINITIALIZED',
8569
9263
  'Slot',
8570
9264
  'Fragment',
8571
9265
  'NaN',
@@ -8576,84 +9270,96 @@
8576
9270
  'MIN_SAFE_INTEGER',
8577
9271
  ];
8578
9272
  const allocate = (container, typeId, value) => {
8579
- if (value === undefined) {
8580
- // When a value was already processed, the result is stored in type
8581
- return typeId;
9273
+ if (typeId === 0 /* TypeIds.Plain */) {
9274
+ return value;
8582
9275
  }
8583
9276
  switch (typeId) {
8584
- case 0 /* TypeIds.RootRef */:
9277
+ case 1 /* TypeIds.RootRef */:
8585
9278
  return container.$getObjectById$(value);
8586
- case 1 /* TypeIds.ForwardRef */:
9279
+ case 2 /* TypeIds.ForwardRef */:
8587
9280
  if (!container.$forwardRefs$) {
8588
9281
  throw qError(18 /* QError.serializeErrorCannotAllocate */, ['forward ref']);
8589
9282
  }
8590
- return container.$getObjectById$(container.$forwardRefs$[value]);
8591
- case 2 /* TypeIds.ForwardRefs */:
9283
+ const rootRef = container.$forwardRefs$[value];
9284
+ if (rootRef === -1) {
9285
+ return _UNINITIALIZED;
9286
+ }
9287
+ else {
9288
+ return container.$getObjectById$(rootRef);
9289
+ }
9290
+ case 13 /* TypeIds.ForwardRefs */:
8592
9291
  return value;
8593
9292
  case 3 /* TypeIds.Constant */:
8594
9293
  return _constants[value];
8595
- case 4 /* TypeIds.Number */:
8596
- return value;
8597
- case 6 /* TypeIds.Array */:
9294
+ case 4 /* TypeIds.Array */:
9295
+ // Wrap while inflating so we can handle cyclic references
8598
9296
  return wrapDeserializerProxy(container, value);
8599
- case 15 /* TypeIds.Object */:
9297
+ case 5 /* TypeIds.Object */:
8600
9298
  return {};
8601
- case 20 /* TypeIds.QRL */:
8602
- case 21 /* TypeIds.PreloadQRL */:
9299
+ case 19 /* TypeIds.QRL */:
9300
+ case 20 /* TypeIds.PreloadQRL */:
8603
9301
  const qrl = typeof value === 'number'
8604
9302
  ? // root reference
8605
9303
  container.$getObjectById$(value)
8606
9304
  : value;
8607
9305
  return parseQRL(qrl);
8608
- case 22 /* TypeIds.Task */:
9306
+ case 21 /* TypeIds.Task */:
8609
9307
  return new Task(-1, -1, null, null, null, null);
8610
- case 23 /* TypeIds.Resource */: {
9308
+ case 22 /* TypeIds.Resource */: {
8611
9309
  const res = createResourceReturn(container,
8612
9310
  // we don't care about the timeout value
8613
9311
  undefined, undefined);
8614
9312
  res.loading = false;
8615
9313
  return res;
8616
9314
  }
8617
- case 7 /* TypeIds.URL */:
9315
+ case 6 /* TypeIds.URL */:
8618
9316
  return new URL(value);
8619
- case 8 /* TypeIds.Date */:
9317
+ case 7 /* TypeIds.Date */:
8620
9318
  return new Date(value);
8621
- case 9 /* TypeIds.Regex */:
9319
+ case 8 /* TypeIds.Regex */:
8622
9320
  const idx = value.lastIndexOf('/');
8623
9321
  return new RegExp(value.slice(1, idx), value.slice(idx + 1));
8624
9322
  case 14 /* TypeIds.Error */:
8625
9323
  return new Error();
8626
- case 24 /* TypeIds.Component */:
9324
+ case 23 /* TypeIds.Component */:
8627
9325
  return componentQrl(null);
8628
- case 25 /* TypeIds.Signal */:
9326
+ case 24 /* TypeIds.Signal */:
8629
9327
  return new SignalImpl(container, 0);
8630
- case 26 /* TypeIds.WrappedSignal */:
9328
+ case 25 /* TypeIds.WrappedSignal */:
8631
9329
  return new WrappedSignalImpl(container, null, null, null);
8632
- case 27 /* TypeIds.ComputedSignal */:
9330
+ case 26 /* TypeIds.ComputedSignal */:
8633
9331
  return new ComputedSignalImpl(container, null);
8634
- case 28 /* TypeIds.AsyncComputedSignal */:
9332
+ case 27 /* TypeIds.AsyncComputedSignal */:
8635
9333
  return new AsyncComputedSignalImpl(container, null);
8636
- case 29 /* TypeIds.SerializerSignal */:
9334
+ case 28 /* TypeIds.SerializerSignal */:
8637
9335
  return new SerializerSignalImpl(container, null);
8638
- case 30 /* TypeIds.Store */:
8639
- case 31 /* TypeIds.StoreArray */:
8640
- // ignore allocate, we need to assign target while creating store
8641
- return null;
8642
- case 13 /* TypeIds.URLSearchParams */:
9336
+ case 29 /* TypeIds.Store */:
9337
+ /**
9338
+ * We have a problem here: In theory, both the store and the target need to be present at
9339
+ * allocate time before inflation can happen. However, that makes the code really complex.
9340
+ * Instead, we deserialize the target here, which will already allocate and inflate this store
9341
+ * if there is a cycle (because the original allocation for the store didn't complete yet).
9342
+ * Because we have a map of target -> store, we will reuse the same store instance after
9343
+ * target deserialization. So in that case, we will be running inflation twice on the same
9344
+ * store, but that is not a problem, very little overhead and the code is way simpler.
9345
+ */
9346
+ const storeValue = deserializeData(container, value[0], value[1]);
9347
+ value[0] = 0 /* TypeIds.Plain */;
9348
+ value[1] = storeValue;
9349
+ return getOrCreateStore(storeValue, 0 /* StoreFlags.NONE */, container);
9350
+ case 12 /* TypeIds.URLSearchParams */:
8643
9351
  return new URLSearchParams(value);
8644
- case 32 /* TypeIds.FormData */:
9352
+ case 30 /* TypeIds.FormData */:
8645
9353
  return new FormData();
8646
- case 33 /* TypeIds.JSXNode */:
9354
+ case 31 /* TypeIds.JSXNode */:
8647
9355
  return new JSXNodeImpl(null, null, null, null, -1, null);
8648
- case 12 /* TypeIds.BigInt */:
9356
+ case 11 /* TypeIds.BigInt */:
8649
9357
  return BigInt(value);
8650
- case 17 /* TypeIds.Set */:
9358
+ case 16 /* TypeIds.Set */:
8651
9359
  return new Set();
8652
- case 18 /* TypeIds.Map */:
9360
+ case 17 /* TypeIds.Map */:
8653
9361
  return new Map();
8654
- case 5 /* TypeIds.String */:
8655
- return value;
8656
- case 16 /* TypeIds.Promise */:
9362
+ case 15 /* TypeIds.Promise */:
8657
9363
  let resolve;
8658
9364
  let reject;
8659
9365
  const promise = new Promise((res, rej) => {
@@ -8664,25 +9370,51 @@
8664
9370
  // Don't leave unhandled promise rejections
8665
9371
  promise.catch(() => { });
8666
9372
  return promise;
8667
- case 19 /* TypeIds.Uint8Array */:
9373
+ case 18 /* TypeIds.Uint8Array */:
8668
9374
  const encodedLength = value.length;
8669
9375
  const blocks = encodedLength >>> 2;
8670
9376
  const rest = encodedLength & 3;
8671
9377
  const decodedLength = blocks * 3 + (rest ? rest - 1 : 0);
8672
9378
  return new Uint8Array(decodedLength);
8673
- case 34 /* TypeIds.PropsProxy */:
9379
+ case 32 /* TypeIds.PropsProxy */:
8674
9380
  return createPropsProxy(null, null);
8675
- case 10 /* TypeIds.VNode */:
9381
+ case 9 /* TypeIds.VNode */:
8676
9382
  return retrieveVNodeOrDocument(container, value);
8677
- case 11 /* TypeIds.RefVNode */:
9383
+ case 10 /* TypeIds.RefVNode */:
8678
9384
  const vNode = retrieveVNodeOrDocument(container, value);
8679
9385
  if (vnode_isVNode(vNode)) {
9386
+ /**
9387
+ * If we have a ref, we need to ensure the element is materialized.
9388
+ *
9389
+ * Example:
9390
+ *
9391
+ * ```
9392
+ * const Cmp = component$(() => {
9393
+ * const element = useSignal<HTMLDivElement>();
9394
+ *
9395
+ * useVisibleTask$(() => {
9396
+ * element.value!.innerHTML = 'I am the innerHTML content!';
9397
+ * });
9398
+ *
9399
+ * return (
9400
+ * <div ref={element} />
9401
+ * );
9402
+ * });
9403
+ * ```
9404
+ *
9405
+ * If we don't materialize early element with ref property, and change element innerHTML it
9406
+ * will be applied to a vnode tree during the lazy materialization, and it is wrong.
9407
+ *
9408
+ * Next if we rerender component it will remove applied innerHTML, because the system thinks
9409
+ * it is a part of the vnode tree.
9410
+ */
9411
+ ensureMaterialized(vNode);
8680
9412
  return vnode_getNode(vNode);
8681
9413
  }
8682
9414
  else {
8683
9415
  throw qError(17 /* QError.serializeErrorExpectedVNode */, [typeof vNode]);
8684
9416
  }
8685
- case 35 /* TypeIds.EffectData */:
9417
+ case 33 /* TypeIds.SubscriptionData */:
8686
9418
  return new SubscriptionData({});
8687
9419
  default:
8688
9420
  throw qError(18 /* QError.serializeErrorCannotAllocate */, [typeId]);
@@ -8744,7 +9476,7 @@
8744
9476
  };
8745
9477
  }
8746
9478
  const seenObjsMap = new Map();
8747
- const rootsPathMap = new Map();
9479
+ const objectPathStringCache = new Map();
8748
9480
  const syncFnMap = new Map();
8749
9481
  const syncFns = [];
8750
9482
  const roots = [];
@@ -8753,7 +9485,7 @@
8753
9485
  return seenObjsMap.set(obj, { $parent$: parent, $index$: index, $rootIndex$: -1 });
8754
9486
  };
8755
9487
  const $addRootPath$ = (obj) => {
8756
- const rootPath = rootsPathMap.get(obj);
9488
+ const rootPath = objectPathStringCache.get(obj);
8757
9489
  if (rootPath) {
8758
9490
  return rootPath;
8759
9491
  }
@@ -8772,7 +9504,7 @@
8772
9504
  current = seenObjsMap.get(current.$parent$);
8773
9505
  }
8774
9506
  const pathStr = path.length > 1 ? path.join(' ') : path.length ? path[0] : seen.$index$;
8775
- rootsPathMap.set(obj, pathStr);
9507
+ objectPathStringCache.set(obj, pathStr);
8776
9508
  return pathStr;
8777
9509
  };
8778
9510
  const $addRoot$ = (obj, parent = null) => {
@@ -8839,7 +9571,7 @@
8839
9571
  $storeProxyMap$: storeProxyMap,
8840
9572
  $getProp$: getProp,
8841
9573
  $setProp$: setProp,
8842
- $pathMap$: rootsPathMap,
9574
+ $objectPathStringCache$: objectPathStringCache,
8843
9575
  };
8844
9576
  };
8845
9577
  function $discoverRoots$(serializationContext, obj, parent, index) {
@@ -8863,7 +9595,8 @@
8863
9595
  for (let i = 1; i < value.length; i += 2) {
8864
9596
  const keyValue = value[i - 1];
8865
9597
  const attrValue = value[i];
8866
- if (typeof attrValue === 'string' ||
9598
+ if (attrValue == null ||
9599
+ typeof attrValue === 'string' ||
8867
9600
  // skip empty props
8868
9601
  (keyValue === ELEMENT_PROPS &&
8869
9602
  Object.keys(attrValue).length === 0)) {
@@ -8888,6 +9621,14 @@
8888
9621
  this.$qrl$ = $qrl$;
8889
9622
  }
8890
9623
  }
9624
+ class SerializationWeakRef {
9625
+ $obj$;
9626
+ constructor($obj$) {
9627
+ this.$obj$ = $obj$;
9628
+ }
9629
+ }
9630
+ /** @internal */
9631
+ const _serializationWeakRef = (obj) => new SerializationWeakRef(obj);
8891
9632
  /**
8892
9633
  * Format:
8893
9634
  *
@@ -8898,12 +9639,14 @@
8898
9639
  * - Therefore root indexes need to be doubled to get the actual index.
8899
9640
  */
8900
9641
  async function serialize(serializationContext) {
8901
- const { $writer$, $isSsrNode$, $isDomRef$, $storeProxyMap$, $addRoot$, $pathMap$, $wasSeen$ } = serializationContext;
9642
+ const { $writer$, $isSsrNode$, $isDomRef$, $storeProxyMap$, $addRoot$, $objectPathStringCache$, $wasSeen$, } = serializationContext;
8902
9643
  let depth = 0;
9644
+ let rootIdx = 0;
8903
9645
  const forwardRefs = [];
8904
9646
  let forwardRefsId = 0;
8905
9647
  const promises = new Set();
8906
9648
  const preloadQrls = new Set();
9649
+ const s11nWeakRefs = new Map();
8907
9650
  let parent = null;
8908
9651
  const isRootObject = () => depth === 0;
8909
9652
  const outputArray = (value, writeFn) => {
@@ -8948,19 +9691,41 @@
8948
9691
  };
8949
9692
  const addPreloadQrl = (qrl) => {
8950
9693
  preloadQrls.add(qrl);
8951
- serializationContext.$addRoot$(qrl, null);
9694
+ serializationContext.$addRoot$(qrl);
8952
9695
  };
8953
- const outputRootRef = (value, rootDepth = 0) => {
9696
+ const outputAsRootRef = (value, rootDepth = 0) => {
8954
9697
  const seen = $wasSeen$(value);
8955
- const rootRefPath = $pathMap$.get(value);
9698
+ const rootRefPath = $objectPathStringCache$.get(value);
9699
+ // Objects are the only way to create circular dependencies.
9700
+ // So the first thing to to is to see if we have a circular dependency.
9701
+ // (NOTE: For root objects we need to serialize them regardless if we have seen
9702
+ // them before, otherwise the root object reference will point to itself.)
9703
+ // Also note that depth will be 1 for objects in root
8956
9704
  if (rootDepth === depth && seen && seen.$parent$ !== null && rootRefPath) {
8957
- output(0 /* TypeIds.RootRef */, rootRefPath);
9705
+ output(1 /* TypeIds.RootRef */, rootRefPath);
8958
9706
  return true;
8959
9707
  }
8960
9708
  else if (depth > rootDepth && seen && seen.$rootIndex$ !== -1) {
8961
- output(0 /* TypeIds.RootRef */, seen.$rootIndex$);
9709
+ // We have seen this object before, so we can serialize it as a reference.
9710
+ // Otherwise serialize as normal
9711
+ output(1 /* TypeIds.RootRef */, seen.$rootIndex$);
8962
9712
  return true;
8963
9713
  }
9714
+ else if (s11nWeakRefs.has(value)) {
9715
+ const forwardRefId = s11nWeakRefs.get(value);
9716
+ // We see the object again, we must now make it a root and update the forward ref
9717
+ if (rootDepth === depth) {
9718
+ // It's already a root
9719
+ forwardRefs[forwardRefId] = rootIdx;
9720
+ }
9721
+ else {
9722
+ // ref
9723
+ const rootRef = $addRoot$(value);
9724
+ output(1 /* TypeIds.RootRef */, rootRef);
9725
+ forwardRefs[forwardRefId] = rootRef;
9726
+ return true;
9727
+ }
9728
+ }
8964
9729
  return false;
8965
9730
  };
8966
9731
  const writeValue = (value) => {
@@ -8968,22 +9733,22 @@
8968
9733
  output(3 /* TypeIds.Constant */, 0 /* Constants.Undefined */);
8969
9734
  }
8970
9735
  else if (typeof value === 'bigint') {
8971
- output(12 /* TypeIds.BigInt */, value.toString());
9736
+ output(11 /* TypeIds.BigInt */, value.toString());
8972
9737
  }
8973
9738
  else if (typeof value === 'boolean') {
8974
9739
  output(3 /* TypeIds.Constant */, value ? 2 /* Constants.True */ : 3 /* Constants.False */);
8975
9740
  }
8976
9741
  else if (typeof value === 'function') {
8977
9742
  if (value === Slot) {
8978
- output(3 /* TypeIds.Constant */, 9 /* Constants.Slot */);
9743
+ output(3 /* TypeIds.Constant */, 10 /* Constants.Slot */);
8979
9744
  }
8980
9745
  else if (value === Fragment) {
8981
- output(3 /* TypeIds.Constant */, 10 /* Constants.Fragment */);
9746
+ output(3 /* TypeIds.Constant */, 11 /* Constants.Fragment */);
8982
9747
  }
8983
9748
  else if (isQrl(value)) {
8984
- if (!outputRootRef(value)) {
9749
+ if (!outputAsRootRef(value)) {
8985
9750
  const qrl = qrlToString(serializationContext, value);
8986
- const type = preloadQrls.has(value) ? 21 /* TypeIds.PreloadQRL */ : 20 /* TypeIds.QRL */;
9751
+ const type = preloadQrls.has(value) ? 20 /* TypeIds.PreloadQRL */ : 19 /* TypeIds.QRL */;
8987
9752
  if (isRootObject()) {
8988
9753
  output(type, qrl);
8989
9754
  }
@@ -8996,7 +9761,7 @@
8996
9761
  else if (isQwikComponent(value)) {
8997
9762
  const [qrl] = value[SERIALIZABLE_STATE];
8998
9763
  serializationContext.$renderSymbols$.add(qrl.$symbol$);
8999
- output(24 /* TypeIds.Component */, [qrl]);
9764
+ output(23 /* TypeIds.Component */, [qrl]);
9000
9765
  }
9001
9766
  else {
9002
9767
  throw qError(34 /* QError.serializeErrorCannotSerializeFunction */, [value.toString()]);
@@ -9004,22 +9769,22 @@
9004
9769
  }
9005
9770
  else if (typeof value === 'number') {
9006
9771
  if (Number.isNaN(value)) {
9007
- output(3 /* TypeIds.Constant */, 11 /* Constants.NaN */);
9772
+ output(3 /* TypeIds.Constant */, 12 /* Constants.NaN */);
9008
9773
  }
9009
9774
  else if (!Number.isFinite(value)) {
9010
- output(3 /* TypeIds.Constant */, value < 0 ? 13 /* Constants.NegativeInfinity */ : 12 /* Constants.PositiveInfinity */);
9775
+ output(3 /* TypeIds.Constant */, value < 0 ? 14 /* Constants.NegativeInfinity */ : 13 /* Constants.PositiveInfinity */);
9011
9776
  }
9012
9777
  else if (value === Number.MAX_SAFE_INTEGER) {
9013
- output(3 /* TypeIds.Constant */, 14 /* Constants.MaxSafeInt */);
9778
+ output(3 /* TypeIds.Constant */, 15 /* Constants.MaxSafeInt */);
9014
9779
  }
9015
9780
  else if (value === Number.MAX_SAFE_INTEGER - 1) {
9016
- output(3 /* TypeIds.Constant */, 15 /* Constants.AlmostMaxSafeInt */);
9781
+ output(3 /* TypeIds.Constant */, 16 /* Constants.AlmostMaxSafeInt */);
9017
9782
  }
9018
9783
  else if (value === Number.MIN_SAFE_INTEGER) {
9019
- output(3 /* TypeIds.Constant */, 16 /* Constants.MinSafeInt */);
9784
+ output(3 /* TypeIds.Constant */, 17 /* Constants.MinSafeInt */);
9020
9785
  }
9021
9786
  else {
9022
- output(4 /* TypeIds.Number */, value);
9787
+ output(0 /* TypeIds.Plain */, value);
9023
9788
  }
9024
9789
  }
9025
9790
  else if (typeof value === 'object') {
@@ -9046,8 +9811,8 @@
9046
9811
  output(3 /* TypeIds.Constant */, 4 /* Constants.EmptyString */);
9047
9812
  }
9048
9813
  else {
9049
- if (!outputRootRef(value)) {
9050
- output(5 /* TypeIds.String */, value);
9814
+ if (!outputAsRootRef(value)) {
9815
+ output(0 /* TypeIds.Plain */, value);
9051
9816
  }
9052
9817
  }
9053
9818
  }
@@ -9060,6 +9825,9 @@
9060
9825
  else if (value === STORE_ALL_PROPS) {
9061
9826
  output(3 /* TypeIds.Constant */, 8 /* Constants.STORE_ALL_PROPS */);
9062
9827
  }
9828
+ else if (value === _UNINITIALIZED) {
9829
+ output(3 /* TypeIds.Constant */, 9 /* Constants.UNINITIALIZED */);
9830
+ }
9063
9831
  else {
9064
9832
  throw qError(20 /* QError.serializeErrorUnknownType */, [typeof value]);
9065
9833
  }
@@ -9069,14 +9837,11 @@
9069
9837
  * The object writer outputs an array object (without type prefix) and this increases the depth
9070
9838
  * for the objects within (depth 1).
9071
9839
  */
9072
- // Objects are the only way to create circular dependencies.
9073
- // So the first thing to to is to see if we have a circular dependency.
9074
- // (NOTE: For root objects we need to serialize them regardless if we have seen
9075
- // them before, otherwise the root object reference will point to itself.)
9076
- // Also note that depth will be 1 for objects in root
9077
- if (outputRootRef(value, 1)) {
9840
+ if (outputAsRootRef(value, 1)) {
9078
9841
  return;
9079
9842
  }
9843
+ // handle custom serializers
9844
+ // add to the seen map
9080
9845
  if (isPropsProxy(value)) {
9081
9846
  const varProps = value[_VAR_PROPS];
9082
9847
  const constProps = value[_CONST_PROPS];
@@ -9085,10 +9850,10 @@
9085
9850
  : Object.keys(varProps).length
9086
9851
  ? [varProps]
9087
9852
  : 0;
9088
- output(34 /* TypeIds.PropsProxy */, out);
9853
+ output(32 /* TypeIds.PropsProxy */, out);
9089
9854
  }
9090
9855
  else if (value instanceof SubscriptionData) {
9091
- output(35 /* TypeIds.EffectData */, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
9856
+ output(33 /* TypeIds.SubscriptionData */, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
9092
9857
  }
9093
9858
  else if (isStore(value)) {
9094
9859
  if (isResource(value)) {
@@ -9096,38 +9861,38 @@
9096
9861
  serializationContext.$resources$.add(value);
9097
9862
  // TODO the effects include the resource return which has duplicate data
9098
9863
  const forwardRefId = $resolvePromise$(value.value, $addRoot$, (resolved, resolvedValue) => {
9099
- return new PromiseResult(23 /* TypeIds.Resource */, resolved, resolvedValue, getStoreHandler(value).$effects$);
9864
+ return new PromiseResult(22 /* TypeIds.Resource */, resolved, resolvedValue, getStoreHandler(value).$effects$);
9100
9865
  });
9101
- output(1 /* TypeIds.ForwardRef */, forwardRefId);
9866
+ output(2 /* TypeIds.ForwardRef */, forwardRefId);
9102
9867
  }
9103
9868
  else {
9104
9869
  const storeHandler = getStoreHandler(value);
9105
9870
  const storeTarget = getStoreTarget(value);
9106
9871
  const flags = storeHandler.$flags$;
9107
9872
  const effects = storeHandler.$effects$;
9873
+ // We need to retain the nested stores too, they won't be found from the target
9108
9874
  const innerStores = [];
9109
9875
  for (const prop in storeTarget) {
9110
9876
  const propValue = storeTarget[prop];
9111
- if ($storeProxyMap$.has(propValue)) {
9112
- const innerStore = $storeProxyMap$.get(propValue);
9877
+ const innerStore = $storeProxyMap$.get(propValue);
9878
+ if (innerStore) {
9113
9879
  innerStores.push(innerStore);
9114
- serializationContext.$addRoot$(innerStore);
9115
9880
  }
9116
9881
  }
9117
9882
  const out = [storeTarget, flags, effects, ...innerStores];
9118
9883
  while (out[out.length - 1] == null) {
9119
9884
  out.pop();
9120
9885
  }
9121
- output(Array.isArray(storeTarget) ? 31 /* TypeIds.StoreArray */ : 30 /* TypeIds.Store */, out);
9886
+ output(29 /* TypeIds.Store */, out);
9122
9887
  }
9123
9888
  }
9124
9889
  else if (isSerializerObj(value)) {
9125
9890
  const result = value[SerializerSymbol](value);
9126
9891
  if (isPromise(result)) {
9127
9892
  const forwardRef = $resolvePromise$(result, $addRoot$, (resolved, resolvedValue) => {
9128
- return new PromiseResult(29 /* TypeIds.SerializerSignal */, resolved, resolvedValue, null, null);
9893
+ return new PromiseResult(28 /* TypeIds.SerializerSignal */, resolved, resolvedValue, null, null);
9129
9894
  });
9130
- output(1 /* TypeIds.ForwardRef */, forwardRef);
9895
+ output(2 /* TypeIds.ForwardRef */, forwardRef);
9131
9896
  }
9132
9897
  else {
9133
9898
  depth--;
@@ -9137,7 +9902,7 @@
9137
9902
  }
9138
9903
  else if (isObjectLiteral(value)) {
9139
9904
  if (Array.isArray(value)) {
9140
- output(6 /* TypeIds.Array */, value);
9905
+ output(4 /* TypeIds.Array */, value);
9141
9906
  }
9142
9907
  else {
9143
9908
  const out = [];
@@ -9148,32 +9913,24 @@
9148
9913
  }
9149
9914
  }
9150
9915
  // TODO if !out.length, output 0 and restore as {}
9151
- output(15 /* TypeIds.Object */, out);
9916
+ output(5 /* TypeIds.Object */, out);
9152
9917
  }
9153
9918
  }
9154
9919
  else if ($isDomRef$(value)) {
9155
9920
  value.$ssrNode$.vnodeData[0] |= 16 /* VNodeDataFlag.SERIALIZE */;
9156
- output(11 /* TypeIds.RefVNode */, value.$ssrNode$.id);
9921
+ output(10 /* TypeIds.RefVNode */, value.$ssrNode$.id);
9157
9922
  }
9158
9923
  else if (value instanceof SignalImpl) {
9159
9924
  if (value instanceof SerializerSignalImpl) {
9160
9925
  addPreloadQrl(value.$computeQrl$);
9161
9926
  const forwardRefId = $resolvePromise$($getCustomSerializerPromise$(value, value.$untrackedValue$), $addRoot$, (resolved, resolvedValue) => {
9162
- return new PromiseResult(29 /* TypeIds.SerializerSignal */, resolved, resolvedValue, value.$effects$, value.$computeQrl$);
9927
+ return new PromiseResult(28 /* TypeIds.SerializerSignal */, resolved, resolvedValue, value.$effects$, value.$computeQrl$);
9163
9928
  });
9164
- output(1 /* TypeIds.ForwardRef */, forwardRefId);
9929
+ output(2 /* TypeIds.ForwardRef */, forwardRefId);
9165
9930
  return;
9166
9931
  }
9167
- /**
9168
- * Special case: when a Signal value is an SSRNode, it always needs to be a DOM ref instead.
9169
- * It can never be meant to become a vNode, because vNodes are internal only.
9170
- */
9171
- const v = value instanceof ComputedSignalImpl &&
9172
- (value.$flags$ & 1 /* SignalFlags.INVALID */ || fastSkipSerialize(value.$untrackedValue$))
9173
- ? NEEDS_COMPUTATION
9174
- : value.$untrackedValue$;
9175
9932
  if (value instanceof WrappedSignalImpl) {
9176
- output(26 /* TypeIds.WrappedSignal */, [
9933
+ output(25 /* TypeIds.WrappedSignal */, [
9177
9934
  ...serializeWrappingFn(serializationContext, value),
9178
9935
  filterEffectBackRefs(value[_EFFECT_BACK_REF]),
9179
9936
  value.$flags$,
@@ -9181,45 +9938,44 @@
9181
9938
  ...(value.$effects$ || []),
9182
9939
  ]);
9183
9940
  }
9184
- else if (value instanceof AsyncComputedSignalImpl) {
9185
- addPreloadQrl(value.$computeQrl$);
9186
- const out = [
9187
- value.$computeQrl$,
9188
- value.$effects$,
9189
- value.$loadingEffects$,
9190
- value.$errorEffects$,
9191
- value.$untrackedLoading$,
9192
- value.$untrackedError$,
9193
- ];
9194
- if (v !== NEEDS_COMPUTATION) {
9195
- out.push(v);
9196
- }
9197
- output(28 /* TypeIds.AsyncComputedSignal */, out);
9198
- }
9199
9941
  else if (value instanceof ComputedSignalImpl) {
9942
+ let v = value.$untrackedValue$;
9943
+ const shouldAlwaysSerialize = value.$flags$ & 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
9944
+ const shouldNeverSerialize = value.$flags$ & 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
9945
+ const isInvalid = value.$flags$ & 1 /* SignalFlags.INVALID */;
9946
+ const isSkippable = fastSkipSerialize(value.$untrackedValue$);
9947
+ if (shouldAlwaysSerialize) {
9948
+ v = value.$untrackedValue$;
9949
+ }
9950
+ else if (shouldNeverSerialize) {
9951
+ v = NEEDS_COMPUTATION;
9952
+ }
9953
+ else if (isInvalid || isSkippable) {
9954
+ v = NEEDS_COMPUTATION;
9955
+ }
9200
9956
  addPreloadQrl(value.$computeQrl$);
9201
- const out = [
9202
- value.$computeQrl$,
9203
- // TODO check if we can use domVRef for effects
9204
- value.$effects$,
9205
- ];
9957
+ const out = [value.$computeQrl$, value.$effects$];
9958
+ const isAsync = value instanceof AsyncComputedSignalImpl;
9959
+ if (isAsync) {
9960
+ out.push(value.$loadingEffects$, value.$errorEffects$, value.$untrackedLoading$, value.$untrackedError$);
9961
+ }
9206
9962
  if (v !== NEEDS_COMPUTATION) {
9207
9963
  out.push(v);
9208
9964
  }
9209
- output(27 /* TypeIds.ComputedSignal */, out);
9965
+ output(isAsync ? 27 /* TypeIds.AsyncComputedSignal */ : 26 /* TypeIds.ComputedSignal */, out);
9210
9966
  }
9211
9967
  else {
9212
- output(25 /* TypeIds.Signal */, [v, ...(value.$effects$ || [])]);
9968
+ output(24 /* TypeIds.Signal */, [value.$untrackedValue$, ...(value.$effects$ || [])]);
9213
9969
  }
9214
9970
  }
9215
9971
  else if (value instanceof URL) {
9216
- output(7 /* TypeIds.URL */, value.href);
9972
+ output(6 /* TypeIds.URL */, value.href);
9217
9973
  }
9218
9974
  else if (value instanceof Date) {
9219
- output(8 /* TypeIds.Date */, Number.isNaN(value.valueOf()) ? '' : value.valueOf());
9975
+ output(7 /* TypeIds.Date */, Number.isNaN(value.valueOf()) ? '' : value.valueOf());
9220
9976
  }
9221
9977
  else if (value instanceof RegExp) {
9222
- output(9 /* TypeIds.Regex */, value.toString());
9978
+ output(8 /* TypeIds.Regex */, value.toString());
9223
9979
  }
9224
9980
  else if (value instanceof Error) {
9225
9981
  const out = [value.message];
@@ -9235,7 +9991,7 @@
9235
9991
  const rootIndex = $addRoot$(value);
9236
9992
  serializationContext.$setProp$(value, ELEMENT_ID, String(rootIndex));
9237
9993
  // we need to output before the vnode overwrites its values
9238
- output(10 /* TypeIds.VNode */, value.id);
9994
+ output(9 /* TypeIds.VNode */, value.id);
9239
9995
  const vNodeData = value.vnodeData;
9240
9996
  if (vNodeData) {
9241
9997
  discoverValuesForVNodeData(vNodeData, (vNodeDataValue) => $addRoot$(vNodeDataValue));
@@ -9271,23 +10027,23 @@
9271
10027
  array.push(key, value.name);
9272
10028
  }
9273
10029
  });
9274
- output(32 /* TypeIds.FormData */, array);
10030
+ output(30 /* TypeIds.FormData */, array);
9275
10031
  }
9276
10032
  else if (value instanceof URLSearchParams) {
9277
- output(13 /* TypeIds.URLSearchParams */, value.toString());
10033
+ output(12 /* TypeIds.URLSearchParams */, value.toString());
9278
10034
  }
9279
10035
  else if (value instanceof Set) {
9280
- output(17 /* TypeIds.Set */, [...value.values()]);
10036
+ output(16 /* TypeIds.Set */, [...value.values()]);
9281
10037
  }
9282
10038
  else if (value instanceof Map) {
9283
10039
  const combined = [];
9284
10040
  for (const [k, v] of value.entries()) {
9285
10041
  combined.push(k, v);
9286
10042
  }
9287
- output(18 /* TypeIds.Map */, combined);
10043
+ output(17 /* TypeIds.Map */, combined);
9288
10044
  }
9289
10045
  else if (isJSXNode(value)) {
9290
- output(33 /* TypeIds.JSXNode */, [
10046
+ output(31 /* TypeIds.JSXNode */, [
9291
10047
  value.type,
9292
10048
  value.varProps,
9293
10049
  value.constProps,
@@ -9308,21 +10064,21 @@
9308
10064
  while (out[out.length - 1] == null) {
9309
10065
  out.pop();
9310
10066
  }
9311
- output(22 /* TypeIds.Task */, out);
10067
+ output(21 /* TypeIds.Task */, out);
9312
10068
  }
9313
10069
  else if (isPromise(value)) {
9314
10070
  const forwardRefId = $resolvePromise$(value, $addRoot$, (resolved, resolvedValue) => {
9315
- return new PromiseResult(16 /* TypeIds.Promise */, resolved, resolvedValue);
10071
+ return new PromiseResult(15 /* TypeIds.Promise */, resolved, resolvedValue);
9316
10072
  });
9317
- output(1 /* TypeIds.ForwardRef */, forwardRefId);
10073
+ output(2 /* TypeIds.ForwardRef */, forwardRefId);
9318
10074
  }
9319
10075
  else if (value instanceof PromiseResult) {
9320
- if (value.$type$ === 23 /* TypeIds.Resource */) {
9321
- output(23 /* TypeIds.Resource */, [value.$resolved$, value.$value$, value.$effects$]);
10076
+ if (value.$type$ === 22 /* TypeIds.Resource */) {
10077
+ output(22 /* TypeIds.Resource */, [value.$resolved$, value.$value$, value.$effects$]);
9322
10078
  }
9323
- else if (value.$type$ === 29 /* TypeIds.SerializerSignal */) {
10079
+ else if (value.$type$ === 28 /* TypeIds.SerializerSignal */) {
9324
10080
  if (value.$qrl$) {
9325
- output(29 /* TypeIds.SerializerSignal */, [value.$qrl$, value.$effects$, value.$value$]);
10081
+ output(28 /* TypeIds.SerializerSignal */, [value.$qrl$, value.$effects$, value.$value$]);
9326
10082
  }
9327
10083
  else if (value.$resolved$) {
9328
10084
  writeValue(value.$value$);
@@ -9333,7 +10089,7 @@
9333
10089
  }
9334
10090
  }
9335
10091
  else {
9336
- output(16 /* TypeIds.Promise */, [value.$resolved$, value.$value$]);
10092
+ output(15 /* TypeIds.Promise */, [value.$resolved$, value.$value$]);
9337
10093
  }
9338
10094
  }
9339
10095
  else if (value instanceof Uint8Array) {
@@ -9342,7 +10098,13 @@
9342
10098
  buf += String.fromCharCode(c);
9343
10099
  }
9344
10100
  const out = btoa(buf).replace(/=+$/, '');
9345
- output(19 /* TypeIds.Uint8Array */, out);
10101
+ output(18 /* TypeIds.Uint8Array */, out);
10102
+ }
10103
+ else if (value instanceof SerializationWeakRef) {
10104
+ const forwardRefId = forwardRefsId++;
10105
+ s11nWeakRefs.set(value.$obj$, forwardRefId);
10106
+ forwardRefs[forwardRefId] = -1;
10107
+ output(2 /* TypeIds.ForwardRef */, forwardRefId);
9346
10108
  }
9347
10109
  else if (vnode_isVNode(value)) {
9348
10110
  output(3 /* TypeIds.Constant */, 0 /* Constants.Undefined */);
@@ -9367,21 +10129,20 @@
9367
10129
  }
9368
10130
  const outputRoots = async () => {
9369
10131
  $writer$.write('[');
9370
- let lastRootsLength = 0;
9371
10132
  let rootsLength = serializationContext.$roots$.length;
9372
- while (lastRootsLength < rootsLength || promises.size) {
9373
- if (lastRootsLength !== 0) {
10133
+ while (rootIdx < rootsLength || promises.size) {
10134
+ if (rootIdx !== 0) {
9374
10135
  $writer$.write(',');
9375
10136
  }
9376
10137
  let separator = false;
9377
- for (let i = lastRootsLength; i < rootsLength; i++) {
10138
+ for (; rootIdx < rootsLength; rootIdx++) {
9378
10139
  if (separator) {
9379
10140
  $writer$.write(',');
9380
10141
  }
9381
10142
  else {
9382
10143
  separator = true;
9383
10144
  }
9384
- writeValue(serializationContext.$roots$[i]);
10145
+ writeValue(serializationContext.$roots$[rootIdx]);
9385
10146
  }
9386
10147
  if (promises.size) {
9387
10148
  try {
@@ -9391,12 +10152,11 @@
9391
10152
  // ignore rejections, they will be serialized as rejected promises
9392
10153
  }
9393
10154
  }
9394
- lastRootsLength = rootsLength;
9395
10155
  rootsLength = serializationContext.$roots$.length;
9396
10156
  }
9397
10157
  if (forwardRefs.length) {
9398
10158
  $writer$.write(',');
9399
- $writer$.write(2 /* TypeIds.ForwardRefs */ + ',');
10159
+ $writer$.write(13 /* TypeIds.ForwardRefs */ + ',');
9400
10160
  outputArray(forwardRefs, (value) => {
9401
10161
  $writer$.write(String(value));
9402
10162
  });
@@ -9545,12 +10305,12 @@
9545
10305
  return output;
9546
10306
  }
9547
10307
  function deserializeData(container, typeId, value) {
9548
- if (typeId === undefined) {
10308
+ if (typeId === 0 /* TypeIds.Plain */) {
9549
10309
  return value;
9550
10310
  }
9551
- let propValue = allocate(container, typeId, value);
9552
- if (typeId >= 14 /* TypeIds.Error */) {
9553
- propValue = inflate(container, propValue, typeId, value);
10311
+ const propValue = allocate(container, typeId, value);
10312
+ if (needsInflation(typeId)) {
10313
+ inflate(container, propValue, typeId, value);
9554
10314
  }
9555
10315
  return propValue;
9556
10316
  }
@@ -9573,7 +10333,7 @@
9573
10333
  $storeProxyMap$: new WeakMap(),
9574
10334
  element: null,
9575
10335
  $forwardRefs$: null,
9576
- $initialQRLsIndexes$: null,
10336
+ $initialQRLs$: null,
9577
10337
  $scheduler$: null,
9578
10338
  };
9579
10339
  preprocessState(stateData, container);
@@ -9633,18 +10393,18 @@
9633
10393
  */
9634
10394
  function preprocessState(data, container) {
9635
10395
  const isRootDeepRef = (type, value) => {
9636
- return type === 0 /* TypeIds.RootRef */ && typeof value === 'string';
10396
+ return type === 1 /* TypeIds.RootRef */ && typeof value === 'string';
9637
10397
  };
9638
10398
  const isForwardRefsMap = (type) => {
9639
- return type === 2 /* TypeIds.ForwardRefs */;
10399
+ return type === 13 /* TypeIds.ForwardRefs */;
9640
10400
  };
9641
10401
  const isPreloadQrlType = (type) => {
9642
- return type === 21 /* TypeIds.PreloadQRL */;
10402
+ return type === 20 /* TypeIds.PreloadQRL */;
9643
10403
  };
9644
10404
  const processRootRef = (index) => {
9645
10405
  const rootRefPath = data[index + 1].split(' ');
9646
10406
  let object = data;
9647
- let objectType = 0 /* TypeIds.RootRef */;
10407
+ let objectType = 1 /* TypeIds.RootRef */;
9648
10408
  let typeIndex = 0;
9649
10409
  let valueIndex = 0;
9650
10410
  let parent = null;
@@ -9654,7 +10414,7 @@
9654
10414
  valueIndex = typeIndex + 1;
9655
10415
  objectType = object[typeIndex];
9656
10416
  object = object[valueIndex];
9657
- if (objectType === 0 /* TypeIds.RootRef */) {
10417
+ if (objectType === 1 /* TypeIds.RootRef */) {
9658
10418
  const rootRef = object;
9659
10419
  const rootRefTypeIndex = rootRef * 2;
9660
10420
  objectType = data[rootRefTypeIndex];
@@ -9662,7 +10422,7 @@
9662
10422
  }
9663
10423
  }
9664
10424
  if (parent) {
9665
- parent[typeIndex] = 0 /* TypeIds.RootRef */;
10425
+ parent[typeIndex] = 1 /* TypeIds.RootRef */;
9666
10426
  parent[valueIndex] = index / 2;
9667
10427
  }
9668
10428
  data[index] = objectType;
@@ -9676,8 +10436,8 @@
9676
10436
  container.$forwardRefs$ = data[i + 1];
9677
10437
  }
9678
10438
  else if (isPreloadQrlType(data[i])) {
9679
- container.$initialQRLsIndexes$ ||= [];
9680
- container.$initialQRLsIndexes$.push(i / 2);
10439
+ const qrl = data[i + 1];
10440
+ (container.$initialQRLs$ ||= []).push(qrl);
9681
10441
  }
9682
10442
  }
9683
10443
  }
@@ -9697,7 +10457,7 @@
9697
10457
  return (
9698
10458
  // THINK: Not sure if we need to keep track of functions (QRLs) Let's skip them for now.
9699
10459
  // and see if we have a test case which requires them.
9700
- (typeof obj === 'object' && obj !== null) ||
10460
+ isObject(obj) ||
9701
10461
  /**
9702
10462
  * We track all strings greater than 1 character, because those take at least 6 bytes to encode
9703
10463
  * and even with 999 root objects it saves one byte per reference. Tracking more objects makes
@@ -9725,9 +10485,7 @@
9725
10485
  return '__brand' in value && value.__brand === 'resource';
9726
10486
  }
9727
10487
  const frameworkType = (obj) => {
9728
- return ((typeof obj === 'object' &&
9729
- obj !== null &&
9730
- (obj instanceof SignalImpl || obj instanceof Task || isJSXNode(obj))) ||
10488
+ return ((isObject(obj) && (obj instanceof SignalImpl || obj instanceof Task || isJSXNode(obj))) ||
9731
10489
  isQrl(obj));
9732
10490
  };
9733
10491
  const canSerialize = (value, seen = new WeakSet()) => {
@@ -9813,17 +10571,19 @@
9813
10571
  return true;
9814
10572
  }
9815
10573
  }
10574
+ else if (value === _UNINITIALIZED) {
10575
+ return true;
10576
+ }
9816
10577
  return false;
9817
10578
  };
9818
10579
  const QRL_RUNTIME_CHUNK = 'mock-chunk';
9819
10580
  const _typeIdNames = [
10581
+ 'Plain',
9820
10582
  'RootRef',
9821
10583
  'ForwardRef',
9822
- 'ForwardRefs',
9823
10584
  'Constant',
9824
- 'Number',
9825
- 'String',
9826
10585
  'Array',
10586
+ 'Object',
9827
10587
  'URL',
9828
10588
  'Date',
9829
10589
  'Regex',
@@ -9831,8 +10591,8 @@
9831
10591
  'RefVNode',
9832
10592
  'BigInt',
9833
10593
  'URLSearchParams',
10594
+ 'ForwardRefs',
9834
10595
  'Error',
9835
- 'Object',
9836
10596
  'Promise',
9837
10597
  'Set',
9838
10598
  'Map',
@@ -9848,16 +10608,15 @@
9848
10608
  'AsyncComputedSignal',
9849
10609
  'SerializerSignal',
9850
10610
  'Store',
9851
- 'StoreArray',
9852
10611
  'FormData',
9853
10612
  'JSXNode',
9854
10613
  'PropsProxy',
9855
- 'EffectData',
10614
+ 'SubscriptionData',
9856
10615
  ];
9857
10616
  const circularProofJson = (obj, indent) => {
9858
10617
  const seen = new WeakSet();
9859
- return JSON.stringify(obj, (key, value) => {
9860
- if (typeof value === 'object' && value !== null) {
10618
+ return JSON.stringify(obj, (_, value) => {
10619
+ if (isObject(value)) {
9861
10620
  if (seen.has(value)) {
9862
10621
  return `[Circular ${value.constructor.name}]`;
9863
10622
  }
@@ -9892,9 +10651,15 @@
9892
10651
  }
9893
10652
  const key = state[i];
9894
10653
  let value = state[++i];
9895
- if (key === undefined) {
9896
- hasRaw = true;
9897
- out.push(`${RED}[raw${typeof value === 'object' && value ? ` ${value.constructor.name}` : ''}]${RESET} ${printRaw(value, `${prefix} `)}`);
10654
+ if (key === 0 /* TypeIds.Plain */) {
10655
+ const isRaw = typeof value !== 'number' && typeof value !== 'string';
10656
+ if (isRaw) {
10657
+ hasRaw = true;
10658
+ }
10659
+ const type = isRaw
10660
+ ? `[raw${isObject(value) ? ` ${value.constructor.name}` : ''}]`
10661
+ : typeIdToName(key);
10662
+ out.push(`${RED}${type}${RESET} ${printRaw(value, `${prefix} `)}`);
9898
10663
  }
9899
10664
  else {
9900
10665
  if (key === 3 /* TypeIds.Constant */) {
@@ -9906,7 +10671,7 @@
9906
10671
  value = value.slice(0, 120) + '"...';
9907
10672
  }
9908
10673
  }
9909
- else if (key === 2 /* TypeIds.ForwardRefs */) {
10674
+ else if (key === 13 /* TypeIds.ForwardRefs */) {
9910
10675
  value = '[' + `\n${prefix} ${value.join(`\n${prefix} `)}\n${prefix}]`;
9911
10676
  }
9912
10677
  else if (Array.isArray(value)) {
@@ -10008,7 +10773,6 @@
10008
10773
  return value;
10009
10774
  };
10010
10775
  const noSerializeSet = /*#__PURE__*/ new WeakSet();
10011
- const weakSerializeSet = /*#__PURE__*/ new WeakSet();
10012
10776
  const shouldSerialize = (obj) => {
10013
10777
  if (isObject(obj) || isFunction(obj)) {
10014
10778
  return !noSerializeSet.has(obj);
@@ -10017,7 +10781,7 @@
10017
10781
  };
10018
10782
  const fastSkipSerialize = (obj) => {
10019
10783
  return (obj &&
10020
- (typeof obj === 'object' || typeof obj === 'function') &&
10784
+ (isObject(obj) || typeof obj === 'function') &&
10021
10785
  (NoSerializeSymbol in obj || noSerializeSet.has(obj)));
10022
10786
  };
10023
10787
  // <docs markdown="../../readme.md#noSerialize">
@@ -10042,16 +10806,11 @@
10042
10806
  // </docs>
10043
10807
  const noSerialize = (input) => {
10044
10808
  // only add supported values to the noSerializeSet, prevent console errors
10045
- if ((typeof input === 'object' && input !== null) || typeof input === 'function') {
10809
+ if ((isObject(input) && input !== null) || typeof input === 'function') {
10046
10810
  noSerializeSet.add(input);
10047
10811
  }
10048
10812
  return input;
10049
10813
  };
10050
- /** @internal */
10051
- const _weakSerialize = (input) => {
10052
- weakSerializeSet.add(input);
10053
- return input;
10054
- };
10055
10814
  /**
10056
10815
  * If an object has this property, it will not be serialized. Use this on prototypes to avoid having
10057
10816
  * to call `noSerialize()` on every object.
@@ -10078,6 +10837,7 @@
10078
10837
  const SerializerSymbol = Symbol('serialize');
10079
10838
 
10080
10839
  // keep these imports above the rest to prevent circular dep issues
10840
+ const resolvedSymbol = Symbol('resolved');
10081
10841
  const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
10082
10842
  if (qDev && qSerialize) {
10083
10843
  if (captureRef) {
@@ -10130,9 +10890,6 @@
10130
10890
  };
10131
10891
  return bound;
10132
10892
  }
10133
- const resolveLazy = (containerEl) => {
10134
- return symbolRef !== null ? symbolRef : resolve(containerEl);
10135
- };
10136
10893
  // Wrap functions to provide their lexical scope
10137
10894
  const wrapFn = (fn) => {
10138
10895
  if (typeof fn !== 'function' || (!capture?.length && !captureRef?.length)) {
@@ -10161,45 +10918,58 @@
10161
10918
  return invoke.call(this, context, fn, ...args);
10162
10919
  };
10163
10920
  };
10164
- const resolve = async (containerEl) => {
10165
- if (symbolRef !== null) {
10166
- // Resolving (Promise) or already resolved (value)
10921
+ // Retrieve memoized result from symbolFn
10922
+ if (symbolFn && resolvedSymbol in symbolFn) {
10923
+ symbolRef = symbolFn[resolvedSymbol];
10924
+ }
10925
+ const resolve = symbolRef
10926
+ ? async () => symbolRef
10927
+ : async (containerEl) => {
10928
+ if (symbolRef !== null) {
10929
+ // Resolving (Promise) or already resolved (value)
10930
+ return symbolRef;
10931
+ }
10932
+ if (containerEl) {
10933
+ setContainer(containerEl);
10934
+ }
10935
+ if (chunk === '') {
10936
+ // Sync QRL
10937
+ assertDefined(_containerEl, 'Sync QRL must have container element');
10938
+ const hash = _containerEl.getAttribute(QInstanceAttr);
10939
+ const doc = _containerEl.ownerDocument;
10940
+ const qFuncs = getQFuncs(doc, hash);
10941
+ // No need to wrap, syncQRLs can't have captured scope
10942
+ return (qrl.resolved = symbolRef = qFuncs[Number(symbol)]);
10943
+ }
10944
+ if (build.isBrowser && chunk) {
10945
+ /** We run the QRL, so now the probability of the chunk is 100% */
10946
+ preloader.p(chunk, 1);
10947
+ }
10948
+ const start = now();
10949
+ const ctx = tryGetInvokeContext();
10950
+ if (symbolFn !== null) {
10951
+ symbolRef = symbolFn().then((module) => {
10952
+ const resolved = wrapFn((symbolRef = module[symbol]));
10953
+ // We memoize the result on the symbolFn
10954
+ symbolFn[resolvedSymbol] = resolved;
10955
+ qrl.resolved = resolved;
10956
+ return resolved;
10957
+ });
10958
+ }
10959
+ else {
10960
+ // TODO cache the imported symbol but watch out for dev mode
10961
+ const imported = getPlatform().importSymbol(_containerEl, chunk, symbol);
10962
+ symbolRef = maybeThen(imported, (ref) => (qrl.resolved = wrapFn((symbolRef = ref))));
10963
+ }
10964
+ if (isPromise(symbolRef)) {
10965
+ symbolRef.then(() => emitUsedSymbol(symbol, ctx?.$element$, start), (err) => {
10966
+ console.error(`qrl ${symbol} failed to load`, err);
10967
+ // We shouldn't cache rejections, we can try again later
10968
+ symbolRef = null;
10969
+ });
10970
+ }
10167
10971
  return symbolRef;
10168
- }
10169
- if (containerEl) {
10170
- setContainer(containerEl);
10171
- }
10172
- if (chunk === '') {
10173
- // Sync QRL
10174
- assertDefined(_containerEl, 'Sync QRL must have container element');
10175
- const hash = _containerEl.getAttribute(QInstanceAttr);
10176
- const doc = _containerEl.ownerDocument;
10177
- const qFuncs = getQFuncs(doc, hash);
10178
- // No need to wrap, syncQRLs can't have captured scope
10179
- return (qrl.resolved = symbolRef = qFuncs[Number(symbol)]);
10180
- }
10181
- if (build.isBrowser && chunk) {
10182
- /** We run the QRL, so now the probability of the chunk is 100% */
10183
- preloader.p(chunk, 1);
10184
- }
10185
- const start = now();
10186
- const ctx = tryGetInvokeContext();
10187
- if (symbolFn !== null) {
10188
- symbolRef = symbolFn().then((module) => (qrl.resolved = wrapFn((symbolRef = module[symbol]))));
10189
- }
10190
- else {
10191
- const imported = getPlatform().importSymbol(_containerEl, chunk, symbol);
10192
- symbolRef = maybeThen(imported, (ref) => (qrl.resolved = wrapFn((symbolRef = ref))));
10193
- }
10194
- if (typeof symbolRef === 'object' && isPromise(symbolRef)) {
10195
- symbolRef.then(() => emitUsedSymbol(symbol, ctx?.$element$, start), (err) => {
10196
- console.error(`qrl ${symbol} failed to load`, err);
10197
- // We shouldn't cache rejections, we can try again later
10198
- symbolRef = null;
10199
- });
10200
- }
10201
- return symbolRef;
10202
- };
10972
+ };
10203
10973
  const createOrReuseInvocationContext = (invoke) => {
10204
10974
  if (invoke == null) {
10205
10975
  return newInvokeContext();
@@ -10217,7 +10987,6 @@
10217
10987
  getHash: () => hash,
10218
10988
  getCaptured: () => captureRef,
10219
10989
  resolve,
10220
- $resolveLazy$: resolveLazy,
10221
10990
  $setContainer$: setContainer,
10222
10991
  $chunk$: chunk,
10223
10992
  $symbol$: symbol,
@@ -10518,7 +11287,7 @@
10518
11287
  container.$serverData$ = opts.serverData || {};
10519
11288
  const host = container.rootVNode;
10520
11289
  container.$scheduler$(4 /* ChoreType.NODE_DIFF */, host, host, jsxNode);
10521
- await container.$scheduler$(255 /* ChoreType.WAIT_FOR_ALL */);
11290
+ await container.$scheduler$(255 /* ChoreType.WAIT_FOR_QUEUE */).$returnValue$;
10522
11291
  return {
10523
11292
  cleanup: () => {
10524
11293
  cleanup(container, container.rootVNode);
@@ -11058,13 +11827,13 @@
11058
11827
  const styleId = styleKey(styleQrl, i);
11059
11828
  const host = iCtx.$hostElement$;
11060
11829
  set(styleId);
11061
- const value = styleQrl.$resolveLazy$(iCtx.$element$);
11062
- if (isPromise(value)) {
11063
- value.then((val) => iCtx.$container$.$appendStyle$(transform(val, styleId), styleId, host, scoped));
11064
- throw value;
11830
+ if (styleQrl.resolved) {
11831
+ iCtx.$container$.$appendStyle$(transform(styleQrl.resolved, styleId), styleId, host, scoped);
11065
11832
  }
11066
11833
  else {
11067
- iCtx.$container$.$appendStyle$(transform(value, styleId), styleId, host, scoped);
11834
+ throw styleQrl
11835
+ .resolve()
11836
+ .then((val) => iCtx.$container$.$appendStyle$(transform(val, styleId), styleId, host, scoped));
11068
11837
  }
11069
11838
  return styleId;
11070
11839
  };
@@ -11245,13 +12014,13 @@
11245
12014
  return set(value);
11246
12015
  };
11247
12016
 
11248
- const useComputedCommon = (qrl, Class) => {
12017
+ const useComputedCommon = (qrl, createFn, options) => {
11249
12018
  const { val, set } = useSequentialScope();
11250
12019
  if (val) {
11251
12020
  return val;
11252
12021
  }
11253
12022
  assertQrl(qrl);
11254
- const signal = new Class(null, qrl);
12023
+ const signal = createFn(qrl, options);
11255
12024
  set(signal);
11256
12025
  // Note that we first save the signal
11257
12026
  // and then we throw to load the qrl
@@ -11260,8 +12029,8 @@
11260
12029
  return signal;
11261
12030
  };
11262
12031
  /** @internal */
11263
- const useComputedQrl = (qrl) => {
11264
- return useComputedCommon(qrl, ComputedSignalImpl);
12032
+ const useComputedQrl = (qrl, options) => {
12033
+ return useComputedCommon(qrl, createComputedSignal, options);
11265
12034
  };
11266
12035
  /**
11267
12036
  * Creates a computed signal which is calculated from the given function. A computed signal is a
@@ -11276,7 +12045,7 @@
11276
12045
  const useComputed$ = implicit$FirstArg(useComputedQrl);
11277
12046
 
11278
12047
  /** @internal */
11279
- const useSerializerQrl = (qrl) => useComputedCommon(qrl, SerializerSignalImpl);
12048
+ const useSerializerQrl = (qrl) => useComputedCommon(qrl, createSerializerSignal);
11280
12049
  /**
11281
12050
  * Creates a signal which holds a custom serializable value. It requires that the value implements
11282
12051
  * the `CustomSerializable` type, which means having a function under the `[SerializeSymbol]`
@@ -11355,8 +12124,8 @@
11355
12124
  set(task);
11356
12125
  useRunTask(task, eagerness);
11357
12126
  if (!isServerPlatform()) {
11358
- qrl.$resolveLazy$(iCtx.$element$);
11359
- iCtx.$container$.$scheduler$(32 /* ChoreType.VISIBLE */, task);
12127
+ qrl.resolve(iCtx.$element$);
12128
+ iCtx.$container$.$scheduler$(16 /* ChoreType.VISIBLE */, task);
11360
12129
  }
11361
12130
  };
11362
12131
  const useRunTask = (task, eagerness) => {
@@ -11531,8 +12300,8 @@
11531
12300
  const useVisibleTask$ = /*#__PURE__*/ implicit$FirstArg(useVisibleTaskQrl);
11532
12301
 
11533
12302
  /** @internal */
11534
- const useAsyncComputedQrl = (qrl) => {
11535
- return useComputedCommon(qrl, AsyncComputedSignalImpl);
12303
+ const useAsyncComputedQrl = (qrl, options) => {
12304
+ return useComputedCommon(qrl, createAsyncComputedSignal, options);
11536
12305
  };
11537
12306
  /**
11538
12307
  * Creates a computed signal which is calculated from the given function. A computed signal is a
@@ -11585,7 +12354,7 @@
11585
12354
  // the file 'qwik-prefetch-service-worker.js' is not located in /build/
11586
12355
  resolvedOpts.path = baseUrl + resolvedOpts.path;
11587
12356
  }
11588
- let code = PREFETCH_CODE.replace("'_URL_'", JSON.stringify(resolvedOpts.path));
12357
+ let code = PREFETCH_CODE.replace('"_URL_"', JSON.stringify(resolvedOpts.path.split('/').pop()));
11589
12358
  if (!build.isDev) {
11590
12359
  // consecutive spaces are indentation
11591
12360
  code = code.replaceAll(/\s\s+/gm, '');
@@ -11615,6 +12384,17 @@
11615
12384
  });
11616
12385
  });
11617
12386
  }
12387
+ if ('caches' in window) {
12388
+ caches
12389
+ .keys()
12390
+ .then((names) => {
12391
+ const cacheName = names.find((name) => name.startsWith('QwikBundles'));
12392
+ if (cacheName) {
12393
+ caches.delete(cacheName).catch(console.error);
12394
+ }
12395
+ })
12396
+ .catch(console.error);
12397
+ }
11618
12398
  }).toString();
11619
12399
  /**
11620
12400
  * @deprecated This is no longer needed as the preloading happens automatically in qrl-class. You
@@ -11623,6 +12403,16 @@
11623
12403
  */
11624
12404
  const PrefetchGraph = (_opts = {}) => null;
11625
12405
 
12406
+ //////////////////////////////////////////////////////////////////////////////////////////
12407
+ // Protect against duplicate imports
12408
+ //////////////////////////////////////////////////////////////////////////////////////////
12409
+ if (globalThis.__qwik) {
12410
+ console.error(`==============================================\n` +
12411
+ `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` +
12412
+ `==============================================\n`);
12413
+ }
12414
+ globalThis.__qwik = version;
12415
+
11626
12416
  Object.defineProperty(exports, "isBrowser", {
11627
12417
  enumerable: true,
11628
12418
  get: function () { return build.isBrowser; }
@@ -11656,41 +12446,63 @@
11656
12446
  exports._IMMUTABLE = _IMMUTABLE;
11657
12447
  exports._SharedContainer = _SharedContainer;
11658
12448
  exports._SubscriptionData = SubscriptionData;
12449
+ exports._UNINITIALIZED = _UNINITIALIZED;
11659
12450
  exports._VAR_PROPS = _VAR_PROPS;
11660
12451
  exports._deserialize = _deserialize;
11661
12452
  exports._dumpState = dumpState;
11662
12453
  exports._fnSignal = _fnSignal;
12454
+ exports._getConstProps = _getConstProps;
12455
+ exports._getContextContainer = _getContextContainer;
11663
12456
  exports._getContextElement = _getContextElement;
11664
12457
  exports._getContextEvent = _getContextEvent;
11665
12458
  exports._getDomContainer = getDomContainer;
11666
12459
  exports._getQContainerElement = _getQContainerElement;
12460
+ exports._getVarProps = _getVarProps;
12461
+ exports._hasStoreEffects = _hasStoreEffects;
11667
12462
  exports._isJSXNode = isJSXNode;
12463
+ exports._isStore = isStore;
11668
12464
  exports._isStringifiable = isStringifiable;
12465
+ exports._isTask = isTask;
11669
12466
  exports._jsxBranch = _jsxBranch;
11670
12467
  exports._jsxC = _jsxC;
11671
12468
  exports._jsxQ = _jsxQ;
11672
12469
  exports._jsxS = _jsxS;
11673
12470
  exports._jsxSorted = _jsxSorted;
11674
12471
  exports._jsxSplit = _jsxSplit;
12472
+ exports._mapApp_findIndx = mapApp_findIndx;
12473
+ exports._mapArray_get = mapArray_get;
12474
+ exports._mapArray_set = mapArray_set;
11675
12475
  exports._noopQrl = _noopQrl;
11676
12476
  exports._noopQrlDEV = _noopQrlDEV;
11677
12477
  exports._preprocessState = preprocessState;
11678
12478
  exports._qrlSync = _qrlSync;
11679
12479
  exports._regSymbol = _regSymbol;
12480
+ exports._resolveContextWithoutSequentialScope = _resolveContextWithoutSequentialScope;
11680
12481
  exports._restProps = _restProps;
11681
- exports._run = queueQRL;
12482
+ exports._run = _run;
12483
+ exports._serializationWeakRef = _serializationWeakRef;
11682
12484
  exports._serialize = _serialize;
11683
12485
  exports._task = scheduleTask;
11684
12486
  exports._verifySerializable = verifySerializable;
12487
+ exports._vnode_ensureElementInflated = vnode_ensureElementInflated;
12488
+ exports._vnode_getAttr = vnode_getAttr;
12489
+ exports._vnode_getAttrKeys = vnode_getAttrKeys;
12490
+ exports._vnode_getFirstChild = vnode_getFirstChild;
12491
+ exports._vnode_getNextSibling = vnode_getNextSibling;
12492
+ exports._vnode_getPropStartIndex = vnode_getPropStartIndex;
12493
+ exports._vnode_getProps = vnode_getProps;
12494
+ exports._vnode_isMaterialized = vnode_isMaterialized;
12495
+ exports._vnode_isTextVNode = vnode_isTextVNode;
12496
+ exports._vnode_isVirtualVNode = vnode_isVirtualVNode;
11685
12497
  exports._vnode_toString = vnode_toString;
11686
12498
  exports._waitUntilRendered = _waitUntilRendered;
11687
12499
  exports._walkJSX = _walkJSX;
11688
- exports._weakSerialize = _weakSerialize;
11689
12500
  exports._wrapProp = _wrapProp;
11690
12501
  exports._wrapSignal = _wrapSignal;
11691
- exports._wrapStore = _wrapStore;
11692
12502
  exports.component$ = component$;
11693
12503
  exports.componentQrl = componentQrl;
12504
+ exports.createAsyncComputed$ = createAsyncComputed$;
12505
+ exports.createAsyncComputedQrl = createAsyncComputedSignal;
11694
12506
  exports.createComputed$ = createComputed$;
11695
12507
  exports.createComputedQrl = createComputedSignal;
11696
12508
  exports.createContextId = createContextId;
@@ -11700,6 +12512,7 @@
11700
12512
  exports.createSignal = createSignal;
11701
12513
  exports.event$ = event$;
11702
12514
  exports.eventQrl = eventQrl;
12515
+ exports.forceStoreEffects = forceStoreEffects;
11703
12516
  exports.getDomContainer = getDomContainer;
11704
12517
  exports.getLocale = getLocale;
11705
12518
  exports.getPlatform = getPlatform;