@qwik.dev/core 2.0.0-beta.2 → 2.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * @qwik.dev/core 2.0.0-beta.2-dev+3ddc6c7
3
+ * @qwik.dev/core 2.0.0-beta.4-dev+9849dcf
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
@@ -72,6 +72,24 @@ const createAndLogError = (asyncThrow, message, ...optionalParams) => {
72
72
  return err;
73
73
  };
74
74
 
75
+ /** @private */
76
+ const isSerializableObject = (v) => {
77
+ const proto = Object.getPrototypeOf(v);
78
+ return proto === Object.prototype || proto === Array.prototype || proto === null;
79
+ };
80
+ const isObject = (v) => {
81
+ return typeof v === 'object' && v !== null;
82
+ };
83
+ const isArray = (v) => {
84
+ return Array.isArray(v);
85
+ };
86
+ const isString = (v) => {
87
+ return typeof v === 'string';
88
+ };
89
+ const isFunction = (v) => {
90
+ return typeof v === 'function';
91
+ };
92
+
75
93
  const codeToText = (code, ...parts) => {
76
94
  if (qDev) {
77
95
  // Keep one error, one line to make it easier to search for the error message.
@@ -116,7 +134,7 @@ const codeToText = (code, ...parts) => {
116
134
  if (parts.length) {
117
135
  text = text.replaceAll(/{{(\d+)}}/g, (_, index) => {
118
136
  let v = parts[index];
119
- if (v && typeof v === 'object' && v.constructor === Object) {
137
+ if (v && isObject(v) && v.constructor === Object) {
120
138
  v = JSON.stringify(v).slice(0, 50);
121
139
  }
122
140
  return v;
@@ -377,7 +395,7 @@ const delay = (timeout) => {
377
395
  setTimeout(resolve, timeout);
378
396
  });
379
397
  };
380
- // Retries a function that throws a promise.
398
+ /** Retries a function that throws a promise. */
381
399
  function retryOnPromise(fn, retryCount = 0) {
382
400
  const retryOrThrow = (e) => {
383
401
  if (isPromise(e) && retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
@@ -402,24 +420,6 @@ function retryOnPromise(fn, retryCount = 0) {
402
420
  }
403
421
  }
404
422
 
405
- /** @private */
406
- const isSerializableObject = (v) => {
407
- const proto = Object.getPrototypeOf(v);
408
- return proto === Object.prototype || proto === Array.prototype || proto === null;
409
- };
410
- const isObject = (v) => {
411
- return !!v && typeof v === 'object';
412
- };
413
- const isArray = (v) => {
414
- return Array.isArray(v);
415
- };
416
- const isString = (v) => {
417
- return typeof v === 'string';
418
- };
419
- const isFunction = (v) => {
420
- return typeof v === 'function';
421
- };
422
-
423
423
  const ASSERT_DISCLAIMER = 'Internal assert, this is likely caused by a bug in Qwik: ';
424
424
  function assertDefined(value, text, ...parts) {
425
425
  if (qDev) {
@@ -639,11 +639,9 @@ class WrappedSignalImpl extends SignalImpl {
639
639
  this.$funcStr$ = fnStr;
640
640
  this.$flags$ = flags;
641
641
  }
642
- $invalidate$() {
642
+ invalidate() {
643
643
  this.$flags$ |= 1 /* SignalFlags.INVALID */;
644
644
  this.$forceRunEffects$ = false;
645
- // We should only call subscribers if the calculation actually changed.
646
- // Therefore, we need to calculate the value now.
647
645
  this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, this.$hostElement$, this, this.$effects$);
648
646
  }
649
647
  /**
@@ -651,9 +649,8 @@ class WrappedSignalImpl extends SignalImpl {
651
649
  * remained the same object.
652
650
  */
653
651
  force() {
654
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
655
- this.$forceRunEffects$ = false;
656
- triggerEffects(this.$container$, this, this.$effects$);
652
+ this.$forceRunEffects$ = true;
653
+ this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, this.$hostElement$, this, this.$effects$);
657
654
  }
658
655
  get untrackedValue() {
659
656
  const didChange = this.$computeIfNeeded$();
@@ -711,7 +708,6 @@ function isSsrNode(value) {
711
708
  }
712
709
 
713
710
  let _context;
714
- /** @public */
715
711
  const tryGetInvokeContext = () => {
716
712
  if (!_context) {
717
713
  const context = typeof document !== 'undefined' && document && document.__q_context__;
@@ -732,6 +728,7 @@ const getInvokeContext = () => {
732
728
  }
733
729
  return ctx;
734
730
  };
731
+ /** @internal */
735
732
  const useInvokeContext = () => {
736
733
  const ctx = tryGetInvokeContext();
737
734
  if (!ctx || ctx.$event$ !== RenderEvent) {
@@ -777,7 +774,7 @@ const newInvokeContextFromTuple = ([element, event, url]) => {
777
774
  // TODO how about putting url and locale (and event/custom?) in to a "static" object
778
775
  const newInvokeContext = (locale, hostElement, element, event, url) => {
779
776
  // ServerRequestEvent has .locale, but it's not always defined.
780
- const $locale$ = locale || (typeof event === 'object' && event && 'locale' in event ? event.locale : undefined);
777
+ const $locale$ = locale || (event && isObject(event) && 'locale' in event ? event.locale : undefined);
781
778
  const ctx = {
782
779
  $url$: url,
783
780
  $i$: 0,
@@ -857,6 +854,13 @@ const _getContextEvent = () => {
857
854
  }
858
855
  };
859
856
  /** @internal */
857
+ const _getContextContainer = () => {
858
+ const iCtx = tryGetInvokeContext();
859
+ if (iCtx) {
860
+ return iCtx.$container$;
861
+ }
862
+ };
863
+ /** @internal */
860
864
  const _jsxBranch = (input) => {
861
865
  return input;
862
866
  };
@@ -1230,7 +1234,7 @@ const isRecoverable = (err) => {
1230
1234
  *
1231
1235
  * @public
1232
1236
  */
1233
- const version = "2.0.0-beta.2-dev+3ddc6c7";
1237
+ const version = "2.0.0-beta.4-dev+9849dcf";
1234
1238
 
1235
1239
  /** @internal */
1236
1240
  const EMPTY_ARRAY = [];
@@ -1395,6 +1399,8 @@ const _CONST_PROPS = Symbol('CONST');
1395
1399
  const _VAR_PROPS = Symbol('VAR');
1396
1400
  /** @internal @deprecated v1 compat */
1397
1401
  const _IMMUTABLE = Symbol('IMMUTABLE');
1402
+ /** @internal */
1403
+ const _UNINITIALIZED = Symbol('UNINITIALIZED');
1398
1404
 
1399
1405
  // <docs markdown="../../readme.md#implicit$FirstArg">
1400
1406
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
@@ -1464,14 +1470,15 @@ class ComputedSignalImpl extends SignalImpl {
1464
1470
  constructor(container, fn,
1465
1471
  // We need a separate flag to know when the computation needs running because
1466
1472
  // we need the old value to know if effects need running after computation
1467
- flags = 1 /* SignalFlags.INVALID */) {
1473
+ flags = 1 /* SignalFlags.INVALID */ |
1474
+ 16 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */) {
1468
1475
  // The value is used for comparison when signals trigger, which can only happen
1469
1476
  // when it was calculated before. Therefore we can pass whatever we like.
1470
1477
  super(container, NEEDS_COMPUTATION);
1471
1478
  this.$computeQrl$ = fn;
1472
1479
  this.$flags$ = flags;
1473
1480
  }
1474
- $invalidate$() {
1481
+ invalidate() {
1475
1482
  this.$flags$ |= 1 /* SignalFlags.INVALID */;
1476
1483
  this.$forceRunEffects$ = false;
1477
1484
  this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
@@ -1541,7 +1548,7 @@ class ComputedSignalImpl extends SignalImpl {
1541
1548
  */
1542
1549
  class SerializerSignalImpl extends ComputedSignalImpl {
1543
1550
  constructor(container, argQrl) {
1544
- super(container, argQrl);
1551
+ super(container, argQrl, 1 /* SignalFlags.INVALID */ | 16 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
1545
1552
  }
1546
1553
  $didInitialize$ = false;
1547
1554
  $computeIfNeeded$() {
@@ -1557,7 +1564,7 @@ class SerializerSignalImpl extends ComputedSignalImpl {
1557
1564
  const update = arg.update;
1558
1565
  const currentValue = this.$untrackedValue$ === NEEDS_COMPUTATION ? initial : this.$untrackedValue$;
1559
1566
  const untrackedValue = trackSignal(() => this.$didInitialize$
1560
- ? update?.(currentValue)
1567
+ ? update?.(currentValue) || currentValue
1561
1568
  : deserialize(currentValue), this, "." /* EffectProperty.VNODE */, this.$container$);
1562
1569
  const didChange = (this.$didInitialize$ && untrackedValue !== 'undefined') ||
1563
1570
  untrackedValue !== this.$untrackedValue$;
@@ -1646,8 +1653,7 @@ class StoreHandler {
1646
1653
  }
1647
1654
  const flags = this.$flags$;
1648
1655
  if (flags & 1 /* StoreFlags.RECURSIVE */ &&
1649
- typeof value === 'object' &&
1650
- value !== null &&
1656
+ isObject(value) &&
1651
1657
  !Object.isFrozen(value) &&
1652
1658
  !isStore(value) &&
1653
1659
  !Object.isFrozen(target)) {
@@ -1825,7 +1831,7 @@ class AsyncComputedSignalImpl extends ComputedSignalImpl {
1825
1831
  $loadingEffects$ = null;
1826
1832
  $errorEffects$ = null;
1827
1833
  $destroy$;
1828
- $promiseValue$ = null;
1834
+ $promiseValue$ = NEEDS_COMPUTATION;
1829
1835
  [_EFFECT_BACK_REF] = null;
1830
1836
  constructor(container, fn, flags = 1 /* SignalFlags.INVALID */) {
1831
1837
  super(container, fn, flags);
@@ -1859,6 +1865,10 @@ class AsyncComputedSignalImpl extends ComputedSignalImpl {
1859
1865
  get untrackedError() {
1860
1866
  return this.$untrackedError$;
1861
1867
  }
1868
+ invalidate() {
1869
+ super.invalidate();
1870
+ this.$promiseValue$ = NEEDS_COMPUTATION;
1871
+ }
1862
1872
  $computeIfNeeded$() {
1863
1873
  if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
1864
1874
  return false;
@@ -1866,11 +1876,12 @@ class AsyncComputedSignalImpl extends ComputedSignalImpl {
1866
1876
  const computeQrl = this.$computeQrl$;
1867
1877
  throwIfQRLNotResolved(computeQrl);
1868
1878
  const [cleanup] = cleanupFn(this, (err) => this.$container$?.handleError(err, null));
1869
- const untrackedValue = this.$promiseValue$ ??
1870
- computeQrl.getFn()({
1879
+ const untrackedValue = this.$promiseValue$ === NEEDS_COMPUTATION
1880
+ ? computeQrl.getFn()({
1871
1881
  track: trackFn(this, this.$container$),
1872
1882
  cleanup,
1873
- });
1883
+ })
1884
+ : this.$promiseValue$;
1874
1885
  if (isPromise(untrackedValue)) {
1875
1886
  this.untrackedLoading = true;
1876
1887
  this.untrackedError = null;
@@ -1881,11 +1892,12 @@ class AsyncComputedSignalImpl extends ComputedSignalImpl {
1881
1892
  this.untrackedError = null;
1882
1893
  })
1883
1894
  .catch((err) => {
1895
+ this.$promiseValue$ = err;
1884
1896
  this.untrackedLoading = false;
1885
1897
  this.untrackedError = err;
1886
1898
  });
1887
1899
  }
1888
- this.$promiseValue$ = null;
1900
+ this.$promiseValue$ = NEEDS_COMPUTATION;
1889
1901
  this.$flags$ &= -2 /* SignalFlags.INVALID */;
1890
1902
  const didChange = untrackedValue !== this.$untrackedValue$;
1891
1903
  if (didChange) {
@@ -1900,13 +1912,15 @@ const createSignal$1 = (value) => {
1900
1912
  return new SignalImpl(null, value);
1901
1913
  };
1902
1914
  /** @internal */
1903
- const createComputedSignal = (qrl) => {
1904
- throwIfQRLNotResolved(qrl);
1905
- return new ComputedSignalImpl(null, qrl);
1915
+ const createComputedSignal = (qrl, options) => {
1916
+ return new ComputedSignalImpl(options?.container || null, qrl, getComputedSignalFlags(options?.serializationStrategy || 'always'));
1917
+ };
1918
+ /** @internal */
1919
+ const createAsyncComputedSignal = (qrl, options) => {
1920
+ return new AsyncComputedSignalImpl(options?.container || null, qrl, getComputedSignalFlags(options?.serializationStrategy || 'never'));
1906
1921
  };
1907
1922
  /** @internal */
1908
1923
  const createSerializerSignal = (arg) => {
1909
- throwIfQRLNotResolved(arg);
1910
1924
  return new SerializerSignalImpl(null, arg);
1911
1925
  };
1912
1926
 
@@ -1925,11 +1939,22 @@ const createSignal = createSignal$1;
1925
1939
  * The QRL must be a function which returns the value of the signal. The function must not have side
1926
1940
  * effects, and it must be synchronous.
1927
1941
  *
1928
- * If you need the function to be async, use `useSignal` and `useTask$` instead.
1942
+ * If you need the function to be async, use `useAsyncComputed$` instead.
1929
1943
  *
1930
1944
  * @public
1931
1945
  */
1932
1946
  const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedSignal);
1947
+ /**
1948
+ * Create an async computed signal which is calculated from the given QRL. A computed signal is a
1949
+ * signal which is calculated from other signals or async operation. When the signals change, the
1950
+ * computed signal is recalculated.
1951
+ *
1952
+ * The QRL must be a function which returns the value of the signal. The function must not have side
1953
+ * effects, and it can be async.
1954
+ *
1955
+ * @public
1956
+ */
1957
+ const createAsyncComputed$ = /*#__PURE__*/ implicit$FirstArg(createAsyncComputedSignal);
1933
1958
  /**
1934
1959
  * Create a signal that holds a custom serializable value. See {@link useSerializer$} for more
1935
1960
  * details.
@@ -1989,17 +2014,6 @@ const _wrapProp = (...args) => {
1989
2014
  // the object is not reactive, so we can just return the value
1990
2015
  return obj[prop];
1991
2016
  };
1992
- /** @internal */
1993
- const _wrapStore = (obj, prop) => {
1994
- const target = getStoreTarget(obj);
1995
- const value = target[prop];
1996
- if (isSignal(value)) {
1997
- return value;
1998
- }
1999
- else {
2000
- return new WrappedSignalImpl(null, getProp, [obj, prop], null, 1 /* SignalFlags.INVALID */);
2001
- }
2002
- };
2003
2017
  /** @internal @deprecated v1 compat */
2004
2018
  const _wrapSignal = (obj, prop) => {
2005
2019
  const r = _wrapProp(obj, prop);
@@ -2421,7 +2435,6 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2421
2435
  if (!isInlineComponent) {
2422
2436
  container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
2423
2437
  container.setHostProp(renderHost, USE_ON_LOCAL_SEQ_IDX, null);
2424
- container.setHostProp(renderHost, ELEMENT_PROPS, props);
2425
2438
  }
2426
2439
  if (vnode_isVNode(renderHost)) {
2427
2440
  clearAllEffects(container, renderHost);
@@ -2435,9 +2448,12 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2435
2448
  return jsx;
2436
2449
  }, (err) => {
2437
2450
  if (isPromise(err) && retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
2438
- return err.then(() => executeComponentWithPromiseExceptionRetry(retryCount++));
2451
+ return err.then(() => executeComponentWithPromiseExceptionRetry(++retryCount));
2439
2452
  }
2440
2453
  else {
2454
+ if (retryCount >= MAX_RETRY_ON_PROMISE_COUNT) {
2455
+ throw new Error(`Max retry count of component execution reached`);
2456
+ }
2441
2457
  throw err;
2442
2458
  }
2443
2459
  });
@@ -4540,16 +4556,36 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
4540
4556
  shouldRender = true;
4541
4557
  }
4542
4558
  if (host) {
4543
- const vNodeProps = vnode_getProp(host, ELEMENT_PROPS, container.$getObjectById$);
4544
- shouldRender = shouldRender || propsDiffer(jsxProps, vNodeProps);
4559
+ let vNodeProps = vnode_getProp(host, ELEMENT_PROPS, container.$getObjectById$);
4560
+ const propsAreDifferent = propsDiffer(jsxProps, vNodeProps);
4561
+ shouldRender = shouldRender || propsAreDifferent;
4545
4562
  if (shouldRender) {
4563
+ if (propsAreDifferent) {
4564
+ if (vNodeProps) {
4565
+ // Reuse the same props instance, qrls can use the current props instance
4566
+ // as a capture ref, so we can't change it.
4567
+ // We need to do this directly, because normally we would subscribe to the signals
4568
+ // if any signal is there.
4569
+ vNodeProps[_CONST_PROPS] = jsxProps[_CONST_PROPS];
4570
+ vNodeProps[_VAR_PROPS] = jsxProps[_VAR_PROPS];
4571
+ }
4572
+ else if (jsxProps) {
4573
+ // If there is no props instance, create a new one.
4574
+ // We can do this because we are not using the props instance for anything else.
4575
+ vnode_setProp(host, ELEMENT_PROPS, jsxProps);
4576
+ vNodeProps = jsxProps;
4577
+ }
4578
+ }
4579
+ // Assign the new QRL instance to the host.
4580
+ // Unfortunately it is created every time, something to fix in the optimizer.
4581
+ vnode_setProp(host, OnRenderProp, componentQRL);
4546
4582
  /**
4547
4583
  * Mark host as not deleted. The host could have been marked as deleted if it there was a
4548
4584
  * cleanup run. Now we found it and want to reuse it, so we need to mark it as not
4549
4585
  * deleted.
4550
4586
  */
4551
4587
  host[0 /* VNodeProps.flags */] &= -33 /* VNodeFlags.Deleted */;
4552
- container.$scheduler$(6 /* ChoreType.COMPONENT */, host, componentQRL, jsxProps);
4588
+ container.$scheduler$(6 /* ChoreType.COMPONENT */, host, componentQRL, vNodeProps);
4553
4589
  }
4554
4590
  }
4555
4591
  descendContentToProject(jsxNode.children, host);
@@ -4980,10 +5016,12 @@ function getResourceValueAsPromise(props) {
4980
5016
  return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
4981
5017
  }
4982
5018
  else if (isSignal(resource)) {
4983
- return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
5019
+ const value = retryOnPromise(() => resource.value);
5020
+ const promise = isPromise(value) ? value : Promise.resolve(value);
5021
+ return promise.then(useBindInvokeContext(props.onResolved));
4984
5022
  }
4985
5023
  else {
4986
- return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
5024
+ return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved));
4987
5025
  }
4988
5026
  }
4989
5027
  const _createResourceReturn = (opts) => {
@@ -5509,22 +5547,23 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
5509
5547
  {
5510
5548
  const target = chore.$target$;
5511
5549
  const effects = chore.$payload$;
5550
+ const ctx = newInvokeContext();
5551
+ ctx.$container$ = container;
5512
5552
  if (target instanceof ComputedSignalImpl || target instanceof WrappedSignalImpl) {
5513
5553
  const forceRunEffects = target.$forceRunEffects$;
5514
5554
  target.$forceRunEffects$ = false;
5515
- if (!target.$effects$?.size) {
5555
+ if (!effects?.size && !forceRunEffects) {
5516
5556
  break;
5517
5557
  }
5518
- returnValue = retryOnPromise(() => {
5519
- if (target.$computeIfNeeded$() || forceRunEffects) {
5520
- triggerEffects(container, target, effects);
5558
+ // needed for computed signals and throwing QRLs
5559
+ returnValue = maybeThen(retryOnPromise(() => invoke.call(target, ctx, target.$computeIfNeeded$)), (didChange) => {
5560
+ if (didChange || forceRunEffects) {
5561
+ return retryOnPromise(() => triggerEffects(container, target, effects));
5521
5562
  }
5522
5563
  });
5523
5564
  }
5524
5565
  else {
5525
- returnValue = retryOnPromise(() => {
5526
- triggerEffects(container, target, effects);
5527
- });
5566
+ returnValue = retryOnPromise(() => triggerEffects(container, target, effects));
5528
5567
  }
5529
5568
  }
5530
5569
  break;
@@ -5606,7 +5645,10 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
5606
5645
  return idxDiff;
5607
5646
  }
5608
5647
  // If the host is the same (or missing), and the type is the same, we need to compare the target.
5609
- if (a.$target$ !== b.$target$ || a.$payload$ !== b.$payload$) {
5648
+ if (a.$target$ !== b.$target$) {
5649
+ if (isQrl(a.$target$) && isQrl(b.$target$) && a.$target$.$hash$ === b.$target$.$hash$) {
5650
+ return 0;
5651
+ }
5610
5652
  // 1 means that we are going to process chores as FIFO
5611
5653
  return 1;
5612
5654
  }
@@ -5654,7 +5696,7 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
5654
5696
  * multiple times during component execution. For this reason it is necessary for us to update
5655
5697
  * the chore with the latest result of the signal.
5656
5698
  */
5657
- if (existing.$type$ === 4 /* ChoreType.NODE_DIFF */) {
5699
+ if (existing.$payload$ !== value.$payload$) {
5658
5700
  existing.$payload$ = value.$payload$;
5659
5701
  }
5660
5702
  if (existing.$executed$) {
@@ -6454,7 +6496,7 @@ const triggerEffects = (container, signal, effects) => {
6454
6496
  container.$scheduler$(1 /* ChoreType.QRL_RESOLVE */, null, consumer.$computeQrl$);
6455
6497
  }
6456
6498
  }
6457
- consumer.$invalidate$();
6499
+ consumer.invalidate();
6458
6500
  }
6459
6501
  else if (property === ":" /* EffectProperty.COMPONENT */) {
6460
6502
  const host = consumer;
@@ -6489,7 +6531,23 @@ const triggerEffects = (container, signal, effects) => {
6489
6531
  };
6490
6532
  /** @internal */
6491
6533
  const isSerializerObj = (obj) => {
6492
- return (typeof obj === 'object' && obj !== null && typeof obj[SerializerSymbol] === 'function');
6534
+ return isObject(obj) && typeof obj[SerializerSymbol] === 'function';
6535
+ };
6536
+ const getComputedSignalFlags = (serializationStrategy) => {
6537
+ let flags = 1 /* SignalFlags.INVALID */;
6538
+ switch (serializationStrategy) {
6539
+ // TODO: implement this in the future
6540
+ // case 'auto':
6541
+ // flags |= ComputedSignalFlags.SERIALIZATION_STRATEGY_AUTO;
6542
+ // break;
6543
+ case 'never':
6544
+ flags |= 8 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
6545
+ break;
6546
+ case 'always':
6547
+ flags |= 16 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
6548
+ break;
6549
+ }
6550
+ return flags;
6493
6551
  };
6494
6552
 
6495
6553
  const stringifyPath = [];
@@ -7786,12 +7844,15 @@ const processVNodeData = (vData, callback) => {
7786
7844
  let nextToConsumeIdx = 0;
7787
7845
  let ch = 0;
7788
7846
  let peekCh = 0;
7847
+ const getChar = (idx) => {
7848
+ return idx < vData.length ? vData.charCodeAt(idx) : 0;
7849
+ };
7789
7850
  const peek = () => {
7790
7851
  if (peekCh !== 0) {
7791
7852
  return peekCh;
7792
7853
  }
7793
7854
  else {
7794
- return (peekCh = nextToConsumeIdx < vData.length ? vData.charCodeAt(nextToConsumeIdx) : 0);
7855
+ return (peekCh = getChar(nextToConsumeIdx));
7795
7856
  }
7796
7857
  };
7797
7858
  const consume = () => {
@@ -7812,7 +7873,7 @@ const processVNodeData = (vData, callback) => {
7812
7873
  return vData.substring(start, nextToConsumeIdx);
7813
7874
  };
7814
7875
  while (peek() !== 0) {
7815
- callback(peek, consumeValue, consume, nextToConsumeIdx);
7876
+ callback(peek, consumeValue, consume, getChar, nextToConsumeIdx);
7816
7877
  }
7817
7878
  };
7818
7879
  const vnode_getNextSibling = (vnode) => {
@@ -8026,21 +8087,19 @@ function materializeFromVNodeData(vParent, vData, element, child) {
8026
8087
  let textIdx = 0;
8027
8088
  let combinedText = null;
8028
8089
  let container = null;
8029
- processVNodeData(vData, (peek, consumeValue, consume, nextToConsumeIdx) => {
8090
+ processVNodeData(vData, (peek, consumeValue, consume, getChar, nextToConsumeIdx) => {
8030
8091
  if (isNumber(peek())) {
8031
8092
  // Element counts get encoded as numbers.
8032
- while (!isElement(child)) {
8093
+ while (!isElement(child) ||
8094
+ // We pretend that style element's don't exist as they can get moved out.
8095
+ // skip over style elements, as those need to be moved to the head
8096
+ // and are not included in the counts.
8097
+ isQStyleElement(child)) {
8033
8098
  child = fastNextSibling(child);
8034
8099
  if (!child) {
8035
8100
  throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), nextToConsumeIdx]);
8036
8101
  }
8037
8102
  }
8038
- // We pretend that style element's don't exist as they can get moved out.
8039
- while (isQStyleElement(child)) {
8040
- // skip over style elements, as those need to be moved to the head
8041
- // and are not included in the counts.
8042
- child = fastNextSibling(child);
8043
- }
8044
8103
  combinedText = null;
8045
8104
  previousTextNode = null;
8046
8105
  let value = 0;
@@ -8072,7 +8131,17 @@ function materializeFromVNodeData(vParent, vData, element, child) {
8072
8131
  vnode_setAttr(null, vParent, ELEMENT_PROPS, consumeValue());
8073
8132
  }
8074
8133
  else if (peek() === VNodeDataChar.KEY) {
8075
- vnode_setAttr(null, vParent, ELEMENT_KEY, consumeValue());
8134
+ const isEscapedValue = getChar(nextToConsumeIdx + 1) === VNodeDataChar.SEPARATOR;
8135
+ let value;
8136
+ if (isEscapedValue) {
8137
+ consume();
8138
+ value = decodeURI(consumeValue());
8139
+ consume();
8140
+ }
8141
+ else {
8142
+ value = consumeValue();
8143
+ }
8144
+ vnode_setAttr(null, vParent, ELEMENT_KEY, value);
8076
8145
  }
8077
8146
  else if (peek() === VNodeDataChar.SEQ) {
8078
8147
  vnode_setAttr(null, vParent, ELEMENT_SEQ, consumeValue());
@@ -8118,6 +8187,10 @@ function materializeFromVNodeData(vParent, vData, element, child) {
8118
8187
  vnode_setAttr(null, vParent, QSlot, consumeValue());
8119
8188
  }
8120
8189
  else {
8190
+ // skip over style elements in front of text nodes, where text node is the first child (except the style node)
8191
+ while (isQStyleElement(child)) {
8192
+ child = fastNextSibling(child);
8193
+ }
8121
8194
  const textNode = child && fastNodeType(child) === /* Node.TEXT_NODE */ 3 ? child : null;
8122
8195
  // must be alphanumeric
8123
8196
  if (combinedText === null) {
@@ -8214,7 +8287,7 @@ const VNodeArray = class VNode extends Array {
8214
8287
  /** There's [documentation](./serialization.md) */
8215
8288
  const deserializedProxyMap = new WeakMap();
8216
8289
  const isDeserializerProxy = (value) => {
8217
- return typeof value === 'object' && value !== null && SERIALIZER_PROXY_UNWRAP in value;
8290
+ return isObject(value) && SERIALIZER_PROXY_UNWRAP in value;
8218
8291
  };
8219
8292
  const SERIALIZER_PROXY_UNWRAP = Symbol('UNWRAP');
8220
8293
  /** Call this on the serialized root state */
@@ -8422,9 +8495,7 @@ const inflate = (container, target, typeId, data) => {
8422
8495
  if (hasValue) {
8423
8496
  asyncComputed.$untrackedValue$ = d[6];
8424
8497
  }
8425
- else {
8426
- asyncComputed.$flags$ |= 1 /* SignalFlags.INVALID */;
8427
- }
8498
+ asyncComputed.$flags$ |= 1 /* SignalFlags.INVALID */;
8428
8499
  break;
8429
8500
  }
8430
8501
  // Inflating a SerializerSignal is the same as inflating a ComputedSignal
@@ -8524,7 +8595,7 @@ const inflate = (container, target, typeId, data) => {
8524
8595
  propsProxy[_VAR_PROPS] = data === 0 ? {} : data[0];
8525
8596
  propsProxy[_CONST_PROPS] = data[1];
8526
8597
  break;
8527
- case 35 /* TypeIds.EffectData */: {
8598
+ case 35 /* TypeIds.SubscriptionData */: {
8528
8599
  const effectData = target;
8529
8600
  effectData.data.$scopedStyleIdPrefix$ = data[0];
8530
8601
  effectData.data.$isConst$ = data[1];
@@ -8545,6 +8616,7 @@ const _constants = [
8545
8616
  EMPTY_OBJ,
8546
8617
  NEEDS_COMPUTATION,
8547
8618
  STORE_ALL_PROPS,
8619
+ _UNINITIALIZED,
8548
8620
  Slot,
8549
8621
  Fragment,
8550
8622
  NaN,
@@ -8564,6 +8636,7 @@ const _constantNames = [
8564
8636
  'EMPTY_OBJ',
8565
8637
  'NEEDS_COMPUTATION',
8566
8638
  'STORE_ALL_PROPS',
8639
+ '_UNINITIALIZED',
8567
8640
  'Slot',
8568
8641
  'Fragment',
8569
8642
  'NaN',
@@ -8585,7 +8658,13 @@ const allocate = (container, typeId, value) => {
8585
8658
  if (!container.$forwardRefs$) {
8586
8659
  throw qError(18 /* QError.serializeErrorCannotAllocate */, ['forward ref']);
8587
8660
  }
8588
- return container.$getObjectById$(container.$forwardRefs$[value]);
8661
+ const rootRef = container.$forwardRefs$[value];
8662
+ if (rootRef === -1) {
8663
+ return _UNINITIALIZED;
8664
+ }
8665
+ else {
8666
+ return container.$getObjectById$(rootRef);
8667
+ }
8589
8668
  case 2 /* TypeIds.ForwardRefs */:
8590
8669
  return value;
8591
8670
  case 3 /* TypeIds.Constant */:
@@ -8680,7 +8759,7 @@ const allocate = (container, typeId, value) => {
8680
8759
  else {
8681
8760
  throw qError(17 /* QError.serializeErrorExpectedVNode */, [typeof vNode]);
8682
8761
  }
8683
- case 35 /* TypeIds.EffectData */:
8762
+ case 35 /* TypeIds.SubscriptionData */:
8684
8763
  return new SubscriptionData({});
8685
8764
  default:
8686
8765
  throw qError(18 /* QError.serializeErrorCannotAllocate */, [typeId]);
@@ -8742,7 +8821,7 @@ DomRefConstructor, symbolToChunkResolver, getProp, setProp, storeProxyMap, write
8742
8821
  };
8743
8822
  }
8744
8823
  const seenObjsMap = new Map();
8745
- const rootsPathMap = new Map();
8824
+ const objectPathStringCache = new Map();
8746
8825
  const syncFnMap = new Map();
8747
8826
  const syncFns = [];
8748
8827
  const roots = [];
@@ -8751,7 +8830,7 @@ DomRefConstructor, symbolToChunkResolver, getProp, setProp, storeProxyMap, write
8751
8830
  return seenObjsMap.set(obj, { $parent$: parent, $index$: index, $rootIndex$: -1 });
8752
8831
  };
8753
8832
  const $addRootPath$ = (obj) => {
8754
- const rootPath = rootsPathMap.get(obj);
8833
+ const rootPath = objectPathStringCache.get(obj);
8755
8834
  if (rootPath) {
8756
8835
  return rootPath;
8757
8836
  }
@@ -8770,7 +8849,7 @@ DomRefConstructor, symbolToChunkResolver, getProp, setProp, storeProxyMap, write
8770
8849
  current = seenObjsMap.get(current.$parent$);
8771
8850
  }
8772
8851
  const pathStr = path.length > 1 ? path.join(' ') : path.length ? path[0] : seen.$index$;
8773
- rootsPathMap.set(obj, pathStr);
8852
+ objectPathStringCache.set(obj, pathStr);
8774
8853
  return pathStr;
8775
8854
  };
8776
8855
  const $addRoot$ = (obj, parent = null) => {
@@ -8837,7 +8916,7 @@ DomRefConstructor, symbolToChunkResolver, getProp, setProp, storeProxyMap, write
8837
8916
  $storeProxyMap$: storeProxyMap,
8838
8917
  $getProp$: getProp,
8839
8918
  $setProp$: setProp,
8840
- $pathMap$: rootsPathMap,
8919
+ $objectPathStringCache$: objectPathStringCache,
8841
8920
  };
8842
8921
  };
8843
8922
  function $discoverRoots$(serializationContext, obj, parent, index) {
@@ -8861,7 +8940,8 @@ const discoverValuesForVNodeData = (vnodeData, callback) => {
8861
8940
  for (let i = 1; i < value.length; i += 2) {
8862
8941
  const keyValue = value[i - 1];
8863
8942
  const attrValue = value[i];
8864
- if (typeof attrValue === 'string' ||
8943
+ if (attrValue == null ||
8944
+ typeof attrValue === 'string' ||
8865
8945
  // skip empty props
8866
8946
  (keyValue === ELEMENT_PROPS &&
8867
8947
  Object.keys(attrValue).length === 0)) {
@@ -8886,6 +8966,14 @@ class PromiseResult {
8886
8966
  this.$qrl$ = $qrl$;
8887
8967
  }
8888
8968
  }
8969
+ class SerializationWeakRef {
8970
+ $obj$;
8971
+ constructor($obj$) {
8972
+ this.$obj$ = $obj$;
8973
+ }
8974
+ }
8975
+ /** @internal */
8976
+ const _serializationWeakRef = (obj) => new SerializationWeakRef(obj);
8889
8977
  /**
8890
8978
  * Format:
8891
8979
  *
@@ -8896,12 +8984,14 @@ class PromiseResult {
8896
8984
  * - Therefore root indexes need to be doubled to get the actual index.
8897
8985
  */
8898
8986
  async function serialize(serializationContext) {
8899
- const { $writer$, $isSsrNode$, $isDomRef$, $storeProxyMap$, $addRoot$, $pathMap$, $wasSeen$ } = serializationContext;
8987
+ const { $writer$, $isSsrNode$, $isDomRef$, $storeProxyMap$, $addRoot$, $objectPathStringCache$, $wasSeen$, } = serializationContext;
8900
8988
  let depth = 0;
8989
+ let rootIdx = 0;
8901
8990
  const forwardRefs = [];
8902
8991
  let forwardRefsId = 0;
8903
8992
  const promises = new Set();
8904
8993
  const preloadQrls = new Set();
8994
+ const s11nWeakRefs = new Map();
8905
8995
  let parent = null;
8906
8996
  const isRootObject = () => depth === 0;
8907
8997
  const outputArray = (value, writeFn) => {
@@ -8946,19 +9036,41 @@ async function serialize(serializationContext) {
8946
9036
  };
8947
9037
  const addPreloadQrl = (qrl) => {
8948
9038
  preloadQrls.add(qrl);
8949
- serializationContext.$addRoot$(qrl, null);
9039
+ serializationContext.$addRoot$(qrl);
8950
9040
  };
8951
- const outputRootRef = (value, rootDepth = 0) => {
9041
+ const outputAsRootRef = (value, rootDepth = 0) => {
8952
9042
  const seen = $wasSeen$(value);
8953
- const rootRefPath = $pathMap$.get(value);
9043
+ const rootRefPath = $objectPathStringCache$.get(value);
9044
+ // Objects are the only way to create circular dependencies.
9045
+ // So the first thing to to is to see if we have a circular dependency.
9046
+ // (NOTE: For root objects we need to serialize them regardless if we have seen
9047
+ // them before, otherwise the root object reference will point to itself.)
9048
+ // Also note that depth will be 1 for objects in root
8954
9049
  if (rootDepth === depth && seen && seen.$parent$ !== null && rootRefPath) {
8955
9050
  output(0 /* TypeIds.RootRef */, rootRefPath);
8956
9051
  return true;
8957
9052
  }
8958
9053
  else if (depth > rootDepth && seen && seen.$rootIndex$ !== -1) {
9054
+ // We have seen this object before, so we can serialize it as a reference.
9055
+ // Otherwise serialize as normal
8959
9056
  output(0 /* TypeIds.RootRef */, seen.$rootIndex$);
8960
9057
  return true;
8961
9058
  }
9059
+ else if (s11nWeakRefs.has(value)) {
9060
+ const forwardRefId = s11nWeakRefs.get(value);
9061
+ // We see the object again, we must now make it a root and update the forward ref
9062
+ if (rootDepth === depth) {
9063
+ // It's already a root
9064
+ forwardRefs[forwardRefId] = rootIdx;
9065
+ }
9066
+ else {
9067
+ // ref
9068
+ const rootRef = $addRoot$(value);
9069
+ output(0 /* TypeIds.RootRef */, rootRef);
9070
+ forwardRefs[forwardRefId] = rootRef;
9071
+ return true;
9072
+ }
9073
+ }
8962
9074
  return false;
8963
9075
  };
8964
9076
  const writeValue = (value) => {
@@ -8973,13 +9085,13 @@ async function serialize(serializationContext) {
8973
9085
  }
8974
9086
  else if (typeof value === 'function') {
8975
9087
  if (value === Slot) {
8976
- output(3 /* TypeIds.Constant */, 9 /* Constants.Slot */);
9088
+ output(3 /* TypeIds.Constant */, 10 /* Constants.Slot */);
8977
9089
  }
8978
9090
  else if (value === Fragment) {
8979
- output(3 /* TypeIds.Constant */, 10 /* Constants.Fragment */);
9091
+ output(3 /* TypeIds.Constant */, 11 /* Constants.Fragment */);
8980
9092
  }
8981
9093
  else if (isQrl(value)) {
8982
- if (!outputRootRef(value)) {
9094
+ if (!outputAsRootRef(value)) {
8983
9095
  const qrl = qrlToString(serializationContext, value);
8984
9096
  const type = preloadQrls.has(value) ? 21 /* TypeIds.PreloadQRL */ : 20 /* TypeIds.QRL */;
8985
9097
  if (isRootObject()) {
@@ -9002,19 +9114,19 @@ async function serialize(serializationContext) {
9002
9114
  }
9003
9115
  else if (typeof value === 'number') {
9004
9116
  if (Number.isNaN(value)) {
9005
- output(3 /* TypeIds.Constant */, 11 /* Constants.NaN */);
9117
+ output(3 /* TypeIds.Constant */, 12 /* Constants.NaN */);
9006
9118
  }
9007
9119
  else if (!Number.isFinite(value)) {
9008
- output(3 /* TypeIds.Constant */, value < 0 ? 13 /* Constants.NegativeInfinity */ : 12 /* Constants.PositiveInfinity */);
9120
+ output(3 /* TypeIds.Constant */, value < 0 ? 14 /* Constants.NegativeInfinity */ : 13 /* Constants.PositiveInfinity */);
9009
9121
  }
9010
9122
  else if (value === Number.MAX_SAFE_INTEGER) {
9011
- output(3 /* TypeIds.Constant */, 14 /* Constants.MaxSafeInt */);
9123
+ output(3 /* TypeIds.Constant */, 15 /* Constants.MaxSafeInt */);
9012
9124
  }
9013
9125
  else if (value === Number.MAX_SAFE_INTEGER - 1) {
9014
- output(3 /* TypeIds.Constant */, 15 /* Constants.AlmostMaxSafeInt */);
9126
+ output(3 /* TypeIds.Constant */, 16 /* Constants.AlmostMaxSafeInt */);
9015
9127
  }
9016
9128
  else if (value === Number.MIN_SAFE_INTEGER) {
9017
- output(3 /* TypeIds.Constant */, 16 /* Constants.MinSafeInt */);
9129
+ output(3 /* TypeIds.Constant */, 17 /* Constants.MinSafeInt */);
9018
9130
  }
9019
9131
  else {
9020
9132
  output(4 /* TypeIds.Number */, value);
@@ -9044,7 +9156,7 @@ async function serialize(serializationContext) {
9044
9156
  output(3 /* TypeIds.Constant */, 4 /* Constants.EmptyString */);
9045
9157
  }
9046
9158
  else {
9047
- if (!outputRootRef(value)) {
9159
+ if (!outputAsRootRef(value)) {
9048
9160
  output(5 /* TypeIds.String */, value);
9049
9161
  }
9050
9162
  }
@@ -9058,6 +9170,9 @@ async function serialize(serializationContext) {
9058
9170
  else if (value === STORE_ALL_PROPS) {
9059
9171
  output(3 /* TypeIds.Constant */, 8 /* Constants.STORE_ALL_PROPS */);
9060
9172
  }
9173
+ else if (value === _UNINITIALIZED) {
9174
+ output(3 /* TypeIds.Constant */, 9 /* Constants.UNINITIALIZED */);
9175
+ }
9061
9176
  else {
9062
9177
  throw qError(20 /* QError.serializeErrorUnknownType */, [typeof value]);
9063
9178
  }
@@ -9067,14 +9182,11 @@ async function serialize(serializationContext) {
9067
9182
  * The object writer outputs an array object (without type prefix) and this increases the depth
9068
9183
  * for the objects within (depth 1).
9069
9184
  */
9070
- // Objects are the only way to create circular dependencies.
9071
- // So the first thing to to is to see if we have a circular dependency.
9072
- // (NOTE: For root objects we need to serialize them regardless if we have seen
9073
- // them before, otherwise the root object reference will point to itself.)
9074
- // Also note that depth will be 1 for objects in root
9075
- if (outputRootRef(value, 1)) {
9185
+ if (outputAsRootRef(value, 1)) {
9076
9186
  return;
9077
9187
  }
9188
+ // handle custom serializers
9189
+ // add to the seen map
9078
9190
  if (isPropsProxy(value)) {
9079
9191
  const varProps = value[_VAR_PROPS];
9080
9192
  const constProps = value[_CONST_PROPS];
@@ -9086,7 +9198,7 @@ async function serialize(serializationContext) {
9086
9198
  output(34 /* TypeIds.PropsProxy */, out);
9087
9199
  }
9088
9200
  else if (value instanceof SubscriptionData) {
9089
- output(35 /* TypeIds.EffectData */, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
9201
+ output(35 /* TypeIds.SubscriptionData */, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
9090
9202
  }
9091
9203
  else if (isStore(value)) {
9092
9204
  if (isResource(value)) {
@@ -9162,14 +9274,6 @@ async function serialize(serializationContext) {
9162
9274
  output(1 /* TypeIds.ForwardRef */, forwardRefId);
9163
9275
  return;
9164
9276
  }
9165
- /**
9166
- * Special case: when a Signal value is an SSRNode, it always needs to be a DOM ref instead.
9167
- * It can never be meant to become a vNode, because vNodes are internal only.
9168
- */
9169
- const v = value instanceof ComputedSignalImpl &&
9170
- (value.$flags$ & 1 /* SignalFlags.INVALID */ || fastSkipSerialize(value.$untrackedValue$))
9171
- ? NEEDS_COMPUTATION
9172
- : value.$untrackedValue$;
9173
9277
  if (value instanceof WrappedSignalImpl) {
9174
9278
  output(26 /* TypeIds.WrappedSignal */, [
9175
9279
  ...serializeWrappingFn(serializationContext, value),
@@ -9179,35 +9283,34 @@ async function serialize(serializationContext) {
9179
9283
  ...(value.$effects$ || []),
9180
9284
  ]);
9181
9285
  }
9182
- else if (value instanceof AsyncComputedSignalImpl) {
9183
- addPreloadQrl(value.$computeQrl$);
9184
- const out = [
9185
- value.$computeQrl$,
9186
- value.$effects$,
9187
- value.$loadingEffects$,
9188
- value.$errorEffects$,
9189
- value.$untrackedLoading$,
9190
- value.$untrackedError$,
9191
- ];
9192
- if (v !== NEEDS_COMPUTATION) {
9193
- out.push(v);
9194
- }
9195
- output(28 /* TypeIds.AsyncComputedSignal */, out);
9196
- }
9197
9286
  else if (value instanceof ComputedSignalImpl) {
9287
+ let v = value.$untrackedValue$;
9288
+ const shouldAlwaysSerialize = value.$flags$ & 16 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
9289
+ const shouldNeverSerialize = value.$flags$ & 8 /* ComputedSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
9290
+ const isInvalid = value.$flags$ & 1 /* SignalFlags.INVALID */;
9291
+ const isSkippable = fastSkipSerialize(value.$untrackedValue$);
9292
+ if (shouldAlwaysSerialize) {
9293
+ v = value.$untrackedValue$;
9294
+ }
9295
+ else if (shouldNeverSerialize) {
9296
+ v = NEEDS_COMPUTATION;
9297
+ }
9298
+ else if (isInvalid || isSkippable) {
9299
+ v = NEEDS_COMPUTATION;
9300
+ }
9198
9301
  addPreloadQrl(value.$computeQrl$);
9199
- const out = [
9200
- value.$computeQrl$,
9201
- // TODO check if we can use domVRef for effects
9202
- value.$effects$,
9203
- ];
9302
+ const out = [value.$computeQrl$, value.$effects$];
9303
+ const isAsync = value instanceof AsyncComputedSignalImpl;
9304
+ if (isAsync) {
9305
+ out.push(value.$loadingEffects$, value.$errorEffects$, value.$untrackedLoading$, value.$untrackedError$);
9306
+ }
9204
9307
  if (v !== NEEDS_COMPUTATION) {
9205
9308
  out.push(v);
9206
9309
  }
9207
- output(27 /* TypeIds.ComputedSignal */, out);
9310
+ output(isAsync ? 28 /* TypeIds.AsyncComputedSignal */ : 27 /* TypeIds.ComputedSignal */, out);
9208
9311
  }
9209
9312
  else {
9210
- output(25 /* TypeIds.Signal */, [v, ...(value.$effects$ || [])]);
9313
+ output(25 /* TypeIds.Signal */, [value.$untrackedValue$, ...(value.$effects$ || [])]);
9211
9314
  }
9212
9315
  }
9213
9316
  else if (value instanceof URL) {
@@ -9342,6 +9445,12 @@ async function serialize(serializationContext) {
9342
9445
  const out = btoa(buf).replace(/=+$/, '');
9343
9446
  output(19 /* TypeIds.Uint8Array */, out);
9344
9447
  }
9448
+ else if (value instanceof SerializationWeakRef) {
9449
+ const forwardRefId = forwardRefsId++;
9450
+ s11nWeakRefs.set(value.$obj$, forwardRefId);
9451
+ forwardRefs[forwardRefId] = -1;
9452
+ output(1 /* TypeIds.ForwardRef */, forwardRefId);
9453
+ }
9345
9454
  else if (vnode_isVNode(value)) {
9346
9455
  output(3 /* TypeIds.Constant */, 0 /* Constants.Undefined */);
9347
9456
  }
@@ -9365,21 +9474,20 @@ async function serialize(serializationContext) {
9365
9474
  }
9366
9475
  const outputRoots = async () => {
9367
9476
  $writer$.write('[');
9368
- let lastRootsLength = 0;
9369
9477
  let rootsLength = serializationContext.$roots$.length;
9370
- while (lastRootsLength < rootsLength || promises.size) {
9371
- if (lastRootsLength !== 0) {
9478
+ while (rootIdx < rootsLength || promises.size) {
9479
+ if (rootIdx !== 0) {
9372
9480
  $writer$.write(',');
9373
9481
  }
9374
9482
  let separator = false;
9375
- for (let i = lastRootsLength; i < rootsLength; i++) {
9483
+ for (; rootIdx < rootsLength; rootIdx++) {
9376
9484
  if (separator) {
9377
9485
  $writer$.write(',');
9378
9486
  }
9379
9487
  else {
9380
9488
  separator = true;
9381
9489
  }
9382
- writeValue(serializationContext.$roots$[i]);
9490
+ writeValue(serializationContext.$roots$[rootIdx]);
9383
9491
  }
9384
9492
  if (promises.size) {
9385
9493
  try {
@@ -9389,7 +9497,6 @@ async function serialize(serializationContext) {
9389
9497
  // ignore rejections, they will be serialized as rejected promises
9390
9498
  }
9391
9499
  }
9392
- lastRootsLength = rootsLength;
9393
9500
  rootsLength = serializationContext.$roots$.length;
9394
9501
  }
9395
9502
  if (forwardRefs.length) {
@@ -9695,7 +9802,7 @@ function shouldTrackObj(obj) {
9695
9802
  return (
9696
9803
  // THINK: Not sure if we need to keep track of functions (QRLs) Let's skip them for now.
9697
9804
  // and see if we have a test case which requires them.
9698
- (typeof obj === 'object' && obj !== null) ||
9805
+ isObject(obj) ||
9699
9806
  /**
9700
9807
  * We track all strings greater than 1 character, because those take at least 6 bytes to encode
9701
9808
  * and even with 999 root objects it saves one byte per reference. Tracking more objects makes
@@ -9723,9 +9830,7 @@ function isResource(value) {
9723
9830
  return '__brand' in value && value.__brand === 'resource';
9724
9831
  }
9725
9832
  const frameworkType = (obj) => {
9726
- return ((typeof obj === 'object' &&
9727
- obj !== null &&
9728
- (obj instanceof SignalImpl || obj instanceof Task || isJSXNode(obj))) ||
9833
+ return ((isObject(obj) && (obj instanceof SignalImpl || obj instanceof Task || isJSXNode(obj))) ||
9729
9834
  isQrl(obj));
9730
9835
  };
9731
9836
  const canSerialize = (value, seen = new WeakSet()) => {
@@ -9811,6 +9916,9 @@ const canSerialize = (value, seen = new WeakSet()) => {
9811
9916
  return true;
9812
9917
  }
9813
9918
  }
9919
+ else if (value === _UNINITIALIZED) {
9920
+ return true;
9921
+ }
9814
9922
  return false;
9815
9923
  };
9816
9924
  const QRL_RUNTIME_CHUNK = 'mock-chunk';
@@ -9850,12 +9958,12 @@ const _typeIdNames = [
9850
9958
  'FormData',
9851
9959
  'JSXNode',
9852
9960
  'PropsProxy',
9853
- 'EffectData',
9961
+ 'SubscriptionData',
9854
9962
  ];
9855
9963
  const circularProofJson = (obj, indent) => {
9856
9964
  const seen = new WeakSet();
9857
- return JSON.stringify(obj, (key, value) => {
9858
- if (typeof value === 'object' && value !== null) {
9965
+ return JSON.stringify(obj, (_, value) => {
9966
+ if (isObject(value)) {
9859
9967
  if (seen.has(value)) {
9860
9968
  return `[Circular ${value.constructor.name}]`;
9861
9969
  }
@@ -9892,7 +10000,7 @@ const dumpState = (state, color = false, prefix = '', limit = 20) => {
9892
10000
  let value = state[++i];
9893
10001
  if (key === undefined) {
9894
10002
  hasRaw = true;
9895
- out.push(`${RED}[raw${typeof value === 'object' && value ? ` ${value.constructor.name}` : ''}]${RESET} ${printRaw(value, `${prefix} `)}`);
10003
+ out.push(`${RED}[raw${isObject(value) ? ` ${value.constructor.name}` : ''}]${RESET} ${printRaw(value, `${prefix} `)}`);
9896
10004
  }
9897
10005
  else {
9898
10006
  if (key === 3 /* TypeIds.Constant */) {
@@ -10006,7 +10114,6 @@ const _verifySerializable = (value, seen, ctx, preMessage) => {
10006
10114
  return value;
10007
10115
  };
10008
10116
  const noSerializeSet = /*#__PURE__*/ new WeakSet();
10009
- const weakSerializeSet = /*#__PURE__*/ new WeakSet();
10010
10117
  const shouldSerialize = (obj) => {
10011
10118
  if (isObject(obj) || isFunction(obj)) {
10012
10119
  return !noSerializeSet.has(obj);
@@ -10015,7 +10122,7 @@ const shouldSerialize = (obj) => {
10015
10122
  };
10016
10123
  const fastSkipSerialize = (obj) => {
10017
10124
  return (obj &&
10018
- (typeof obj === 'object' || typeof obj === 'function') &&
10125
+ (isObject(obj) || typeof obj === 'function') &&
10019
10126
  (NoSerializeSymbol in obj || noSerializeSet.has(obj)));
10020
10127
  };
10021
10128
  // <docs markdown="../../readme.md#noSerialize">
@@ -10040,16 +10147,11 @@ const fastSkipSerialize = (obj) => {
10040
10147
  // </docs>
10041
10148
  const noSerialize = (input) => {
10042
10149
  // only add supported values to the noSerializeSet, prevent console errors
10043
- if ((typeof input === 'object' && input !== null) || typeof input === 'function') {
10150
+ if ((isObject(input) && input !== null) || typeof input === 'function') {
10044
10151
  noSerializeSet.add(input);
10045
10152
  }
10046
10153
  return input;
10047
10154
  };
10048
- /** @internal */
10049
- const _weakSerialize = (input) => {
10050
- weakSerializeSet.add(input);
10051
- return input;
10052
- };
10053
10155
  /**
10054
10156
  * If an object has this property, it will not be serialized. Use this on prototypes to avoid having
10055
10157
  * to call `noSerialize()` on every object.
@@ -10076,6 +10178,7 @@ const NoSerializeSymbol = Symbol('noSerialize');
10076
10178
  const SerializerSymbol = Symbol('serialize');
10077
10179
 
10078
10180
  // keep these imports above the rest to prevent circular dep issues
10181
+ const resolvedSymbol = Symbol('resolved');
10079
10182
  const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
10080
10183
  if (qDev && qSerialize) {
10081
10184
  if (captureRef) {
@@ -10128,9 +10231,6 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
10128
10231
  };
10129
10232
  return bound;
10130
10233
  }
10131
- const resolveLazy = (containerEl) => {
10132
- return symbolRef !== null ? symbolRef : resolve(containerEl);
10133
- };
10134
10234
  // Wrap functions to provide their lexical scope
10135
10235
  const wrapFn = (fn) => {
10136
10236
  if (typeof fn !== 'function' || (!capture?.length && !captureRef?.length)) {
@@ -10159,45 +10259,58 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
10159
10259
  return invoke.call(this, context, fn, ...args);
10160
10260
  };
10161
10261
  };
10162
- const resolve = async (containerEl) => {
10163
- if (symbolRef !== null) {
10164
- // Resolving (Promise) or already resolved (value)
10262
+ // Retrieve memoized result from symbolFn
10263
+ if (symbolFn && resolvedSymbol in symbolFn) {
10264
+ symbolRef = symbolFn[resolvedSymbol];
10265
+ }
10266
+ const resolve = symbolRef
10267
+ ? async () => symbolRef
10268
+ : async (containerEl) => {
10269
+ if (symbolRef !== null) {
10270
+ // Resolving (Promise) or already resolved (value)
10271
+ return symbolRef;
10272
+ }
10273
+ if (containerEl) {
10274
+ setContainer(containerEl);
10275
+ }
10276
+ if (chunk === '') {
10277
+ // Sync QRL
10278
+ assertDefined(_containerEl, 'Sync QRL must have container element');
10279
+ const hash = _containerEl.getAttribute(QInstanceAttr);
10280
+ const doc = _containerEl.ownerDocument;
10281
+ const qFuncs = getQFuncs(doc, hash);
10282
+ // No need to wrap, syncQRLs can't have captured scope
10283
+ return (qrl.resolved = symbolRef = qFuncs[Number(symbol)]);
10284
+ }
10285
+ if (isBrowser && chunk) {
10286
+ /** We run the QRL, so now the probability of the chunk is 100% */
10287
+ p(chunk, 1);
10288
+ }
10289
+ const start = now();
10290
+ const ctx = tryGetInvokeContext();
10291
+ if (symbolFn !== null) {
10292
+ symbolRef = symbolFn().then((module) => {
10293
+ const resolved = wrapFn((symbolRef = module[symbol]));
10294
+ // We memoize the result on the symbolFn
10295
+ symbolFn[resolvedSymbol] = resolved;
10296
+ qrl.resolved = resolved;
10297
+ return resolved;
10298
+ });
10299
+ }
10300
+ else {
10301
+ // TODO cache the imported symbol but watch out for dev mode
10302
+ const imported = getPlatform().importSymbol(_containerEl, chunk, symbol);
10303
+ symbolRef = maybeThen(imported, (ref) => (qrl.resolved = wrapFn((symbolRef = ref))));
10304
+ }
10305
+ if (isPromise(symbolRef)) {
10306
+ symbolRef.then(() => emitUsedSymbol(symbol, ctx?.$element$, start), (err) => {
10307
+ console.error(`qrl ${symbol} failed to load`, err);
10308
+ // We shouldn't cache rejections, we can try again later
10309
+ symbolRef = null;
10310
+ });
10311
+ }
10165
10312
  return symbolRef;
10166
- }
10167
- if (containerEl) {
10168
- setContainer(containerEl);
10169
- }
10170
- if (chunk === '') {
10171
- // Sync QRL
10172
- assertDefined(_containerEl, 'Sync QRL must have container element');
10173
- const hash = _containerEl.getAttribute(QInstanceAttr);
10174
- const doc = _containerEl.ownerDocument;
10175
- const qFuncs = getQFuncs(doc, hash);
10176
- // No need to wrap, syncQRLs can't have captured scope
10177
- return (qrl.resolved = symbolRef = qFuncs[Number(symbol)]);
10178
- }
10179
- if (isBrowser && chunk) {
10180
- /** We run the QRL, so now the probability of the chunk is 100% */
10181
- p(chunk, 1);
10182
- }
10183
- const start = now();
10184
- const ctx = tryGetInvokeContext();
10185
- if (symbolFn !== null) {
10186
- symbolRef = symbolFn().then((module) => (qrl.resolved = wrapFn((symbolRef = module[symbol]))));
10187
- }
10188
- else {
10189
- const imported = getPlatform().importSymbol(_containerEl, chunk, symbol);
10190
- symbolRef = maybeThen(imported, (ref) => (qrl.resolved = wrapFn((symbolRef = ref))));
10191
- }
10192
- if (typeof symbolRef === 'object' && isPromise(symbolRef)) {
10193
- symbolRef.then(() => emitUsedSymbol(symbol, ctx?.$element$, start), (err) => {
10194
- console.error(`qrl ${symbol} failed to load`, err);
10195
- // We shouldn't cache rejections, we can try again later
10196
- symbolRef = null;
10197
- });
10198
- }
10199
- return symbolRef;
10200
- };
10313
+ };
10201
10314
  const createOrReuseInvocationContext = (invoke) => {
10202
10315
  if (invoke == null) {
10203
10316
  return newInvokeContext();
@@ -10215,7 +10328,6 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
10215
10328
  getHash: () => hash,
10216
10329
  getCaptured: () => captureRef,
10217
10330
  resolve,
10218
- $resolveLazy$: resolveLazy,
10219
10331
  $setContainer$: setContainer,
10220
10332
  $chunk$: chunk,
10221
10333
  $symbol$: symbol,
@@ -11056,13 +11168,13 @@ const _useStyles = (styleQrl, transform, scoped) => {
11056
11168
  const styleId = styleKey(styleQrl, i);
11057
11169
  const host = iCtx.$hostElement$;
11058
11170
  set(styleId);
11059
- const value = styleQrl.$resolveLazy$(iCtx.$element$);
11060
- if (isPromise(value)) {
11061
- value.then((val) => iCtx.$container$.$appendStyle$(transform(val, styleId), styleId, host, scoped));
11062
- throw value;
11171
+ if (styleQrl.resolved) {
11172
+ iCtx.$container$.$appendStyle$(transform(styleQrl.resolved, styleId), styleId, host, scoped);
11063
11173
  }
11064
11174
  else {
11065
- iCtx.$container$.$appendStyle$(transform(value, styleId), styleId, host, scoped);
11175
+ throw styleQrl
11176
+ .resolve()
11177
+ .then((val) => iCtx.$container$.$appendStyle$(transform(val, styleId), styleId, host, scoped));
11066
11178
  }
11067
11179
  return styleId;
11068
11180
  };
@@ -11243,13 +11355,13 @@ const useConstant = (value) => {
11243
11355
  return set(value);
11244
11356
  };
11245
11357
 
11246
- const useComputedCommon = (qrl, Class) => {
11358
+ const useComputedCommon = (qrl, createFn, options) => {
11247
11359
  const { val, set } = useSequentialScope();
11248
11360
  if (val) {
11249
11361
  return val;
11250
11362
  }
11251
11363
  assertQrl(qrl);
11252
- const signal = new Class(null, qrl);
11364
+ const signal = createFn(qrl, options);
11253
11365
  set(signal);
11254
11366
  // Note that we first save the signal
11255
11367
  // and then we throw to load the qrl
@@ -11258,8 +11370,8 @@ const useComputedCommon = (qrl, Class) => {
11258
11370
  return signal;
11259
11371
  };
11260
11372
  /** @internal */
11261
- const useComputedQrl = (qrl) => {
11262
- return useComputedCommon(qrl, ComputedSignalImpl);
11373
+ const useComputedQrl = (qrl, options) => {
11374
+ return useComputedCommon(qrl, createComputedSignal, options);
11263
11375
  };
11264
11376
  /**
11265
11377
  * Creates a computed signal which is calculated from the given function. A computed signal is a
@@ -11274,7 +11386,7 @@ const useComputedQrl = (qrl) => {
11274
11386
  const useComputed$ = implicit$FirstArg(useComputedQrl);
11275
11387
 
11276
11388
  /** @internal */
11277
- const useSerializerQrl = (qrl) => useComputedCommon(qrl, SerializerSignalImpl);
11389
+ const useSerializerQrl = (qrl) => useComputedCommon(qrl, createSerializerSignal);
11278
11390
  /**
11279
11391
  * Creates a signal which holds a custom serializable value. It requires that the value implements
11280
11392
  * the `CustomSerializable` type, which means having a function under the `[SerializeSymbol]`
@@ -11353,7 +11465,7 @@ const useVisibleTaskQrl = (qrl, opts) => {
11353
11465
  set(task);
11354
11466
  useRunTask(task, eagerness);
11355
11467
  if (!isServerPlatform()) {
11356
- qrl.$resolveLazy$(iCtx.$element$);
11468
+ qrl.resolve(iCtx.$element$);
11357
11469
  iCtx.$container$.$scheduler$(32 /* ChoreType.VISIBLE */, task);
11358
11470
  }
11359
11471
  };
@@ -11529,8 +11641,8 @@ const useTask$ = /*#__PURE__*/ implicit$FirstArg(useTaskQrl);
11529
11641
  const useVisibleTask$ = /*#__PURE__*/ implicit$FirstArg(useVisibleTaskQrl);
11530
11642
 
11531
11643
  /** @internal */
11532
- const useAsyncComputedQrl = (qrl) => {
11533
- return useComputedCommon(qrl, AsyncComputedSignalImpl);
11644
+ const useAsyncComputedQrl = (qrl, options) => {
11645
+ return useComputedCommon(qrl, createAsyncComputedSignal, options);
11534
11646
  };
11535
11647
  /**
11536
11648
  * Creates a computed signal which is calculated from the given function. A computed signal is a
@@ -11621,5 +11733,5 @@ const PREFETCH_CODE = /*#__PURE__*/ ((c // Service worker container
11621
11733
  */
11622
11734
  const PrefetchGraph = (_opts = {}) => null;
11623
11735
 
11624
- export { $, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, _VAR_PROPS, _deserialize, dumpState as _dumpState, _fnSignal, _getContextElement, _getContextEvent, getDomContainer as _getDomContainer, _getQContainerElement, isJSXNode as _isJSXNode, isStringifiable as _isStringifiable, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, _regSymbol, _restProps, queueQRL as _run, _serialize, scheduleTask as _task, verifySerializable as _verifySerializable, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _weakSerialize, _wrapProp, _wrapSignal, _wrapStore, component$, componentQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsx as jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsyncComputed$, useAsyncComputedQrl, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useSerializer$, useSerializerQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
11736
+ export { $, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, _UNINITIALIZED, _VAR_PROPS, _deserialize, dumpState as _dumpState, _fnSignal, _getContextContainer, _getContextElement, _getContextEvent, getDomContainer as _getDomContainer, _getQContainerElement, isJSXNode as _isJSXNode, isStringifiable as _isStringifiable, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, _regSymbol, _restProps, queueQRL as _run, _serializationWeakRef, _serialize, scheduleTask as _task, useInvokeContext as _useInvokeContext, verifySerializable as _verifySerializable, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _wrapProp, _wrapSignal, component$, componentQrl, createAsyncComputed$, createAsyncComputedSignal as createAsyncComputedQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsx as jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsyncComputed$, useAsyncComputedQrl, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useSerializer$, useSerializerQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
11625
11737
  //# sourceMappingURL=core.mjs.map