@qwik.dev/core 2.0.0-beta.37 → 2.0.0-beta.39

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 (37) hide show
  1. package/dist/backpatch/package.json +1 -1
  2. package/dist/build/package.json +1 -1
  3. package/dist/cli.mjs +14 -5
  4. package/dist/core-internal.d.ts +397 -323
  5. package/dist/core.min.mjs +9 -8
  6. package/dist/core.mjs +1886 -1161
  7. package/dist/core.mjs.map +1 -1
  8. package/dist/core.prod.mjs +4646 -4048
  9. package/dist/loader/index.mjs +2 -2
  10. package/dist/loader/package.json +1 -1
  11. package/dist/optimizer.d.ts +5 -3
  12. package/dist/optimizer.mjs +1676 -1722
  13. package/dist/preloader.mjs +1 -1
  14. package/dist/qwikloader.debug.js +1 -1
  15. package/dist/qwikloader.js +1 -1
  16. package/dist/server.d.ts +7 -0
  17. package/dist/server.mjs +75 -34
  18. package/dist/server.prod.mjs +392 -345
  19. package/dist/starters/adapters/bun/src/entry.bun.ts +35 -0
  20. package/dist/starters/adapters/cloud-run/src/entry.cloud-run.tsx +20 -0
  21. package/dist/starters/adapters/deno/src/entry.deno.ts +41 -0
  22. package/dist/starters/adapters/express/src/entry.express.tsx +20 -0
  23. package/dist/starters/adapters/fastify/package.json +2 -2
  24. package/dist/starters/adapters/fastify/src/entry.fastify.tsx +20 -0
  25. package/dist/starters/adapters/fastify/src/plugins/fastify-qwik.ts +10 -1
  26. package/dist/starters/adapters/node-server/src/entry.node-server.tsx +20 -0
  27. package/dist/starters/features/csr/package.json +1 -1
  28. package/dist/starters/features/drizzle/package.json +1 -1
  29. package/dist/starters/features/vitest/package.json +1 -1
  30. package/dist/testing/index.d.ts +40 -31
  31. package/dist/testing/index.mjs +1568 -942
  32. package/dist/testing/package.json +1 -1
  33. package/dist/worker/package.json +1 -1
  34. package/dist/worker/worker.shared.js +1 -1
  35. package/handlers.mjs +3 -0
  36. package/package.json +7 -7
  37. package/public.d.ts +3 -0
package/dist/core.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * @qwik.dev/core 2.0.0-beta.37-dev+82dc74d
3
+ * @qwik.dev/core 2.0.0-beta.39-dev+6fb620f
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
@@ -225,13 +225,16 @@ const codeToText = (code, ...parts) => {
225
225
  'Unknown vnode type {{0}}.', // 26
226
226
  'Materialize error: missing element: {{0}} {{1}} {{2}}', // 27
227
227
  'Cannot coerce a Signal, use `.value` instead', // 28
228
- 'useComputed$ QRL {{0}} {{1}} cannot return a Promise', // 29
228
+ '', // 29 (cleared: useComputed$ now supports async functions)
229
229
  '===\nQwik version {{0}} already imported while importing {{1}}.\nThis can lead to issues due to duplicated shared structures.\nVerify that the Qwik libraries you\'re using are in "resolve.noExternal[]" and in "optimizeDeps.exclude".\n===\n', // 30
230
230
  'WrappedSignal is read-only', // 31
231
231
  'Attribute value is unsafe for SSR {{0}}', // 32
232
232
  'SerializerSymbol function returned rejected promise', // 33
233
233
  'Serialization Error: Cannot serialize function: {{0}}', // 34
234
234
  'Cannot read .value of a clientOnly async signal during SSR. Use .loading to check state, or provide an initial value.', // 35
235
+ 'Invalid element name for SSR {{0}}', // 36
236
+ 'Invalid serialized Promise dependency', // 37
237
+ 'Invalid serialized Uint8Array payload', // 38
235
238
  ];
236
239
  let text = MAP[code] ?? '';
237
240
  if (parts.length) {
@@ -318,6 +321,7 @@ const VNodeDataChar = {
318
321
  CLOSE: /* ************* */ 125, // `}` is the end of the VNodeData for a virtual element.
319
322
  SCOPED_STYLE: /* ******* */ 59, // `;` - `q:sstyle` - Style attribute.
320
323
  RENDER_FN: /* ********** */ 60, // `<` - `q:renderFn' - Component QRL render function (body)
324
+ RENDER_HASH_PREFIX: /* **/ 95, // `_` after `<` identifies a component hash.
321
325
  ID: /* ***************** */ 61, // `=` - `q:id` - ID of the element.
322
326
  PROPS: /* ************** */ 62, // `>` - `q:props' - Component Props
323
327
  SLOT_PARENT: /* ******** */ 63, // `?` - `q:sparent` - Slot parent.
@@ -402,6 +406,7 @@ function decodeVNodeDataString(str) {
402
406
 
403
407
  /** State factory of the component. */
404
408
  const OnRenderProp = 'q:renderFn';
409
+ const QComponentHash = 'q:componentHash';
405
410
  /** Target DOM element for external projection rendering. */
406
411
  const QTargetElement = 'q:targetEl';
407
412
  /** Component style content prefix */
@@ -428,13 +433,13 @@ const QBaseAttr = 'q:base';
428
433
  const QLocaleAttr = 'q:locale';
429
434
  const QManifestHashAttr = 'q:manifest-hash';
430
435
  const QInstanceAttr = 'q:instance';
436
+ const QStatePrewarmAttr = 'q:prewarm';
431
437
  const QContainerIsland = 'q:container-island';
432
438
  const QContainerIslandEnd = '/' + QContainerIsland;
433
439
  const QIgnore = 'q:ignore';
434
440
  const QIgnoreEnd = '/' + QIgnore;
435
441
  const QContainerAttr = 'q:container';
436
442
  const QContainerAttrEnd = '/' + QContainerAttr;
437
- const QCursorBoundary = 'q:cursorBoundary';
438
443
  const QTemplate = 'q:template';
439
444
  // the same selector should be inside the qwik loader
440
445
  // and the same selector should be inside the qwik router spa-shim and spa-init
@@ -475,6 +480,7 @@ const NON_SERIALIZABLE_MARKER_PREFIX = ':';
475
480
  const USE_ON_LOCAL = NON_SERIALIZABLE_MARKER_PREFIX + 'on';
476
481
  const USE_ON_LOCAL_SEQ_IDX = NON_SERIALIZABLE_MARKER_PREFIX + 'onIdx';
477
482
  const USE_ON_LOCAL_FLAGS = NON_SERIALIZABLE_MARKER_PREFIX + 'onFlags';
483
+ const QCursorBoundary = NON_SERIALIZABLE_MARKER_PREFIX + 'cursorBoundary';
478
484
  const NEAREST_CURSOR_BOUNDARY = NON_SERIALIZABLE_MARKER_PREFIX + 'nearestCursorBoundary';
479
485
  const Q_PROPS_SEPARATOR = ':';
480
486
  const dangerouslySetInnerHTML = 'dangerouslySetInnerHTML';
@@ -842,8 +848,21 @@ const createMacroTask = (fn) => {
842
848
  let macroTask;
843
849
  if (typeof MessageChannel !== 'undefined') {
844
850
  const channel = new MessageChannel();
851
+ let active = true;
845
852
  channel.port1.onmessage = () => fn();
846
- macroTask = () => channel.port2.postMessage(null);
853
+ macroTask = () => {
854
+ if (active) {
855
+ channel.port2.postMessage(null);
856
+ }
857
+ };
858
+ macroTask.$destroy$ = () => {
859
+ if (active) {
860
+ active = false;
861
+ channel.port1.onmessage = null;
862
+ channel.port1.close();
863
+ channel.port2.close();
864
+ }
865
+ };
847
866
  }
848
867
  else {
849
868
  macroTask = () => setTimeout(fn);
@@ -963,7 +982,7 @@ const rel = isBrowser && doc.createElement('link').relList?.supports?.('modulepr
963
982
  ? 'modulePreload'
964
983
  : 'preload';
965
984
  const isJSRegex = /\.[mc]?js$/;
966
- const yieldInterval = 1000 / 60;
985
+ const yieldInterval = 10;
967
986
 
968
987
  const BundleImportState_None = 0;
969
988
  const BundleImportState_Preload = 2;
@@ -1219,6 +1238,8 @@ const writeStringRootRef = (writer, id) => writer.write(String(id));
1219
1238
  /** @internal */
1220
1239
  const writeStringRootRefPath = (writer, path) => writer.write(stringifyRootRefPath(path));
1221
1240
  /** @internal */
1241
+ const writeStringRootRefDelta = (writer, id, base) => writer.write(String(id - base));
1242
+ /** @internal */
1222
1243
  const createStringStreamWriter = (write) => ({
1223
1244
  write,
1224
1245
  writeRootRef(id) {
@@ -1227,6 +1248,9 @@ const createStringStreamWriter = (write) => ({
1227
1248
  writeRootRefPath(path) {
1228
1249
  return writeStringRootRefPath(this, path);
1229
1250
  },
1251
+ writeRootRefDelta(id, base) {
1252
+ return writeStringRootRefDelta(this, id, base);
1253
+ },
1230
1254
  });
1231
1255
 
1232
1256
  // Pre-allocated common strings to reduce allocation overhead
@@ -1242,7 +1266,7 @@ const COMMA = ',';
1242
1266
  *
1243
1267
  * @public
1244
1268
  */
1245
- const version = "2.0.0-beta.37-dev+82dc74d";
1269
+ const version = "2.0.0-beta.39-dev+6fb620f";
1246
1270
 
1247
1271
  const isNode = (value) => {
1248
1272
  return value && typeof value.nodeType === 'number';
@@ -1698,9 +1722,12 @@ function setCursorPosition(container, cursorData, position) {
1698
1722
  }
1699
1723
  }
1700
1724
  function mergeCursors(container, newCursorData, oldCursor) {
1725
+ const oldCursorData = getCursorData(oldCursor);
1726
+ if (!oldCursorData || oldCursorData === newCursorData) {
1727
+ return;
1728
+ }
1701
1729
  // delete from global cursors queue
1702
1730
  removeCursorFromQueue(oldCursor, container);
1703
- const oldCursorData = getCursorData(oldCursor);
1704
1731
  mergeCursorData(newCursorData, oldCursorData);
1705
1732
  }
1706
1733
  function mergeCursorData(newCursorData, oldCursorData) {
@@ -1778,6 +1805,234 @@ function setCursorData(vNode, cursorData) {
1778
1805
  cursorDatas.set(vNode, cursorData);
1779
1806
  }
1780
1807
 
1808
+ /** @internal */
1809
+ const _CONST_PROPS = Symbol('CONST');
1810
+ /** @internal */
1811
+ const _VAR_PROPS = Symbol('VAR');
1812
+ /** @internal */
1813
+ const _OWNER = Symbol('OWNER');
1814
+ /** @internal */
1815
+ const _PROPS_HANDLER = Symbol('PROPS_HANDLER');
1816
+ /** @internal @deprecated v1 compat */
1817
+ const _IMMUTABLE = Symbol('IMMUTABLE');
1818
+ /** @internal */
1819
+ const _UNINITIALIZED = Symbol('UNINITIALIZED');
1820
+
1821
+ /** @internal */
1822
+ const EMPTY_ARRAY = [];
1823
+ /** @internal */
1824
+ const EMPTY_OBJ = {};
1825
+ Object.freeze(EMPTY_ARRAY);
1826
+ Object.freeze(EMPTY_OBJ);
1827
+
1828
+ function createPropsProxy(owner) {
1829
+ // TODO don't make a proxy but populate getters? benchmark
1830
+ return new Proxy({}, new PropsProxyHandler(owner));
1831
+ }
1832
+ class PropsProxyHandler {
1833
+ owner;
1834
+ $effects$ = undefined;
1835
+ $container$ = null;
1836
+ constructor(owner) {
1837
+ this.owner = owner;
1838
+ }
1839
+ get(_, prop) {
1840
+ // escape hatch to get the separated props from a component
1841
+ if (prop === _CONST_PROPS) {
1842
+ return this.owner.constProps;
1843
+ }
1844
+ else if (prop === _VAR_PROPS) {
1845
+ return this.owner.varProps;
1846
+ }
1847
+ else if (prop === _OWNER) {
1848
+ return this.owner;
1849
+ }
1850
+ else if (prop === _PROPS_HANDLER) {
1851
+ return this;
1852
+ }
1853
+ let value;
1854
+ if (prop === 'children') {
1855
+ value = this.owner.children;
1856
+ }
1857
+ else {
1858
+ if (typeof prop === 'string' && typeof this.owner.type === 'string') {
1859
+ const attr = jsxEventToHtmlAttribute(prop);
1860
+ if (attr) {
1861
+ prop = attr;
1862
+ }
1863
+ }
1864
+ value = directGetPropsProxyProp(this.owner, prop);
1865
+ if (prop in this.owner.varProps) {
1866
+ addPropsProxyEffect(this, prop);
1867
+ }
1868
+ }
1869
+ // a proxied value that the optimizer made
1870
+ return value instanceof WrappedSignalImpl ? value.value : value;
1871
+ }
1872
+ set(_, prop, value) {
1873
+ if (prop === _OWNER) {
1874
+ // used for deserialization
1875
+ this.owner = value;
1876
+ }
1877
+ else if (prop === 'children') {
1878
+ this.owner.children = value;
1879
+ }
1880
+ else if (prop === _CONST_PROPS) {
1881
+ this.owner.constProps = value;
1882
+ }
1883
+ else if (prop === _VAR_PROPS) {
1884
+ this.owner.varProps = value;
1885
+ }
1886
+ else {
1887
+ if (typeof prop === 'string' && typeof this.owner.type === 'string') {
1888
+ const attr = jsxEventToHtmlAttribute(prop);
1889
+ if (attr) {
1890
+ prop = attr;
1891
+ }
1892
+ }
1893
+ if (this.owner.constProps && prop in this.owner.constProps) {
1894
+ // delete the prop from the const props first
1895
+ delete this.owner.constProps[prop];
1896
+ }
1897
+ if (this.owner.varProps === EMPTY_OBJ) {
1898
+ this.owner.varProps = {};
1899
+ }
1900
+ else if (!(prop in this.owner.varProps)) {
1901
+ this.owner.toSort = true;
1902
+ }
1903
+ if (this.owner.varProps[prop] !== value) {
1904
+ this.owner.varProps[prop] = value;
1905
+ triggerPropsProxyEffect(this, prop);
1906
+ }
1907
+ }
1908
+ return true;
1909
+ }
1910
+ deleteProperty(_, prop) {
1911
+ let didDelete = delete this.owner.varProps[prop];
1912
+ if (didDelete) {
1913
+ triggerPropsProxyEffect(this, prop);
1914
+ }
1915
+ if (this.owner.constProps) {
1916
+ didDelete = delete this.owner.constProps[prop] || didDelete;
1917
+ }
1918
+ if (this.owner.children != null && prop === 'children') {
1919
+ this.owner.children = null;
1920
+ didDelete = true;
1921
+ }
1922
+ return didDelete;
1923
+ }
1924
+ has(_, prop) {
1925
+ if (prop === 'children') {
1926
+ return this.owner.children != null;
1927
+ }
1928
+ else if (prop === _CONST_PROPS || prop === _VAR_PROPS) {
1929
+ return true;
1930
+ }
1931
+ const inVarProps = prop in this.owner.varProps;
1932
+ if (typeof prop === 'string') {
1933
+ if (inVarProps) {
1934
+ addPropsProxyEffect(this, prop);
1935
+ }
1936
+ if (typeof this.owner.type === 'string') {
1937
+ const attr = jsxEventToHtmlAttribute(prop);
1938
+ if (attr) {
1939
+ prop = attr;
1940
+ }
1941
+ }
1942
+ }
1943
+ return inVarProps || (this.owner.constProps ? prop in this.owner.constProps : false);
1944
+ }
1945
+ getOwnPropertyDescriptor(_, p) {
1946
+ const value = p === 'children'
1947
+ ? this.owner.children
1948
+ : this.owner.constProps && p in this.owner.constProps
1949
+ ? this.owner.constProps[p]
1950
+ : this.owner.varProps[p];
1951
+ return {
1952
+ configurable: true,
1953
+ enumerable: true,
1954
+ value: value,
1955
+ };
1956
+ }
1957
+ ownKeys() {
1958
+ const out = Object.keys(this.owner.varProps);
1959
+ if (this.owner.children != null) {
1960
+ out.push('children');
1961
+ }
1962
+ if (this.owner.constProps) {
1963
+ for (const key in this.owner.constProps) {
1964
+ if (out.indexOf(key) === -1) {
1965
+ out.push(key);
1966
+ }
1967
+ }
1968
+ }
1969
+ return out;
1970
+ }
1971
+ }
1972
+ const addPropsProxyEffect = (propsProxy, prop) => {
1973
+ // Lazily grab the container from the invoke context
1974
+ const ctx = tryGetInvokeContext();
1975
+ if (ctx) {
1976
+ if (propsProxy.$container$ === null) {
1977
+ if (ctx.$container$) {
1978
+ propsProxy.$container$ = ctx.$container$;
1979
+ }
1980
+ }
1981
+ else {
1982
+ isDev &&
1983
+ assertTrue(!ctx.$container$ || ctx.$container$ === propsProxy.$container$, 'Do not use props across containers');
1984
+ }
1985
+ }
1986
+ const effectSubscriber = ctx?.$effectSubscriber$;
1987
+ if (effectSubscriber) {
1988
+ addStoreEffect(propsProxy.owner._proxy, prop, propsProxy, effectSubscriber);
1989
+ }
1990
+ };
1991
+ const triggerPropsProxyEffect = (propsProxy, prop) => {
1992
+ const effects = getEffects$1(propsProxy.$effects$, prop);
1993
+ if (effects) {
1994
+ scheduleEffects(propsProxy.$container$, propsProxy, effects);
1995
+ }
1996
+ return !!effects;
1997
+ };
1998
+ function getEffects$1(effects, prop) {
1999
+ // TODO: Handle STORE_ALL_PROPS
2000
+ return effects?.get(prop);
2001
+ }
2002
+ /**
2003
+ * Instead of using PropsProxyHandler getter (which could create a component-level subscription).
2004
+ * Use this function to get the props directly from a const or var props.
2005
+ *
2006
+ * This does not convert jsx event names.
2007
+ */
2008
+ const directGetPropsProxyProp = (jsx, prop) => {
2009
+ return (jsx.constProps && prop in jsx.constProps ? jsx.constProps[prop] : jsx.varProps[prop]);
2010
+ };
2011
+ const _getProps = (props, prop) => {
2012
+ return _getVarProps(props)?.[prop] || _getConstProps(props)?.[prop] || null;
2013
+ };
2014
+ /** Used by the optimizer for spread props operations @internal */
2015
+ const _getVarProps = (props) => {
2016
+ if (!props) {
2017
+ return null;
2018
+ }
2019
+ return _VAR_PROPS in props
2020
+ ? 'children' in props
2021
+ ? { ...props[_VAR_PROPS], children: props.children }
2022
+ : props[_VAR_PROPS]
2023
+ : props;
2024
+ };
2025
+ /** Used by the optimizer for spread props operations @internal */
2026
+ const _getConstProps = (props) => {
2027
+ if (!props) {
2028
+ return null;
2029
+ }
2030
+ return _CONST_PROPS in props ? props[_CONST_PROPS] : null;
2031
+ };
2032
+ const isPropsProxy = (obj) => {
2033
+ return obj && _VAR_PROPS in obj;
2034
+ };
2035
+
1781
2036
  /**
1782
2037
  * # ================================
1783
2038
  *
@@ -2017,7 +2272,7 @@ class SignalImpl {
2017
2272
  toString() {
2018
2273
  if (isDev) {
2019
2274
  try {
2020
- return (`[${this.constructor.name}${this.$flags$ & 1 /* SignalFlags.INVALID */ ? ' INVALID' : ''} ${this.$untrackedValue$}]` +
2275
+ return (`[${this.constructor.name}${this.$flags$ & 1 /* ComputedSignalFlags.INVALID */ ? ' INVALID' : ''} ${this.$untrackedValue$}]` +
2021
2276
  (Array.from(this.$effects$ || [])
2022
2277
  .map((e) => '\n -> ' + pad(qwikDebugToString(e.consumer), ' '))
2023
2278
  .join('\n') || ''));
@@ -2035,99 +2290,11 @@ class SignalImpl {
2035
2290
  }
2036
2291
  }
2037
2292
 
2038
- const DEBUG$1 = false;
2293
+ const DEBUG = false;
2039
2294
  // eslint-disable-next-line no-console
2040
- const log$1 = (...args) => console.log('COMPUTED SIGNAL', ...args.map(qwikDebugToString));
2041
- /**
2042
- * A signal which is computed from other signals.
2043
- *
2044
- * The value is available synchronously, but the computation is done lazily.
2045
- */
2046
- class ComputedSignalImpl extends SignalImpl {
2047
- /**
2048
- * The compute function is stored here.
2049
- *
2050
- * The computed functions must be executed synchronously (because of this we need to eagerly
2051
- * resolve the QRL during the mark dirty phase so that any call to it will be synchronous). )
2052
- */
2053
- $computeQrl$;
2054
- $flags$;
2055
- [_EFFECT_BACK_REF] = undefined;
2056
- constructor(container, fn,
2057
- // We need a separate flag to know when the computation needs running because
2058
- // we need the old value to know if effects need running after computation
2059
- flags = 1 /* SignalFlags.INVALID */ |
2060
- 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */) {
2061
- // The value is used for comparison when signals trigger, which can only happen
2062
- // when it was calculated before. Therefore we can pass whatever we like.
2063
- super(container || tryGetInvokeContext()?.$container$, NEEDS_COMPUTATION);
2064
- this.$computeQrl$ = fn;
2065
- this.$flags$ = flags;
2066
- }
2067
- invalidate() {
2068
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
2069
- const ctx = newInvokeContext();
2070
- ctx.$container$ = this.$container$ || undefined;
2071
- // @ts-expect-error it's confused about args any[] vs []
2072
- const running = retryOnPromise(invokeApply.bind(this, ctx, this.$computeIfNeeded$));
2073
- if (running) {
2074
- running.catch((err) => {
2075
- if (this.$container$) {
2076
- this.$container$.handleError(err, null);
2077
- }
2078
- else {
2079
- console.error('Error during computation', err);
2080
- }
2081
- });
2082
- }
2083
- }
2084
- get untrackedValue() {
2085
- this.$computeIfNeeded$();
2086
- isDev && assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
2087
- return this.$untrackedValue$;
2088
- }
2089
- set untrackedValue(value) {
2090
- this.$untrackedValue$ = value;
2091
- }
2092
- $computeIfNeeded$() {
2093
- if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
2094
- return;
2095
- }
2096
- const computeQrl = this.$computeQrl$;
2097
- throwIfQRLNotResolved(computeQrl);
2098
- const ctx = tryGetInvokeContext();
2099
- const previousEffectSubscription = ctx?.$effectSubscriber$;
2100
- if (ctx) {
2101
- const effectSubscriber = getSubscriber(this, "." /* EffectProperty.VNODE */);
2102
- clearEffectSubscription(this.$container$, effectSubscriber);
2103
- ctx.$effectSubscriber$ = effectSubscriber;
2104
- }
2105
- try {
2106
- const untrackedValue = computeQrl.getFn(ctx)();
2107
- if (isPromise(untrackedValue)) {
2108
- throw qError(29 /* QError.computedNotSync */, [
2109
- computeQrl.dev ? computeQrl.dev.file : '',
2110
- computeQrl.$hash$,
2111
- ]);
2112
- }
2113
- DEBUG$1 && log$1('Signal.$compute$', untrackedValue);
2114
- this.$flags$ &= ~1 /* SignalFlags.INVALID */;
2115
- super.value = untrackedValue;
2116
- }
2117
- finally {
2118
- if (ctx) {
2119
- ctx.$effectSubscriber$ = previousEffectSubscription;
2120
- }
2121
- }
2122
- }
2123
- }
2124
-
2125
- const DEBUG = false;
2126
- const log = (...args) =>
2127
- // eslint-disable-next-line no-console
2128
- console.log('ASYNC COMPUTED SIGNAL', ...args.map(qwikDebugToString));
2129
- /** Retains job metadata and also serves as the argument for the compute function */
2130
- class AsyncJob {
2295
+ const log = (...args) => console.log('COMPUTED SIGNAL', ...args.map(qwikDebugToString));
2296
+ /** Retains job metadata and also serves as the argument for CTX_ARG compute functions */
2297
+ class Job {
2131
2298
  $signal$;
2132
2299
  /** First holds the compute promise and then the cleanup promise */
2133
2300
  $promise$ = null;
@@ -2149,7 +2316,7 @@ class AsyncJob {
2149
2316
  /** Backward compatible cache method for resource */
2150
2317
  cache() {
2151
2318
  isDev &&
2152
- console.error('useResource cache() method does not do anything. Use `useAsync$` instead of `useResource$`, use the `expires` option for polling behavior.');
2319
+ console.error('useResource cache() method does not do anything. Use `useComputed$` instead of `useResource$`; for polling use the `usePoll` hook from `@qwik.dev/utils`.');
2153
2320
  }
2154
2321
  get previous() {
2155
2322
  const val = this.$signal$.$untrackedValue$;
@@ -2164,62 +2331,123 @@ class AsyncJob {
2164
2331
  }
2165
2332
  }
2166
2333
  /**
2167
- * # ================================
2168
- *
2169
- * AsyncSignalImpl
2170
- *
2171
- * # ================================
2334
+ * A signal which is computed from other signals.
2172
2335
  *
2173
- * @internal
2336
+ * Sync compute functions resolve synchronously and every read they perform is auto-tracked. When
2337
+ * the compute function turns out to be async (it returned a promise), the signal lazily switches on
2338
+ * the async engine — jobs, `loading`, `error` — giving it the same API as an AsyncSignal.
2339
+ * Auto-tracking only covers reads before the first await point (the invoke context is lost after
2340
+ * it); later reads must use the ComputeCtx `track()`.
2174
2341
  */
2175
- class AsyncSignalImpl extends ComputedSignalImpl {
2176
- $untrackedLoading$ = false;
2177
- $untrackedError$ = undefined;
2178
- $current$ = null;
2342
+ // TODO when we make core lazy async, lazy load the async engine part of this class
2343
+ class ComputedSignalImpl extends SignalImpl {
2344
+ /**
2345
+ * The compute function is stored here.
2346
+ *
2347
+ * Sync computed functions must be executed synchronously (because of this we need to eagerly
2348
+ * resolve the QRL during the mark dirty phase so that any call to it will be synchronous).
2349
+ */
2350
+ $computeQrl$;
2351
+ $flags$;
2179
2352
  [_EFFECT_BACK_REF] = undefined;
2180
- constructor(container, fn, flags = 1 /* SignalFlags.INVALID */ |
2353
+ constructor(container, fn,
2354
+ // We need a separate flag to know when the computation needs running because
2355
+ // we need the old value to know if effects need running after computation
2356
+ flags = 1 /* ComputedSignalFlags.INVALID */ |
2181
2357
  16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */, options) {
2182
- super(container, fn, flags);
2358
+ // The value is used for comparison when signals trigger, which can only happen
2359
+ // when it was calculated before. Therefore we can pass whatever we like.
2360
+ super(container || tryGetInvokeContext()?.$container$, NEEDS_COMPUTATION);
2361
+ this.$computeQrl$ = fn;
2362
+ this.$flags$ = flags;
2183
2363
  if (!options) {
2184
2364
  return;
2185
2365
  }
2186
- // Handle initial value - eagerly evaluate if function, set $untrackedValue$ and $promiseValue$
2187
- // Do NOT call setValue() which would clear the INVALID flag and prevent async computation
2366
+ // Eagerly evaluate an initial value without clearing INVALID, so the compute still runs
2188
2367
  const initial = options.initial;
2189
2368
  if (initial !== undefined) {
2190
- const initialValue = typeof initial === 'function' ? initial() : initial;
2191
- this.$untrackedValue$ = initialValue;
2369
+ this.$untrackedValue$ = typeof initial === 'function' ? initial() : initial;
2192
2370
  }
2193
2371
  const concurrency = options.concurrency;
2194
2372
  if (concurrency !== undefined && concurrency >= 0 && concurrency !== 1) {
2195
2373
  this.$concurrency$ = concurrency;
2196
2374
  this.$jobs$ = [];
2197
2375
  }
2198
- const timeout = options.timeout;
2199
- if (timeout) {
2200
- this.$timeoutMs$ = timeout;
2376
+ if (options.timeout) {
2377
+ this.$timeoutMs$ = options.timeout;
2201
2378
  }
2202
2379
  if (options.eagerCleanup) {
2203
2380
  this.$flags$ |= 32 /* AsyncSignalFlags.EAGER_CLEANUP */;
2204
2381
  }
2205
2382
  if (options.clientOnly) {
2206
- this.$flags$ |= 64 /* AsyncSignalFlags.CLIENT_ONLY */;
2383
+ // clientOnly can never resolve during SSR, so it must be async from the start
2384
+ this.$flags$ |= 64 /* AsyncSignalFlags.CLIENT_ONLY */ | 512 /* AsyncSignalFlags.ASYNC_MODE */;
2207
2385
  }
2208
- if (options.allowStale === false) {
2209
- if (isDev && initial !== undefined) {
2210
- throw new Error('allowStale: false and initial cannot be used together. ' +
2211
- 'allowStale: false clears the value on invalidation, which conflicts with providing an initial value.');
2212
- }
2213
- this.$flags$ |= 128 /* AsyncSignalFlags.CLEAR_ON_INVALIDATE */;
2386
+ }
2387
+ /** Clear the value so readers see the loading state, then recompute. */
2388
+ clear() {
2389
+ this.$untrackedValue$ = NEEDS_COMPUTATION;
2390
+ this.invalidate();
2391
+ }
2392
+ invalidate(info) {
2393
+ if (arguments.length > 0) {
2394
+ this.$info$ = info;
2395
+ this.$infoVersion$ = this.$infoVersion$ === undefined ? 1 : this.$infoVersion$ + 1;
2214
2396
  }
2215
- const expires = options.expires ?? (options.interval ? Math.abs(options.interval) : undefined);
2216
- if (expires) {
2217
- this.expires = expires;
2397
+ if (this.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */) {
2398
+ this.$setInvalid$(true);
2399
+ return;
2218
2400
  }
2219
- if (options.poll === false || (options.interval !== undefined && options.interval < 0)) {
2220
- this.$flags$ |= 256 /* AsyncSignalFlags.NO_POLL */;
2401
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
2402
+ const ctx = newInvokeContext();
2403
+ ctx.$container$ = this.$container$ || undefined;
2404
+ // @ts-expect-error it's confused about args any[] vs []
2405
+ const running = retryOnPromise(invokeApply.bind(this, ctx, this.$computeIfNeeded$));
2406
+ if (isPromise(running)) {
2407
+ running.catch((err) => {
2408
+ if (this.$container$) {
2409
+ this.$container$.handleError(err, null);
2410
+ }
2411
+ else {
2412
+ console.error('Error during computation', err);
2413
+ }
2414
+ });
2221
2415
  }
2222
2416
  }
2417
+ /**
2418
+ * Read the value, subscribing if in a tracking context. Triggers computation if needed.
2419
+ *
2420
+ * Setting the value will mark the signal as not loading and clear any error, and prevent any
2421
+ * pending computations from writing their results.
2422
+ *
2423
+ * If you want to set the value without affecting loading or error state, set `untrackedValue`
2424
+ * instead and make sure to trigger effects manually if needed.
2425
+ *
2426
+ * If you want to abort pending computations when setting, you have to call `abort()` manually.
2427
+ */
2428
+ get value() {
2429
+ return super.value;
2430
+ }
2431
+ set value(value) {
2432
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2433
+ this.untrackedError = undefined;
2434
+ if (this.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */) {
2435
+ this.untrackedPending = false;
2436
+ this.$info$ = undefined;
2437
+ // Prevent pending computations from overwriting this value
2438
+ if (this.$jobs$) {
2439
+ for (let i = 0; i < this.$jobs$.length; i++) {
2440
+ this.$jobs$[i].$canWrite$ = false;
2441
+ }
2442
+ }
2443
+ else if (this.$current$) {
2444
+ this.$current$.$canWrite$ = false;
2445
+ }
2446
+ super.value = value;
2447
+ return;
2448
+ }
2449
+ super.value = value;
2450
+ }
2223
2451
  get untrackedValue() {
2224
2452
  this.$computeIfNeeded$();
2225
2453
  if (this.$current$?.$promise$) {
@@ -2238,52 +2466,26 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2238
2466
  this.$untrackedValue$ === NEEDS_COMPUTATION) {
2239
2467
  throw qError(35 /* QError.asyncClientOnlyValueDuringSSR */);
2240
2468
  }
2469
+ isDev &&
2470
+ !(this.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */) &&
2471
+ assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
2241
2472
  return this.$untrackedValue$;
2242
2473
  }
2243
2474
  set untrackedValue(value) {
2244
2475
  this.$untrackedValue$ = value;
2245
2476
  }
2246
- /**
2247
- * Read the value, subscribing if in a tracking context. Triggers computation if needed.
2248
- *
2249
- * Setting the value will mark the signal as not loading and clear any error, and prevent any
2250
- * pending computations from writing their results.
2251
- *
2252
- * If you want to set the value without affecting loading or error state, set `untrackedValue`
2253
- * instead and make sure to trigger effects manually if needed.
2254
- *
2255
- * If you want to abort pending computations when setting, you have to call `abort()` manually.
2256
- */
2257
- get value() {
2258
- return super.value;
2259
- }
2260
- set value(value) {
2261
- this.$flags$ &= -2 /* SignalFlags.INVALID */;
2262
- this.untrackedLoading = false;
2263
- this.untrackedError = undefined;
2264
- this.$info$ = undefined;
2265
- // Prevent pending computations from overwriting this value
2266
- if (this.$jobs$) {
2267
- for (let i = 0; i < this.$jobs$.length; i++) {
2268
- this.$jobs$[i].$canWrite$ = false;
2269
- }
2270
- }
2271
- else if (this.$current$) {
2272
- this.$current$.$canWrite$ = false;
2273
- }
2274
- this.$clearNextPoll$();
2275
- super.value = value;
2276
- this.$scheduleNextPoll$();
2277
- }
2278
2477
  /**
2279
2478
  * Loading is true if the signal is still waiting for the promise to resolve, false if the promise
2280
- * has resolved or rejected.
2479
+ * has resolved or rejected (or the compute function is synchronous).
2480
+ *
2481
+ * Accessing `.pending` will trigger computation if needed, since it's often used like
2281
2482
  *
2282
- * Accessing .loading will trigger computation if needed, since it's often used like
2283
- * `signal.loading ? <Loading /> : signal.value`.
2483
+ * ```ts
2484
+ * signal.pending ? <Loading /> : signal.value
2485
+ * ```
2284
2486
  */
2285
- get loading() {
2286
- const val = this.untrackedLoading;
2487
+ get pending() {
2488
+ const val = this.untrackedPending;
2287
2489
  const ctx = tryGetInvokeContext();
2288
2490
  if (ctx && (this.$container$ ||= ctx.$container$ || null)) {
2289
2491
  isDev &&
@@ -2297,22 +2499,41 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2297
2499
  }
2298
2500
  return val;
2299
2501
  }
2300
- set untrackedLoading(value) {
2301
- if (value !== this.$untrackedLoading$) {
2302
- this.$untrackedLoading$ = value;
2502
+ set untrackedPending(value) {
2503
+ if (value !== !!this.$untrackedPending$) {
2504
+ this.$untrackedPending$ = value;
2303
2505
  scheduleEffects(this.$container$, this, this.$loadingEffects$);
2304
2506
  }
2305
2507
  }
2306
- get untrackedLoading() {
2508
+ get untrackedPending() {
2307
2509
  // reading `.loading` means someone is interested in the result, so we should trigger the computation. The alternative is eager computation or imperative calls to invalidate; this seems nicer.
2308
2510
  this.$computeIfNeeded$();
2309
2511
  // During SSR there's no such thing as loading state, we must render complete results
2310
2512
  if ((qTest ? isServerPlatform() : isServer) && this.$current$?.$promise$) {
2311
2513
  throw this.$current$?.$promise$;
2312
2514
  }
2313
- return this.$untrackedLoading$;
2515
+ return !!this.$untrackedPending$;
2314
2516
  }
2315
- /** The error that occurred when the signal was resolved. */
2517
+ /** @deprecated Use `pending` instead */
2518
+ get loading() {
2519
+ return this.pending;
2520
+ }
2521
+ /** @deprecated Use `untrackedPending` instead */
2522
+ get untrackedLoading() {
2523
+ return this.untrackedPending;
2524
+ }
2525
+ set untrackedLoading(value) {
2526
+ this.untrackedPending = value;
2527
+ }
2528
+ /**
2529
+ * The error the compute function threw or rejected with.
2530
+ *
2531
+ * Accessing .error will trigger computation if needed, since it's often used like
2532
+ *
2533
+ * ```ts
2534
+ * signal.error ? <Failed /> : signal.value
2535
+ * ```
2536
+ */
2316
2537
  get error() {
2317
2538
  const val = this.untrackedError;
2318
2539
  const ctx = tryGetInvokeContext();
@@ -2335,62 +2556,15 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2335
2556
  }
2336
2557
  }
2337
2558
  get untrackedError() {
2338
- return this.$untrackedError$;
2339
- }
2340
- get expires() {
2341
- return this.$expires$ || 0;
2342
- }
2343
- set expires(value) {
2344
- this.$clearNextPoll$();
2345
- this.$expires$ = value;
2346
- if (this.$expires$ && this.$hasSubscribers$()) {
2347
- this.$scheduleNextPoll$();
2348
- }
2349
- }
2350
- get poll() {
2351
- return !(this.$flags$ & 256 /* AsyncSignalFlags.NO_POLL */);
2352
- }
2353
- set poll(value) {
2354
- if (value) {
2355
- this.$flags$ &= -257 /* AsyncSignalFlags.NO_POLL */;
2356
- }
2357
- else {
2358
- this.$flags$ |= 256 /* AsyncSignalFlags.NO_POLL */;
2359
- }
2360
- // Reschedule since poll behavior changed
2361
- if (this.$expires$ && this.$hasSubscribers$()) {
2362
- this.$clearNextPoll$();
2363
- this.$scheduleNextPoll$();
2364
- }
2365
- }
2366
- /** @deprecated Use `expires` and `poll` instead. */
2367
- get interval() {
2368
- const expires = this.$expires$ || 0;
2369
- return this.$flags$ & 256 /* AsyncSignalFlags.NO_POLL */ ? -expires : expires;
2370
- }
2371
- set interval(value) {
2372
- if (value < 0) {
2373
- this.$flags$ |= 256 /* AsyncSignalFlags.NO_POLL */;
2374
- }
2375
- else {
2376
- this.$flags$ &= -257 /* AsyncSignalFlags.NO_POLL */;
2377
- }
2378
- this.expires = Math.abs(value);
2379
- }
2380
- /** Invalidates the signal, causing it to re-compute its value. */
2381
- async invalidate(info) {
2382
- if (arguments.length > 0) {
2383
- this.$info$ = info;
2384
- this.$infoVersion$ = this.$infoVersion$ === undefined ? 1 : this.$infoVersion$ + 1;
2559
+ this.$computeIfNeeded$();
2560
+ // During SSR we must render settled results
2561
+ if ((qTest ? isServerPlatform() : isServer) && this.$current$?.$promise$) {
2562
+ throw this.$current$?.$promise$;
2385
2563
  }
2386
- this.$setInvalid$(true, this.$flags$ & 128 /* AsyncSignalFlags.CLEAR_ON_INVALIDATE */);
2564
+ return this.$untrackedError$;
2387
2565
  }
2388
- $setInvalid$(allowRecalc, mustClear) {
2389
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
2390
- this.$clearNextPoll$();
2391
- if (mustClear) {
2392
- this.$untrackedValue$ = NEEDS_COMPUTATION;
2393
- }
2566
+ $setInvalid$(allowRecalc) {
2567
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
2394
2568
  if (allowRecalc &&
2395
2569
  (this.$effects$?.size || this.$loadingEffects$?.size || this.$errorEffects$?.size)) {
2396
2570
  // compute in next microtask
@@ -2427,19 +2601,96 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2427
2601
  }
2428
2602
  /** Run the computation if needed */
2429
2603
  $computeIfNeeded$() {
2430
- if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
2604
+ if (!(this.$flags$ & 1 /* ComputedSignalFlags.INVALID */)) {
2431
2605
  return;
2432
2606
  }
2607
+ if (this.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */) {
2608
+ return this.$computeAsync$();
2609
+ }
2610
+ const computeQrl = this.$computeQrl$;
2611
+ throwIfQRLNotResolved(computeQrl);
2612
+ // The previous compute's cleanups run before the new compute, like the async engine
2613
+ const prevJob = this.$current$;
2614
+ if (prevJob && !prevJob.$cleanupRequested$) {
2615
+ prevJob.$cleanupRequested$ = true;
2616
+ prevJob.$abortController$?.abort();
2617
+ this.$runCleanupCallbacks$(prevJob);
2618
+ }
2619
+ const running = new Job(this, this.$info$, this.$infoVersion$);
2620
+ this.$current$ = running;
2621
+ let result;
2622
+ try {
2623
+ result = this.$invokeComputeFn$(computeQrl.resolved, running);
2624
+ }
2625
+ catch (err) {
2626
+ // A thrown promise is a retry request, not a failure
2627
+ if (isPromise(err)) {
2628
+ throw err;
2629
+ }
2630
+ if (running.$infoVersion$ === this.$infoVersion$) {
2631
+ this.$info$ = undefined;
2632
+ }
2633
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2634
+ this.$setError$(running, err);
2635
+ return;
2636
+ }
2637
+ if (isPromise(result)) {
2638
+ // The compute fn is async: hand the in-flight computation to the async engine
2639
+ this.$flags$ |= 512 /* AsyncSignalFlags.ASYNC_MODE */;
2640
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2641
+ // Consume the in-flight promise first; retries re-invoke the compute fn
2642
+ let first = result;
2643
+ const compute = () => {
2644
+ const r = first ?? this.$invokeComputeFn$(this.$computeQrl$.resolved, running);
2645
+ first = undefined;
2646
+ return r;
2647
+ };
2648
+ running.$promise$ = this.$settleComputation$(running, () => retryOnPromise(compute));
2649
+ return;
2650
+ }
2651
+ if (running.$infoVersion$ === this.$infoVersion$) {
2652
+ this.$info$ = undefined;
2653
+ }
2654
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2655
+ this.untrackedError = undefined;
2656
+ super.value = result;
2657
+ }
2658
+ /**
2659
+ * Invoke the compute fn with the ComputeCtx argument. CTX_ARG signals (useAsync$, useResource$)
2660
+ * only track via its explicit track(). Computeds auto-track every synchronous read instead; after
2661
+ * the first await they must use the explicit track() too.
2662
+ */
2663
+ $invokeComputeFn$(fn, job) {
2664
+ const computeFn = fn;
2665
+ if (this.$flags$ & 1024 /* AsyncSignalFlags.CTX_ARG */) {
2666
+ // Strip the ambient effect subscriber: these compute fns track only via their track()
2667
+ let iCtx = tryGetInvokeContext();
2668
+ if (iCtx?.$effectSubscriber$) {
2669
+ iCtx = { ...iCtx, $effectSubscriber$: undefined };
2670
+ }
2671
+ return invokeApply(iCtx, computeFn, [job]);
2672
+ }
2673
+ const container = (this.$container$ ||= tryGetInvokeContext()?.$container$ || null);
2674
+ const subscriber = getSubscriber(this, "." /* EffectProperty.VNODE */);
2675
+ if (container) {
2676
+ clearEffectSubscription(container, subscriber);
2677
+ }
2678
+ const ctx = newInvokeContext();
2679
+ ctx.$container$ = container || undefined;
2680
+ ctx.$effectSubscriber$ = subscriber;
2681
+ return invokeApply(ctx, computeFn, [job]);
2682
+ }
2683
+ /** Start an async computation (the async engine; assumes the INVALID flag is set) */
2684
+ $computeAsync$() {
2433
2685
  // Skip computation on SSR for clientOnly signals
2434
2686
  if ((qTest ? isServerPlatform() : isServer) && this.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
2435
2687
  // We must pretend to load, and register as a listener for the captures
2436
- this.$untrackedLoading$ = true;
2688
+ this.$untrackedPending$ = true;
2437
2689
  this.$container$?.serializationCtx.$eagerResume$.add(this);
2438
2690
  return;
2439
2691
  }
2440
- this.$clearNextPoll$();
2441
2692
  // Clear flag here to make sure the cleanups don't start another compute
2442
- this.$flags$ &= -2 /* SignalFlags.INVALID */;
2693
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2443
2694
  const current = this.$current$;
2444
2695
  if (current) {
2445
2696
  this.$requestCleanups$(current);
@@ -2449,12 +2700,12 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2449
2700
  if (this.$jobs$ ? this.$jobs$.length >= limit : current?.$promise$) {
2450
2701
  // We requested cleanups for all the previous jobs, once one finishes it will be removed from the jobs array and trigger computeIfNeeded
2451
2702
  // Restore invalid state
2452
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
2703
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
2453
2704
  return;
2454
2705
  }
2455
2706
  // We put the actual computation in a separate method so we can easily retain the promise
2456
2707
  const infoVersion = this.$infoVersion$;
2457
- const running = new AsyncJob(this, this.$info$, infoVersion);
2708
+ const running = new Job(this, this.$info$, infoVersion);
2458
2709
  this.$current$ = running;
2459
2710
  if (this.$jobs$) {
2460
2711
  this.$jobs$.push(running);
@@ -2462,35 +2713,39 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2462
2713
  running.$promise$ = this.$runComputation$(running);
2463
2714
  }
2464
2715
  async $runComputation$(running) {
2465
- const isCurrent = () => running === this.$current$;
2466
2716
  let fn = this.$computeQrl$.resolved;
2467
2717
  if (!fn) {
2468
2718
  // QRL resolution is async — we have to publish loading=true before awaiting so
2469
2719
  // subscribers know the value isn't ready yet.
2470
- this.untrackedLoading = true;
2720
+ this.untrackedPending = true;
2471
2721
  fn = await this.$computeQrl$.resolve();
2472
2722
  if (running.$abortController$?.signal.aborted) {
2473
2723
  running.$promise$ = null;
2474
2724
  return;
2475
2725
  }
2476
2726
  }
2727
+ if (this.$timeoutMs$) {
2728
+ this.$computationTimeoutId$ = setTimeout(() => {
2729
+ const error = new Error(`timeout ${this.$timeoutMs$}ms`);
2730
+ this.$setError$(running, error);
2731
+ running.$abortController$?.abort(error);
2732
+ }, this.$timeoutMs$);
2733
+ }
2734
+ return this.$settleComputation$(running, () => retryOnPromise(() => this.$invokeComputeFn$(fn, running)));
2735
+ }
2736
+ /** Await the computation and publish its result, error, and loading transitions. */
2737
+ async $settleComputation$(running, compute) {
2738
+ const isCurrent = () => running === this.$current$;
2477
2739
  try {
2478
- if (this.$timeoutMs$) {
2479
- this.$computationTimeoutId$ = setTimeout(() => {
2480
- const error = new Error(`timeout ${this.$timeoutMs$}ms`);
2481
- this.$setError$(running, error);
2482
- running.$abortController$?.abort(error);
2483
- }, this.$timeoutMs$);
2484
- }
2485
2740
  // Try to stay sync if possible. Only publish loading=true to subscribers when
2486
2741
  // the compute is actually asynchronous — a synchronous resolve (e.g. pre-loaded
2487
2742
  // values injected via _injectAsyncSignalValue) should never transition through a
2488
2743
  // visible loading state, which on SSR would fire the loading-effect subscribers
2489
2744
  // (tasks) while the value is still "loading" from their perspective.
2490
- const valuePromise = retryOnPromise(fn.bind(null, running));
2745
+ const valuePromise = compute();
2491
2746
  let value;
2492
2747
  if (isPromise(valuePromise)) {
2493
- this.untrackedLoading = true;
2748
+ this.untrackedPending = true;
2494
2749
  value = await valuePromise;
2495
2750
  }
2496
2751
  else {
@@ -2515,7 +2770,7 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2515
2770
  // Note that these assignments run setters
2516
2771
  this.untrackedError = undefined;
2517
2772
  /**
2518
- * Use super.value instead of this.value to persist invalid state, so that invalidation
2773
+ * Use SignalImpl.value instead of this.value to persist invalid state, so that invalidation
2519
2774
  * during computation recomputes
2520
2775
  */
2521
2776
  super.value = value;
@@ -2530,13 +2785,12 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2530
2785
  if (running.$infoVersion$ === this.$infoVersion$) {
2531
2786
  this.$info$ = undefined;
2532
2787
  }
2533
- if (this.$flags$ & 1 /* SignalFlags.INVALID */) {
2788
+ if (this.$flags$ & 1 /* ComputedSignalFlags.INVALID */) {
2534
2789
  // we became invalid again while running, so we need to re-run the computation to get the new promise
2535
2790
  this.$computeIfNeeded$();
2536
2791
  }
2537
2792
  else {
2538
- this.untrackedLoading = false;
2539
- this.$scheduleNextPoll$();
2793
+ this.untrackedPending = false;
2540
2794
  }
2541
2795
  }
2542
2796
  }
@@ -2559,341 +2813,82 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2559
2813
  }
2560
2814
  /** Called after SSR/unmount */
2561
2815
  async $destroy$() {
2562
- this.$clearNextPoll$();
2563
2816
  clearTimeout(this.$computationTimeoutId$);
2564
2817
  const current = this.$current$;
2565
2818
  if (current) {
2566
- this.$requestCleanups$(current);
2567
- }
2568
- if (this.$jobs$) {
2569
- await Promise.all(this.$jobs$.map((job) => job.$promise$));
2570
- }
2571
- else {
2572
- await current?.$promise$;
2573
- }
2574
- }
2575
- $clearNextPoll$() {
2576
- if (this.$pollTimeoutId$ !== undefined) {
2577
- clearTimeout(this.$pollTimeoutId$);
2578
- this.$pollTimeoutId$ = undefined;
2579
- }
2580
- }
2581
- $scheduleNextPoll$() {
2582
- if ((qTest ? isServerPlatform() : isServer) || !this.$expires$) {
2583
- return;
2584
- }
2585
- this.$clearNextPoll$();
2586
- const allowRecalc = !(this.$flags$ & 256 /* AsyncSignalFlags.NO_POLL */);
2587
- // Even when clear on invalidate, we don't clear if we're merely re-running due to polling
2588
- // We expect to get the new value soon, so we can avoid showing a loading state
2589
- const mustClear = this.$flags$ & 128 /* AsyncSignalFlags.CLEAR_ON_INVALIDATE */ && !allowRecalc;
2590
- this.$pollTimeoutId$ = setTimeout(() => this.$setInvalid$(allowRecalc, mustClear), this.$expires$);
2591
- this.$pollTimeoutId$?.unref?.();
2592
- }
2593
- $hasSubscribers$() {
2594
- return !!(this.$effects$?.size || this.$loadingEffects$?.size || this.$errorEffects$?.size);
2595
- }
2596
- $requestCleanups$(job, reason) {
2597
- if (job.$cleanupRequested$) {
2598
- return;
2599
- }
2600
- job.$cleanupRequested$ = true;
2601
- job.$abortController$?.abort(reason);
2602
- job.$promise$ = maybeThen(job.$promise$, () => this.$runCleanups$(job));
2603
- }
2604
- /** Clean up and trigger signal compute once complete */
2605
- $runCleanups$(job) {
2606
- const cleanups = job.$cleanups$;
2607
- const onError = (err) => {
2608
- const handleError = this.$container$?.handleError;
2609
- if (handleError) {
2610
- handleError(err, null);
2611
- }
2612
- else {
2613
- console.error('Error in async signal cleanup', err);
2614
- }
2615
- };
2616
- const onDone = () => {
2617
- job.$promise$ = null;
2618
- if (cleanups) {
2619
- cleanups.length = 0;
2620
- }
2621
- // Now trigger compute
2622
- const jobs = this.$jobs$;
2623
- if (jobs) {
2624
- const idx = jobs.indexOf(job);
2625
- if (idx !== -1) {
2626
- jobs.splice(idx, 1);
2627
- }
2628
- }
2629
- this.$computeIfNeeded$();
2630
- };
2631
- let promiseChain = undefined;
2632
- if (cleanups) {
2633
- // Keep this sync-ish so sync functions run immediately.
2634
- for (let i = 0; i < cleanups.length; i++) {
2635
- try {
2636
- const result = cleanups[i]();
2637
- if (isPromise(result)) {
2638
- promiseChain = (promiseChain ? promiseChain.then(() => result) : result).catch(onError);
2639
- }
2640
- }
2641
- catch (err) {
2642
- onError(err);
2643
- }
2644
- }
2645
- }
2646
- if (promiseChain) {
2647
- return promiseChain.then(onDone);
2648
- }
2649
- else {
2650
- onDone();
2651
- }
2652
- }
2653
- }
2654
- /**
2655
- * Inject a pre-loaded value into an AsyncSignal while preserving track() subscriptions. Calls
2656
- * `invalidate({ __v })` so the compute function reads the value from `info`, then triggers an
2657
- * immediate synchronous compute via the private $computeIfNeeded$() method (which is mangled in
2658
- * core builds, so callers from other packages must go through this helper).
2659
- *
2660
- * @internal
2661
- */
2662
- const _injectAsyncSignalValue = (signal, value) => {
2663
- const impl = signal;
2664
- impl.invalidate({ __v: value });
2665
- impl.$computeIfNeeded$();
2666
- };
2667
-
2668
- /** @internal */
2669
- const _CONST_PROPS = Symbol('CONST');
2670
- /** @internal */
2671
- const _VAR_PROPS = Symbol('VAR');
2672
- /** @internal */
2673
- const _OWNER = Symbol('OWNER');
2674
- /** @internal */
2675
- const _PROPS_HANDLER = Symbol('PROPS_HANDLER');
2676
- /** @internal @deprecated v1 compat */
2677
- const _IMMUTABLE = Symbol('IMMUTABLE');
2678
- /** @internal */
2679
- const _UNINITIALIZED = Symbol('UNINITIALIZED');
2680
-
2681
- /** @internal */
2682
- const EMPTY_ARRAY = [];
2683
- /** @internal */
2684
- const EMPTY_OBJ = {};
2685
- Object.freeze(EMPTY_ARRAY);
2686
- Object.freeze(EMPTY_OBJ);
2687
-
2688
- function createPropsProxy(owner) {
2689
- // TODO don't make a proxy but populate getters? benchmark
2690
- return new Proxy({}, new PropsProxyHandler(owner));
2691
- }
2692
- class PropsProxyHandler {
2693
- owner;
2694
- $effects$ = undefined;
2695
- $container$ = null;
2696
- constructor(owner) {
2697
- this.owner = owner;
2698
- }
2699
- get(_, prop) {
2700
- // escape hatch to get the separated props from a component
2701
- if (prop === _CONST_PROPS) {
2702
- return this.owner.constProps;
2703
- }
2704
- else if (prop === _VAR_PROPS) {
2705
- return this.owner.varProps;
2706
- }
2707
- else if (prop === _OWNER) {
2708
- return this.owner;
2709
- }
2710
- else if (prop === _PROPS_HANDLER) {
2711
- return this;
2712
- }
2713
- let value;
2714
- if (prop === 'children') {
2715
- value = this.owner.children;
2716
- }
2717
- else {
2718
- if (typeof prop === 'string' && typeof this.owner.type === 'string') {
2719
- const attr = jsxEventToHtmlAttribute(prop);
2720
- if (attr) {
2721
- prop = attr;
2722
- }
2723
- }
2724
- value = directGetPropsProxyProp(this.owner, prop);
2725
- if (prop in this.owner.varProps) {
2726
- addPropsProxyEffect(this, prop);
2727
- }
2728
- }
2729
- // a proxied value that the optimizer made
2730
- return value instanceof WrappedSignalImpl && value.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */
2731
- ? value.value
2732
- : value;
2733
- }
2734
- set(_, prop, value) {
2735
- if (prop === _OWNER) {
2736
- // used for deserialization
2737
- this.owner = value;
2738
- }
2739
- else if (prop === 'children') {
2740
- this.owner.children = value;
2741
- }
2742
- else if (prop === _CONST_PROPS) {
2743
- this.owner.constProps = value;
2744
- }
2745
- else if (prop === _VAR_PROPS) {
2746
- this.owner.varProps = value;
2747
- }
2748
- else {
2749
- if (typeof prop === 'string' && typeof this.owner.type === 'string') {
2750
- const attr = jsxEventToHtmlAttribute(prop);
2751
- if (attr) {
2752
- prop = attr;
2753
- }
2754
- }
2755
- if (this.owner.constProps && prop in this.owner.constProps) {
2756
- // delete the prop from the const props first
2757
- delete this.owner.constProps[prop];
2758
- }
2759
- if (this.owner.varProps === EMPTY_OBJ) {
2760
- this.owner.varProps = {};
2761
- }
2762
- else if (!(prop in this.owner.varProps)) {
2763
- this.owner.toSort = true;
2764
- }
2765
- if (this.owner.varProps[prop] !== value) {
2766
- this.owner.varProps[prop] = value;
2767
- triggerPropsProxyEffect(this, prop);
2768
- }
2769
- }
2770
- return true;
2771
- }
2772
- deleteProperty(_, prop) {
2773
- let didDelete = delete this.owner.varProps[prop];
2774
- if (didDelete) {
2775
- triggerPropsProxyEffect(this, prop);
2776
- }
2777
- if (this.owner.constProps) {
2778
- didDelete = delete this.owner.constProps[prop] || didDelete;
2779
- }
2780
- if (this.owner.children != null && prop === 'children') {
2781
- this.owner.children = null;
2782
- didDelete = true;
2783
- }
2784
- return didDelete;
2785
- }
2786
- has(_, prop) {
2787
- if (prop === 'children') {
2788
- return this.owner.children != null;
2789
- }
2790
- else if (prop === _CONST_PROPS || prop === _VAR_PROPS) {
2791
- return true;
2792
- }
2793
- const inVarProps = prop in this.owner.varProps;
2794
- if (typeof prop === 'string') {
2795
- if (inVarProps) {
2796
- addPropsProxyEffect(this, prop);
2797
- }
2798
- if (typeof this.owner.type === 'string') {
2799
- const attr = jsxEventToHtmlAttribute(prop);
2800
- if (attr) {
2801
- prop = attr;
2802
- }
2803
- }
2819
+ this.$requestCleanups$(current);
2820
+ }
2821
+ if (this.$jobs$) {
2822
+ await Promise.all(this.$jobs$.map((job) => job.$promise$));
2823
+ }
2824
+ else {
2825
+ await current?.$promise$;
2804
2826
  }
2805
- return inVarProps || (this.owner.constProps ? prop in this.owner.constProps : false);
2806
2827
  }
2807
- getOwnPropertyDescriptor(_, p) {
2808
- const value = p === 'children'
2809
- ? this.owner.children
2810
- : this.owner.constProps && p in this.owner.constProps
2811
- ? this.owner.constProps[p]
2812
- : this.owner.varProps[p];
2813
- return {
2814
- configurable: true,
2815
- enumerable: true,
2816
- value: value,
2817
- };
2828
+ $hasSubscribers$() {
2829
+ return !!(this.$effects$?.size || this.$loadingEffects$?.size || this.$errorEffects$?.size);
2818
2830
  }
2819
- ownKeys() {
2820
- const out = Object.keys(this.owner.varProps);
2821
- if (this.owner.children != null) {
2822
- out.push('children');
2831
+ $requestCleanups$(job, reason) {
2832
+ if (job.$cleanupRequested$) {
2833
+ return;
2823
2834
  }
2824
- if (this.owner.constProps) {
2825
- for (const key in this.owner.constProps) {
2826
- if (out.indexOf(key) === -1) {
2827
- out.push(key);
2835
+ job.$cleanupRequested$ = true;
2836
+ job.$abortController$?.abort(reason);
2837
+ job.$promise$ = maybeThen(job.$promise$, () => this.$runCleanups$(job));
2838
+ }
2839
+ /** Run a job's cleanup callbacks, returning a promise when any of them are async. */
2840
+ $runCleanupCallbacks$(job) {
2841
+ const cleanups = job.$cleanups$;
2842
+ const onError = (err) => {
2843
+ const handleError = this.$container$?.handleError;
2844
+ if (handleError) {
2845
+ handleError(err, null);
2846
+ }
2847
+ else {
2848
+ console.error('Error in async signal cleanup', err);
2849
+ }
2850
+ };
2851
+ let promiseChain = undefined;
2852
+ if (cleanups) {
2853
+ // Keep this sync-ish so sync functions run immediately.
2854
+ for (let i = 0; i < cleanups.length; i++) {
2855
+ try {
2856
+ const result = cleanups[i]();
2857
+ if (isPromise(result)) {
2858
+ promiseChain = (promiseChain ? promiseChain.then(() => result) : result).catch(onError);
2859
+ }
2860
+ }
2861
+ catch (err) {
2862
+ onError(err);
2828
2863
  }
2829
2864
  }
2865
+ cleanups.length = 0;
2830
2866
  }
2831
- return out;
2867
+ return promiseChain;
2832
2868
  }
2833
- }
2834
- const addPropsProxyEffect = (propsProxy, prop) => {
2835
- // Lazily grab the container from the invoke context
2836
- const ctx = tryGetInvokeContext();
2837
- if (ctx) {
2838
- if (propsProxy.$container$ === null) {
2839
- if (ctx.$container$) {
2840
- propsProxy.$container$ = ctx.$container$;
2869
+ /** Clean up and trigger signal compute once complete */
2870
+ $runCleanups$(job) {
2871
+ const onDone = () => {
2872
+ job.$promise$ = null;
2873
+ // Now trigger compute
2874
+ const jobs = this.$jobs$;
2875
+ if (jobs) {
2876
+ const idx = jobs.indexOf(job);
2877
+ if (idx !== -1) {
2878
+ jobs.splice(idx, 1);
2879
+ }
2841
2880
  }
2881
+ this.$computeIfNeeded$();
2882
+ };
2883
+ const promiseChain = this.$runCleanupCallbacks$(job);
2884
+ if (promiseChain) {
2885
+ return promiseChain.then(onDone);
2842
2886
  }
2843
2887
  else {
2844
- isDev &&
2845
- assertTrue(!ctx.$container$ || ctx.$container$ === propsProxy.$container$, 'Do not use props across containers');
2888
+ onDone();
2846
2889
  }
2847
2890
  }
2848
- const effectSubscriber = ctx?.$effectSubscriber$;
2849
- if (effectSubscriber) {
2850
- addStoreEffect(propsProxy.owner._proxy, prop, propsProxy, effectSubscriber);
2851
- }
2852
- };
2853
- const triggerPropsProxyEffect = (propsProxy, prop) => {
2854
- const effects = getEffects$1(propsProxy.$effects$, prop);
2855
- if (effects) {
2856
- scheduleEffects(propsProxy.$container$, propsProxy, effects);
2857
- }
2858
- return !!effects;
2859
- };
2860
- function getEffects$1(effects, prop) {
2861
- // TODO: Handle STORE_ALL_PROPS
2862
- return effects?.get(prop);
2863
2891
  }
2864
- /**
2865
- * Instead of using PropsProxyHandler getter (which could create a component-level subscription).
2866
- * Use this function to get the props directly from a const or var props.
2867
- *
2868
- * This does not convert jsx event names.
2869
- */
2870
- const directGetPropsProxyProp = (jsx, prop) => {
2871
- return (jsx.constProps && prop in jsx.constProps ? jsx.constProps[prop] : jsx.varProps[prop]);
2872
- };
2873
- const _getProps = (props, prop) => {
2874
- return _getVarProps(props)?.[prop] || _getConstProps(props)?.[prop] || null;
2875
- };
2876
- /** Used by the optimizer for spread props operations @internal */
2877
- const _getVarProps = (props) => {
2878
- if (!props) {
2879
- return null;
2880
- }
2881
- return _VAR_PROPS in props
2882
- ? 'children' in props
2883
- ? { ...props[_VAR_PROPS], children: props.children }
2884
- : props[_VAR_PROPS]
2885
- : props;
2886
- };
2887
- /** Used by the optimizer for spread props operations @internal */
2888
- const _getConstProps = (props) => {
2889
- if (!props) {
2890
- return null;
2891
- }
2892
- return _CONST_PROPS in props ? props[_CONST_PROPS] : null;
2893
- };
2894
- const isPropsProxy = (obj) => {
2895
- return obj && _VAR_PROPS in obj;
2896
- };
2897
2892
 
2898
2893
  // <docs markdown="../../readme.md#implicit$FirstArg">
2899
2894
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
@@ -2949,11 +2944,11 @@ const implicit$FirstArg = (fn) => {
2949
2944
  */
2950
2945
  class SerializerSignalImpl extends ComputedSignalImpl {
2951
2946
  constructor(container, argQrl) {
2952
- super(container, argQrl, 1 /* SignalFlags.INVALID */ | 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
2947
+ super(container, argQrl, 1 /* ComputedSignalFlags.INVALID */ | 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
2953
2948
  }
2954
2949
  $didInitialize$ = false;
2955
2950
  $computeIfNeeded$() {
2956
- if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
2951
+ if (!(this.$flags$ & 1 /* ComputedSignalFlags.INVALID */)) {
2957
2952
  return;
2958
2953
  }
2959
2954
  throwIfQRLNotResolved(this.$computeQrl$);
@@ -2966,7 +2961,7 @@ class SerializerSignalImpl extends ComputedSignalImpl {
2966
2961
  : deserialize(currentValue), this, "." /* EffectProperty.VNODE */, this.$container$);
2967
2962
  this.$didInitialize$ = true;
2968
2963
  // Needs to invalidate only after all possible Promise throws happened
2969
- this.$flags$ &= -2 /* SignalFlags.INVALID */;
2964
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2970
2965
  // We allow forcing the update of the signal without changing the value, for example when the deserialized value is the same reference as the old value but its internals have changed. In that case we want to trigger effects that depend on this signal, even though the value is the same.
2971
2966
  const didChange = (this.$didInitialize$ && untrackedValue !== 'undefined') ||
2972
2967
  untrackedValue !== this.$untrackedValue$;
@@ -2977,13 +2972,46 @@ class SerializerSignalImpl extends ComputedSignalImpl {
2977
2972
  }
2978
2973
  }
2979
2974
 
2975
+ /**
2976
+ * # ================================
2977
+ *
2978
+ * AsyncSignalImpl
2979
+ *
2980
+ * # ================================
2981
+ *
2982
+ * The async engine (jobs, loading, error) lives in ComputedSignalImpl; this subclass configures it
2983
+ * from options and switches the compute invocation to CTX_ARG mode: the compute fn receives the
2984
+ * ComputeCtx argument and tracks only via its explicit `track()` (no auto-tracking).
2985
+ *
2986
+ * @internal
2987
+ */
2988
+ class AsyncSignalImpl extends ComputedSignalImpl {
2989
+ constructor(container, fn, flags = 1 /* ComputedSignalFlags.INVALID */ |
2990
+ 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */, options) {
2991
+ super(container, fn, flags | 512 /* AsyncSignalFlags.ASYNC_MODE */ | 1024 /* AsyncSignalFlags.CTX_ARG */, options);
2992
+ }
2993
+ }
2994
+ /**
2995
+ * Inject a pre-loaded value into a signal while preserving subscriptions. Calls `invalidate({__v})`
2996
+ * so the compute function reads the value from `info`, then triggers an immediate synchronous
2997
+ * compute via the private `$computeIfNeeded$()` method (which is mangled in core builds, so callers
2998
+ * from other packages must go through this helper).
2999
+ *
3000
+ * @internal
3001
+ */
3002
+ const _injectAsyncSignalValue = (signal, value) => {
3003
+ const impl = signal;
3004
+ impl.invalidate({ __v: value });
3005
+ impl.$computeIfNeeded$();
3006
+ };
3007
+
2980
3008
  /** @internal */
2981
3009
  const createSignal$1 = (value) => {
2982
3010
  return new SignalImpl(null, value);
2983
3011
  };
2984
3012
  /** @internal */
2985
3013
  const createComputedSignal = (qrl, options) => {
2986
- return new ComputedSignalImpl(options?.container || null, qrl, getComputedSignalFlags(options?.serializationStrategy || 'always'));
3014
+ return new ComputedSignalImpl(options?.container || null, qrl, getComputedSignalFlags(options?.serializationStrategy || 'always'), options);
2987
3015
  };
2988
3016
  /** @internal */
2989
3017
  const createAsyncSignal = (qrl, options) => {
@@ -3007,9 +3035,10 @@ const createSignal = createSignal$1;
3007
3035
  * recalculated.
3008
3036
  *
3009
3037
  * The QRL must be a function which returns the value of the signal. The function must not have side
3010
- * effects, and it must be synchronous.
3011
- *
3012
- * If you need the function to be async, use `createAsync$` instead (don't forget to use `track()`).
3038
+ * effects. Every synchronous signal or store read is tracked automatically; reads after the first
3039
+ * `await` must use the `track()` provided on the context argument. When the function is async, the
3040
+ * returned signal exposes the async API (`.pending`, `.error`, `.promise()`), and reading an
3041
+ * unresolved `.value` throws the computation promise.
3013
3042
  *
3014
3043
  * @public
3015
3044
  */
@@ -3018,6 +3047,7 @@ const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedSignal);
3018
3047
  * Create a signal holding a `.value` which is calculated from the given async function (QRL). The
3019
3048
  * standalone version of `useAsync$`.
3020
3049
  *
3050
+ * @deprecated Use `createComputed$` instead, it has async support now.
3021
3051
  * @public
3022
3052
  */
3023
3053
  const createAsync$ = /*#__PURE__*/ implicit$FirstArg(createAsyncSignal);
@@ -3064,10 +3094,10 @@ const _wrapProp = (...args) => {
3064
3094
  return obj[prop];
3065
3095
  }
3066
3096
  if (isSignal(obj)) {
3067
- if (!(obj instanceof AsyncSignalImpl)) {
3097
+ if (!(obj instanceof ComputedSignalImpl)) {
3068
3098
  isDev && assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
3069
3099
  }
3070
- if (obj instanceof WrappedSignalImpl && obj.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */) {
3100
+ if (obj instanceof WrappedSignalImpl) {
3071
3101
  return obj;
3072
3102
  }
3073
3103
  return getWrapped(args);
@@ -3121,7 +3151,7 @@ class WrappedSignalImpl extends SignalImpl {
3121
3151
  constructor(container, fn, args, fnStr,
3122
3152
  // We need a separate flag to know when the computation needs running because
3123
3153
  // we need the old value to know if effects need running after computation
3124
- flags = 1 /* SignalFlags.INVALID */ | 4 /* WrappedSignalFlags.UNWRAP */) {
3154
+ flags = 1 /* ComputedSignalFlags.INVALID */) {
3125
3155
  super(container, NEEDS_COMPUTATION);
3126
3156
  this.$args$ = args;
3127
3157
  this.$func$ = fn;
@@ -3129,7 +3159,7 @@ class WrappedSignalImpl extends SignalImpl {
3129
3159
  this.$flags$ = flags;
3130
3160
  }
3131
3161
  invalidate() {
3132
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
3162
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
3133
3163
  // we are trying to run computation without creating a chore, which can be expensive
3134
3164
  // for many signals. If it fails, we schedule a chore to run the computation.
3135
3165
  try {
@@ -3142,8 +3172,8 @@ class WrappedSignalImpl extends SignalImpl {
3142
3172
  }
3143
3173
  }
3144
3174
  // if the computation not failed, we can run the effects directly
3145
- if (this.$flags$ & 2 /* SignalFlags.RUN_EFFECTS */) {
3146
- this.$flags$ &= -3 /* SignalFlags.RUN_EFFECTS */;
3175
+ if (this.$flags$ & 2 /* ComputedSignalFlags.RUN_EFFECTS */) {
3176
+ this.$flags$ &= -3 /* ComputedSignalFlags.RUN_EFFECTS */;
3147
3177
  scheduleEffects(this.$container$, this, this.$effects$);
3148
3178
  }
3149
3179
  }
@@ -3153,7 +3183,7 @@ class WrappedSignalImpl extends SignalImpl {
3153
3183
  return this.$untrackedValue$;
3154
3184
  }
3155
3185
  $computeIfNeeded$() {
3156
- if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
3186
+ if (!(this.$flags$ & 1 /* ComputedSignalFlags.INVALID */)) {
3157
3187
  return;
3158
3188
  }
3159
3189
  const untrackedValue = trackSignal(() => this.$func$(...this.$args$), this, "." /* EffectProperty.VNODE */, this.$container$);
@@ -3164,10 +3194,10 @@ class WrappedSignalImpl extends SignalImpl {
3164
3194
  // this.$flags$ &= ~SignalFlags.INVALID;
3165
3195
  // reset flag in case we call computedIfNeeded twice and the value was changed only the first time
3166
3196
  // TODO: change to version number?
3167
- this.$flags$ &= -3 /* SignalFlags.RUN_EFFECTS */;
3197
+ this.$flags$ &= -3 /* ComputedSignalFlags.RUN_EFFECTS */;
3168
3198
  const didChange = untrackedValue !== this.$untrackedValue$;
3169
3199
  if (didChange) {
3170
- this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
3200
+ this.$flags$ |= 2 /* ComputedSignalFlags.RUN_EFFECTS */;
3171
3201
  this.$untrackedValue$ = untrackedValue;
3172
3202
  }
3173
3203
  }
@@ -3271,12 +3301,15 @@ const isJSXNode = (n) => {
3271
3301
  * load the QRL captured scope ourselves. Otherwise, we are being called as a QRL and the captures
3272
3302
  * are already set.
3273
3303
  */
3274
- const maybeScopeFromQL = (captureIds, element) => {
3275
- if (typeof captureIds === 'string') {
3304
+ const withScopeFromQL = (captureDeltas, element, callback) => {
3305
+ if (typeof captureDeltas === 'string') {
3276
3306
  const container = getDomContainer(element);
3277
- setCaptures(deserializeCaptures(container, captureIds));
3307
+ return whenContainerDataReady(container, () => {
3308
+ setCaptures(deserializeCaptureDeltas(container, captureDeltas));
3309
+ return callback();
3310
+ });
3278
3311
  }
3279
- return null;
3312
+ return callback();
3280
3313
  };
3281
3314
  /**
3282
3315
  * Handles events for bind:value
@@ -3284,9 +3317,10 @@ const maybeScopeFromQL = (captureIds, element) => {
3284
3317
  * @internal
3285
3318
  */
3286
3319
  function _val(_, element) {
3287
- maybeScopeFromQL(this, element);
3288
- const signal = _captures[0];
3289
- signal.value = element.type === 'number' ? element.valueAsNumber : element.value;
3320
+ return withScopeFromQL(this, element, () => {
3321
+ const signal = _captures[0];
3322
+ signal.value = element.type === 'number' ? element.valueAsNumber : element.value;
3323
+ });
3290
3324
  }
3291
3325
  /**
3292
3326
  * Handles events for bind:checked
@@ -3294,9 +3328,10 @@ function _val(_, element) {
3294
3328
  * @internal
3295
3329
  */
3296
3330
  function _chk(_, element) {
3297
- maybeScopeFromQL(this, element);
3298
- const signal = _captures[0];
3299
- signal.value = element.checked;
3331
+ return withScopeFromQL(this, element, () => {
3332
+ const signal = _captures[0];
3333
+ signal.value = element.checked;
3334
+ });
3300
3335
  }
3301
3336
  /**
3302
3337
  * Resumes selected state (e.g. polling AsyncSignals) by deserializing captures. Used for
@@ -3305,17 +3340,18 @@ function _chk(_, element) {
3305
3340
  * @internal
3306
3341
  */
3307
3342
  function _res(_, element) {
3308
- maybeScopeFromQL(this, element);
3309
- // Captures are deserialized, now trigger computation on AsyncSignals
3310
- if (_captures) {
3311
- for (let i = 0; i < _captures.length; i++) {
3312
- const capture = _captures[i];
3313
- if (capture instanceof AsyncSignalImpl && capture.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
3314
- capture.$computeIfNeeded$();
3343
+ return withScopeFromQL(this, element, () => {
3344
+ // Captures are deserialized, now trigger computation on AsyncSignals
3345
+ if (_captures) {
3346
+ for (let i = 0; i < _captures.length; i++) {
3347
+ const capture = _captures[i];
3348
+ if (capture instanceof AsyncSignalImpl && capture.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
3349
+ capture.$computeIfNeeded$();
3350
+ }
3351
+ // note that polling async signals will automatically schedule themselves so no action needed
3315
3352
  }
3316
- // note that polling async signals will automatically schedule themselves so no action needed
3317
3353
  }
3318
- }
3354
+ });
3319
3355
  }
3320
3356
 
3321
3357
  const BIND_VALUE = 'bind:value';
@@ -3956,31 +3992,25 @@ const useConstant = (value, ...args) => {
3956
3992
  return set(value);
3957
3993
  };
3958
3994
 
3959
- const createCursorBoundary = () => {
3960
- return {
3961
- pending: createSignal(0),
3962
- version: createSignal(0),
3963
- };
3964
- };
3965
3995
  /** @internal */
3966
3996
  const useCursorBoundary = () => {
3967
3997
  if (!__EXPERIMENTAL__.suspense) {
3968
3998
  throw new Error('useCursorBoundary is experimental and must be enabled with `experimental: ["suspense"]` in the `qwikVite` plugin.');
3969
3999
  }
3970
- return useConstant(createCursorBoundary);
4000
+ return useSignal(0);
3971
4001
  };
3972
4002
  function addCursorBoundary(cursorData, vNode) {
3973
4003
  if (!__EXPERIMENTAL__.suspense) {
3974
4004
  return;
3975
4005
  }
3976
- const boundary = getNearestCursorBoundary(cursorData.container, vNode);
4006
+ const boundary = getNearestCursorBoundary(vNode);
3977
4007
  if (!boundary) {
3978
4008
  return;
3979
4009
  }
3980
4010
  const boundaries = (cursorData.boundaries ||= []);
3981
4011
  if (!boundaries.includes(boundary)) {
3982
4012
  boundaries.push(boundary);
3983
- boundary.pending.value++;
4013
+ boundary.value++;
3984
4014
  }
3985
4015
  }
3986
4016
  function resolveCursorBoundaries(cursorData) {
@@ -3994,40 +4024,35 @@ function resolveCursorBoundaries(cursorData) {
3994
4024
  cursorData.boundaries = null;
3995
4025
  for (let i = 0; i < boundaries.length; i++) {
3996
4026
  const boundary = boundaries[i];
3997
- boundary.pending.value = Math.max(0, boundary.pending.value - 1);
3998
- boundary.version.value++;
3999
- }
4000
- }
4001
- function getOwnCursorBoundary(container, vNode) {
4002
- if (!__EXPERIMENTAL__.suspense) {
4003
- return null;
4027
+ boundary.value = Math.max(0, boundary.value - 1);
4004
4028
  }
4005
- return container.getHostProp(vNode, QCursorBoundary);
4006
4029
  }
4007
- function getNearestCursorBoundaryProp(vNode) {
4030
+ function getOwnCursorBoundary(vNode) {
4008
4031
  if (!__EXPERIMENTAL__.suspense) {
4009
4032
  return null;
4010
4033
  }
4011
- return (vnode_getProp(vNode, NEAREST_CURSOR_BOUNDARY, null) ||
4012
- null);
4034
+ return vnode_getProp(vNode, QCursorBoundary, null) || null;
4013
4035
  }
4014
4036
  function clearNearestCursorBoundary(vNode) {
4015
- if (!__EXPERIMENTAL__.suspense) {
4016
- return;
4017
- }
4018
- vnode_setProp(vNode, NEAREST_CURSOR_BOUNDARY, null);
4037
+ setNearestCursorBoundary(vNode, null);
4019
4038
  }
4020
- function getNearestCursorBoundary(container, vNode) {
4039
+ function getNearestCursorBoundary(vNode) {
4021
4040
  if (!__EXPERIMENTAL__.suspense) {
4022
4041
  return null;
4023
4042
  }
4024
- return getNearestCursorBoundaryProp(vNode) || getOwnCursorBoundary(container, vNode);
4043
+ return (vnode_getProp(vNode, NEAREST_CURSOR_BOUNDARY, null) ||
4044
+ getOwnCursorBoundary(vNode));
4025
4045
  }
4026
4046
  function setNearestCursorBoundary(vNode, boundary) {
4027
4047
  __EXPERIMENTAL__.suspense && vnode_setProp(vNode, NEAREST_CURSOR_BOUNDARY, boundary);
4028
4048
  }
4049
+ function clearCursorBoundary(vNode) {
4050
+ if (__EXPERIMENTAL__.suspense && vNode.props && QCursorBoundary in vNode.props) {
4051
+ vnode_setProp(vNode, QCursorBoundary, null);
4052
+ }
4053
+ }
4029
4054
  /** Updates the nearest cursor boundary cache on a vnode and any already-dirty descendants. */
4030
- function updateDirtySubtreeCursorBoundary(container, vNode, boundary) {
4055
+ function updateDirtySubtreeCursorBoundary(vNode, boundary) {
4031
4056
  if (!__EXPERIMENTAL__.suspense) {
4032
4057
  return;
4033
4058
  }
@@ -4038,7 +4063,7 @@ function updateDirtySubtreeCursorBoundary(container, vNode, boundary) {
4038
4063
  }
4039
4064
  for (let i = 0; i < dirtyChildren.length; i++) {
4040
4065
  const child = dirtyChildren[i];
4041
- updateDirtySubtreeCursorBoundary(container, child, getOwnCursorBoundary(container, child) || boundary);
4066
+ updateDirtySubtreeCursorBoundary(child, getOwnCursorBoundary(child) || boundary);
4042
4067
  }
4043
4068
  }
4044
4069
 
@@ -4095,7 +4120,8 @@ function runEventHandlerQRL(handler, event, element, ctx) {
4095
4120
  return;
4096
4121
  }
4097
4122
  if (!ctx) {
4098
- ctx = newInvokeContextFromDOM(event, element);
4123
+ const container = getDomContainer(element);
4124
+ return whenContainerDataReady(container, () => runEventHandlerQRL(handler, event, element, newInvokeContextFromDOM(event, element, container)));
4099
4125
  }
4100
4126
  const container = ctx.$container$;
4101
4127
  const hostElement = ctx.$hostElement$;
@@ -4136,13 +4162,16 @@ function _run(event, element) {
4136
4162
  // ignore events on disconnected elements, this can happen when the event is triggered while the element is being removed
4137
4163
  return;
4138
4164
  }
4139
- const ctx = newInvokeContextFromDOM(event, element);
4140
- if (typeof this === 'string') {
4141
- setCaptures(deserializeCaptures(ctx.$container$, this));
4142
- }
4143
- const qrlToRun = _captures[0];
4144
- isDev && assertQrl(qrlToRun);
4145
- return runEventHandlerQRL(qrlToRun, event, element, ctx);
4165
+ const container = getDomContainer(element);
4166
+ return whenContainerDataReady(container, () => {
4167
+ const ctx = newInvokeContextFromDOM(event, element, container);
4168
+ if (typeof this === 'string') {
4169
+ setCaptures(deserializeCaptureDeltas(ctx.$container$, this));
4170
+ }
4171
+ const qrlToRun = _captures[0];
4172
+ isDev && assertQrl(qrlToRun);
4173
+ return runEventHandlerQRL(qrlToRun, event, element, ctx);
4174
+ });
4146
4175
  }
4147
4176
 
4148
4177
  function peekNextSibling(vCurrent) {
@@ -4303,9 +4332,7 @@ function diff(diffContext, jsxNode, vStartNode, vCurrent = vnode_getFirstChild(v
4303
4332
  else if (type === Projection) {
4304
4333
  expectProjection(diffContext);
4305
4334
  descend(diffContext, diffContext.$jsxValue$.children, true,
4306
- // special case for projection, we don't want to expect no children
4307
- // because the projection's children are not removed
4308
- false);
4335
+ /* isProjection */ true);
4309
4336
  }
4310
4337
  else if (type === SSRComment) {
4311
4338
  expectNoMore(diffContext);
@@ -4418,9 +4445,11 @@ function advance(diffContext) {
4418
4445
  *
4419
4446
  * In the above example all nodes are on same level so we don't `descendVNode` even thought there is
4420
4447
  * an array produced by the `map` function.
4448
+ * @param isProjection - True when descending into a Projection, whose children survive re-renders:
4449
+ * they are diffed against (never re-created) and kept when the new JSX has no children.
4421
4450
  */
4422
- function descend(diffContext, children, descendVNode, shouldExpectNoChildren = true) {
4423
- if (shouldExpectNoChildren &&
4451
+ function descend(diffContext, children, descendVNode, isProjection = false) {
4452
+ if (!isProjection &&
4424
4453
  (children == null || (descendVNode && isArray(children) && children.length === 0))) {
4425
4454
  expectNoChildren(diffContext);
4426
4455
  return;
@@ -4429,7 +4458,10 @@ function descend(diffContext, children, descendVNode, shouldExpectNoChildren = t
4429
4458
  if (descendVNode) {
4430
4459
  isDev &&
4431
4460
  assertDefined(diffContext.$vCurrent$ || diffContext.$vNewNode$, 'Expecting vCurrent to be defined.');
4432
- const creationMode = diffContext.$isCreationMode$ ||
4461
+ const creationMode =
4462
+ // a projection keeps its children across re-renders, so it never inherits
4463
+ // creation mode from a not-yet-rendered host; create only if new or empty
4464
+ (!isProjection && diffContext.$isCreationMode$) ||
4433
4465
  !!diffContext.$vNewNode$ ||
4434
4466
  !vnode_getFirstChild(diffContext.$vCurrent$);
4435
4467
  diffContext.$isCreationMode$ = creationMode;
@@ -4581,7 +4613,7 @@ function expectSlot(diffContext) {
4581
4613
  diffContext.$vNewNode$ = vnode_newVirtual();
4582
4614
  vnode_setProp(diffContext.$vNewNode$, QSlot, slotNameKey);
4583
4615
  vnode_setProp(diffContext.$vNewNode$, QCursorBoundary, cursorBoundary);
4584
- updateDirtySubtreeCursorBoundary(diffContext.$container$, diffContext.$vNewNode$, cursorBoundary);
4616
+ updateDirtySubtreeCursorBoundary(diffContext.$vNewNode$, cursorBoundary);
4585
4617
  vHost && vnode_setProp(vHost, slotNameKey, diffContext.$vNewNode$);
4586
4618
  isDev &&
4587
4619
  vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "P" /* VirtualType.Projection */); // Nothing to project, so render content of the slot.
@@ -4595,7 +4627,7 @@ function expectSlot(diffContext) {
4595
4627
  diffContext.$vNewNode$ = vProjectedNode;
4596
4628
  vnode_setProp(diffContext.$vNewNode$, QSlot, slotNameKey);
4597
4629
  vnode_setProp(diffContext.$vNewNode$, QCursorBoundary, cursorBoundary);
4598
- updateDirtySubtreeCursorBoundary(diffContext.$container$, diffContext.$vNewNode$, cursorBoundary);
4630
+ updateDirtySubtreeCursorBoundary(diffContext.$vNewNode$, cursorBoundary);
4599
4631
  vHost && vnode_setProp(vHost, slotNameKey, diffContext.$vNewNode$);
4600
4632
  isDev &&
4601
4633
  vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "P" /* VirtualType.Projection */);
@@ -4862,18 +4894,20 @@ function diffProps(diffContext, vnode, newAttrs, currentFile) {
4862
4894
  vnode_ensureElementInflated(diffContext.$container$, vnode);
4863
4895
  }
4864
4896
  const oldAttrs = vnode.props;
4897
+ const isHtml = (vnode.flags & 1536 /* VNodeFlags.NAMESPACE_MASK */) === 0 /* VNodeFlags.NS_html */;
4865
4898
  // Actual diffing logic
4866
4899
  // Apply all new attributes
4867
4900
  for (const key in newAttrs) {
4868
4901
  const newValue = newAttrs[key];
4869
- if (oldAttrs && _hasOwnProperty.call(oldAttrs, key)) {
4870
- const oldValue = oldAttrs[key];
4871
- if (newValue !== oldValue) {
4872
- if (newValue instanceof WrappedSignalImpl &&
4873
- oldValue instanceof WrappedSignalImpl &&
4874
- areWrappedSignalsEqual(newValue, oldValue)) {
4875
- continue;
4876
- }
4902
+ // HTML parsers lowercase attribute names.
4903
+ const oldKey = oldAttrs && isHtml && !_hasOwnProperty.call(oldAttrs, key) ? key.toLowerCase() : key;
4904
+ if (oldAttrs && _hasOwnProperty.call(oldAttrs, oldKey)) {
4905
+ const oldValue = oldAttrs[oldKey];
4906
+ if (oldKey !== key) {
4907
+ vnode_setProp(vnode, oldKey, null);
4908
+ vnode_setProp(vnode, key, oldValue);
4909
+ }
4910
+ if (!areDiffValuesEqual(newValue, oldValue)) {
4877
4911
  patchProperty(diffContext, vnode, key, newValue, currentFile);
4878
4912
  }
4879
4913
  }
@@ -5285,7 +5319,9 @@ function getComponentHash(vNode, getObject) {
5285
5319
  return null;
5286
5320
  }
5287
5321
  const qrl = vnode_getProp(vNode, OnRenderProp, getObject);
5288
- return qrl ? qrl.$hash$ : null;
5322
+ return qrl
5323
+ ? qrl.$hash$
5324
+ : vnode_getProp(vNode, QComponentHash, null);
5289
5325
  }
5290
5326
  /**
5291
5327
  * Marker class for JSX projection.
@@ -5343,13 +5379,26 @@ function handleChangedProps(src, dst, propsHandler, container, triggerEffects =
5343
5379
  if (key === 'children' || key === QBackRefs) {
5344
5380
  continue;
5345
5381
  }
5346
- if (!dst || src[key] !== dst[key]) {
5382
+ const newValue = src[key];
5383
+ const oldValue = dst?.[key];
5384
+ if (!dst ||
5385
+ !(areDiffValuesEqual(newValue, oldValue) || areSignalValuesEqual(newValue, oldValue))) {
5386
+ if (key === 'class') {
5387
+ if (areClassValuesEqual(newValue, oldValue)) {
5388
+ continue;
5389
+ }
5390
+ }
5391
+ else if (key === 'style') {
5392
+ if (areStyleValuesEqual(newValue, oldValue)) {
5393
+ continue;
5394
+ }
5395
+ }
5347
5396
  if (triggerEffects) {
5348
5397
  if (dst) {
5349
5398
  // Update the value in dst BEFORE triggering effects
5350
5399
  // so effects see the new value
5351
5400
  // Note: Value is not triggering effects, because we are modyfing direct VAR_PROPS object
5352
- dst[key] = src[key];
5401
+ dst[key] = newValue;
5353
5402
  }
5354
5403
  const didTigger = triggerPropsProxyEffect(propsHandler, key);
5355
5404
  if (!didTigger) {
@@ -5436,10 +5485,16 @@ function cleanup(container, journal, vNode, cursorRoot = null) {
5436
5485
  // don't call cleanupDestroyable yet, do it by the scheduler
5437
5486
  continue;
5438
5487
  }
5439
- else if (obj instanceof SignalImpl || isStore(obj)) {
5440
- clearAllEffects(container, obj);
5488
+ // Stores and plain signals are only producers; their subscriptions are removed
5489
+ // when cleaning the consumers that read them. They don't own reactive backrefs.
5490
+ else if (obj instanceof ComputedSignalImpl || obj instanceof WrappedSignalImpl) {
5491
+ if (!(obj.$flags$ & 4 /* ComputedSignalFlags.PRESERVE_ON_SEQ_CLEANUP */)) {
5492
+ clearAllEffects(container, obj);
5493
+ }
5441
5494
  }
5442
- if (objIsTask || obj instanceof AsyncSignalImpl) {
5495
+ if (objIsTask ||
5496
+ (obj instanceof ComputedSignalImpl &&
5497
+ (obj.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */ || obj.$current$))) {
5443
5498
  cleanupDestroyable(obj);
5444
5499
  }
5445
5500
  }
@@ -5558,6 +5613,37 @@ function markVNodeAsDeleted(vCursor) {
5558
5613
  */
5559
5614
  vCursor.flags |= 32 /* VNodeFlags.Deleted */;
5560
5615
  }
5616
+ function areDiffValuesEqual(newValue, oldValue) {
5617
+ if (newValue === oldValue) {
5618
+ return true;
5619
+ }
5620
+ return (newValue instanceof WrappedSignalImpl &&
5621
+ oldValue instanceof WrappedSignalImpl &&
5622
+ areWrappedSignalsEqual(oldValue, newValue));
5623
+ }
5624
+ function areSignalValuesEqual(newValue, oldValue) {
5625
+ if (newValue === oldValue) {
5626
+ return true;
5627
+ }
5628
+ if (isSignal(newValue) && isSignal(oldValue)) {
5629
+ const unwrappedNew = newValue instanceof WrappedSignalImpl ? newValue.$unwrapIfSignal$() : newValue;
5630
+ const unwrappedOld = oldValue instanceof WrappedSignalImpl ? oldValue.$unwrapIfSignal$() : oldValue;
5631
+ return untrack(() => unwrappedNew.value === unwrappedOld.value);
5632
+ }
5633
+ return false;
5634
+ }
5635
+ function areStyleValuesEqual(newValue, oldValue) {
5636
+ if (newValue === oldValue) {
5637
+ return true;
5638
+ }
5639
+ return stringifyStyle(newValue) === stringifyStyle(oldValue);
5640
+ }
5641
+ function areClassValuesEqual(newValue, oldValue) {
5642
+ if (newValue === oldValue) {
5643
+ return true;
5644
+ }
5645
+ return serializeClass(newValue) === serializeClass(oldValue);
5646
+ }
5561
5647
  function areWrappedSignalsEqual(oldSignal, newSignal) {
5562
5648
  if (oldSignal === newSignal) {
5563
5649
  return true;
@@ -5860,8 +5946,8 @@ function findFirstElementNode(jsx) {
5860
5946
  function injectPlaceholderElement(jsx, placeholder) {
5861
5947
  // For regular JSX nodes, we can append the placeholder to its children.
5862
5948
  if (isJSXNode(jsx)) {
5863
- // Inline components don't always render children, so we wrap them in Fragment which does.
5864
- if (jsx.type !== Fragment && !isQwikComponent(jsx.type)) {
5949
+ // Inline components may ignore children, while component$ children become projections.
5950
+ if (jsx.type !== Fragment) {
5865
5951
  return _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null);
5866
5952
  }
5867
5953
  if (jsx.children == null) {
@@ -6406,8 +6492,8 @@ function executeCompute(vNode, container) {
6406
6492
  ctx.$container$ = container;
6407
6493
  // needed for computed signals and throwing QRLs
6408
6494
  return maybeThen(retryOnPromise(() => invoke.call(target, ctx, target.$computeIfNeeded$)), () => {
6409
- if (target.$flags$ & 2 /* SignalFlags.RUN_EFFECTS */) {
6410
- target.$flags$ &= -3 /* SignalFlags.RUN_EFFECTS */;
6495
+ if (target.$flags$ & 2 /* ComputedSignalFlags.RUN_EFFECTS */) {
6496
+ target.$flags$ &= -3 /* ComputedSignalFlags.RUN_EFFECTS */;
6411
6497
  return scheduleEffects(container, target, effects);
6412
6498
  }
6413
6499
  });
@@ -6825,6 +6911,7 @@ function finishWalk(container, cursor, cursorData, isServer) {
6825
6911
  executeFlushPhase(cursor, container);
6826
6912
  }
6827
6913
  resolveCursorBoundaries(cursorData);
6914
+ clearCursorBoundary(cursor);
6828
6915
  if (cursorData.extraPromises) {
6829
6916
  Promise.all(cursorData.extraPromises).then(() => {
6830
6917
  resolveCursor(container);
@@ -6925,12 +7012,7 @@ function getNextVNode(vNode, cursor, container) {
6925
7012
  return getNextVNode(parent, cursor, container);
6926
7013
  }
6927
7014
  function splitCursorBoundary(container, vNode) {
6928
- if (!__EXPERIMENTAL__.suspense) {
6929
- return false;
6930
- }
6931
- if (!vNode.props ||
6932
- !(QCursorBoundary in vNode.props) ||
6933
- !container.getHostProp(vNode, QCursorBoundary)) {
7015
+ if (!getOwnCursorBoundary(vNode)) {
6934
7016
  return false;
6935
7017
  }
6936
7018
  if (!isCursor(vNode)) {
@@ -6948,6 +7030,10 @@ function splitCursorBoundary(container, vNode) {
6948
7030
  * @returns The vNode itself, now acting as a cursor
6949
7031
  */
6950
7032
  function addCursor(container, root, priority) {
7033
+ if (isCursor(root)) {
7034
+ triggerCursors();
7035
+ return root;
7036
+ }
6951
7037
  const cursorData = {
6952
7038
  afterFlushTasks: null,
6953
7039
  extraPromises: null,
@@ -7000,7 +7086,11 @@ function _executeSsrChores(container, ssrNode) {
7000
7086
  if (ssrNode.dirty & 32 /* ChoreBits.COMPUTE */) {
7001
7087
  executeCompute(ssrNode, container);
7002
7088
  }
7003
- if (isDev && ssrNode.dirty & 1023 /* ChoreBits.DIRTY_MASK */) {
7089
+ if (isDev &&
7090
+ ssrNode.dirty & 1023 /* ChoreBits.DIRTY_MASK */ &&
7091
+ !(ssrNode.flags & 2 /* SsrNodeFlags.WarnedStreamedChore */)) {
7092
+ // Warn once per host; a re-dirtied streamed host would otherwise spam every tick.
7093
+ ssrNode.flags |= 2 /* SsrNodeFlags.WarnedStreamedChore */;
7004
7094
  // We are running on the server.
7005
7095
  // On server we can't schedule task for a different host!
7006
7096
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -7123,17 +7213,16 @@ function propagatePath(target) {
7123
7213
  * Propagates dirty bits from vNode up to the specified cursorRoot. Used during diff when we know
7124
7214
  * the cursor root to merge with. Also updates cursor position if we pass through any cursors.
7125
7215
  */
7126
- function propagateToCursorRoot(container, vNode, cursorRoot) {
7216
+ function propagateToCursorRoot(vNode, cursorRoot) {
7127
7217
  reusablePath.push(vNode);
7128
- let cursorBoundary = getOwnCursorBoundary(container, vNode);
7218
+ let cursorBoundary = getOwnCursorBoundary(vNode);
7129
7219
  let current = vNode.slotParent || vNode.parent;
7130
7220
  while (current) {
7131
7221
  const isDirty = current.dirty & 1023 /* ChoreBits.DIRTY_MASK */;
7132
7222
  const currentIsCursor = isCursor(current);
7133
7223
  if (__EXPERIMENTAL__.suspense) {
7134
7224
  cursorBoundary ||=
7135
- getOwnCursorBoundary(container, current) ||
7136
- (isDirty ? getNearestCursorBoundary(container, current) : null);
7225
+ getOwnCursorBoundary(current) || (isDirty ? getNearestCursorBoundary(current) : null);
7137
7226
  }
7138
7227
  // Stop when we reach the cursor root or a dirty ancestor
7139
7228
  if (current === cursorRoot || isDirty) {
@@ -7168,18 +7257,16 @@ function propagateToCursorRoot(container, vNode, cursorRoot) {
7168
7257
  * Finds a blocking cursor or dirty ancestor and propagates dirty bits to it. Returns true if found
7169
7258
  * and attached, false if a new cursor should be created.
7170
7259
  */
7171
- function findAndPropagateToBlockingCursor(container, vNode) {
7260
+ function findAndPropagateToBlockingCursor(vNode) {
7172
7261
  reusablePath.push(vNode);
7173
- let cursorBoundary = __EXPERIMENTAL__.suspense
7174
- ? getOwnCursorBoundary(container, vNode)
7175
- : null;
7262
+ let cursorBoundary = getOwnCursorBoundary(vNode);
7176
7263
  let current = vNode.slotParent || vNode.parent;
7177
7264
  while (current) {
7178
7265
  const currentIsCursor = isCursor(current);
7179
7266
  if (__EXPERIMENTAL__.suspense) {
7180
7267
  cursorBoundary ||=
7181
- getOwnCursorBoundary(container, current) ||
7182
- (currentIsCursor ? getNearestCursorBoundary(container, current) : null);
7268
+ getOwnCursorBoundary(current) ||
7269
+ (currentIsCursor ? getNearestCursorBoundary(current) : null);
7183
7270
  }
7184
7271
  if (currentIsCursor) {
7185
7272
  // Existing cursor case: attach this dirty vnode to the blocking cursor found above it and
@@ -7220,7 +7307,7 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
7220
7307
  ? container.$renderPromise$.then(() => result)
7221
7308
  : result;
7222
7309
  }
7223
- return;
7310
+ return result;
7224
7311
  }
7225
7312
  const isRealDirty = bits & 1023 /* ChoreBits.DIRTY_MASK */;
7226
7313
  // If already dirty, no need to propagate again
@@ -7230,14 +7317,14 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
7230
7317
  const parent = vNode.slotParent || vNode.parent;
7231
7318
  // If cursorRoot is provided, propagate up to it
7232
7319
  if (cursorRoot && isRealDirty && parent && !parent.dirty) {
7233
- propagateToCursorRoot(container, vNode, cursorRoot);
7320
+ propagateToCursorRoot(vNode, cursorRoot);
7234
7321
  return;
7235
7322
  }
7236
7323
  // We must attach to a cursor subtree if it exists
7237
7324
  if (parent && parent.dirty & 1023 /* ChoreBits.DIRTY_MASK */) {
7238
7325
  // Dirty parent case: this vnode joins an already scheduled subtree, so inherit the parent's
7239
7326
  // nearest boundary unless this vnode owns a boundary itself.
7240
- setNearestCursorBoundary(vNode, getOwnCursorBoundary(container, vNode) || getNearestCursorBoundary(container, parent));
7327
+ setNearestCursorBoundary(vNode, getOwnCursorBoundary(vNode) || getNearestCursorBoundary(parent));
7241
7328
  if (isRealDirty) {
7242
7329
  parent.dirty |= 64 /* ChoreBits.CHILDREN */;
7243
7330
  }
@@ -7269,7 +7356,7 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
7269
7356
  else if (!isCursor(vNode)) {
7270
7357
  // Check if there's an existing cursor that is blocking (executing a render-blocking task)
7271
7358
  // If so, merge with it instead of creating a new cursor (single-pass find + propagate)
7272
- if (!findAndPropagateToBlockingCursor(container, vNode)) {
7359
+ if (!findAndPropagateToBlockingCursor(vNode)) {
7273
7360
  // No blocking cursor found, create a new one
7274
7361
  addCursor(container, vNode, 0);
7275
7362
  }
@@ -7277,7 +7364,7 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
7277
7364
  else {
7278
7365
  // Existing cursor-root case: the vnode is already the scheduled cursor, so only its own
7279
7366
  // boundary can be authoritative here.
7280
- setNearestCursorBoundary(vNode, getOwnCursorBoundary(container, vNode));
7367
+ setNearestCursorBoundary(vNode, getOwnCursorBoundary(vNode));
7281
7368
  }
7282
7369
  }
7283
7370
  function addVNodeOperation(journal, operation) {
@@ -8717,8 +8804,17 @@ const processVNodeData$1 = (vData, callback) => {
8717
8804
  }
8718
8805
  return vData.substring(start, nextToConsumeIdx);
8719
8806
  };
8807
+ const consumeEncodedValue = () => {
8808
+ if (getChar(nextToConsumeIdx + 1) !== VNodeDataChar.SEPARATOR) {
8809
+ return consumeValue();
8810
+ }
8811
+ consume();
8812
+ const value = decodeURIComponent(decodeVNodeDataString(consumeValue()));
8813
+ consume();
8814
+ return value;
8815
+ };
8720
8816
  while (peek() !== 0) {
8721
- callback(peek, consumeValue, consume, getChar, nextToConsumeIdx);
8817
+ callback(peek, consumeValue, consume, getChar, nextToConsumeIdx, consumeEncodedValue);
8722
8818
  }
8723
8819
  };
8724
8820
  /** @internal */
@@ -8909,13 +9005,13 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8909
9005
  return !nodeIsElement || (nodeIsElement && shouldSkipElement(node));
8910
9006
  };
8911
9007
  let components = null;
8912
- processVNodeData$1(vData, (peek, consumeValue, consume, getChar, nextToConsumeIdx) => {
9008
+ processVNodeData$1(vData, (peek, consumeValue, consume, getChar, dataIdx, consumeEncodedValue) => {
8913
9009
  if (isNumber(peek())) {
8914
9010
  // Element counts get encoded as numbers.
8915
9011
  while (shouldSkipNode(child)) {
8916
9012
  child = fastNextSibling(child);
8917
9013
  if (!child) {
8918
- throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), nextToConsumeIdx]);
9014
+ throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), dataIdx]);
8919
9015
  }
8920
9016
  }
8921
9017
  combinedText = null;
@@ -8936,7 +9032,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8936
9032
  shouldSkipElement(child)) {
8937
9033
  child = fastNextSibling(child);
8938
9034
  if (!child && value > 0) {
8939
- throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), nextToConsumeIdx]);
9035
+ throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), dataIdx]);
8940
9036
  }
8941
9037
  }
8942
9038
  }
@@ -8946,8 +9042,14 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8946
9042
  vnode_setProp(vParent, QScopedStyle, consumeValue());
8947
9043
  }
8948
9044
  else if (peek() === VNodeDataChar.RENDER_FN) {
8949
- (components ||= []).push(vParent);
8950
- vnode_setProp(vParent, OnRenderProp, consumeValue());
9045
+ const renderRef = consumeValue();
9046
+ if (renderRef.charCodeAt(0) === VNodeDataChar.RENDER_HASH_PREFIX) {
9047
+ vnode_setProp(vParent, QComponentHash, decodeURIComponent(decodeVNodeDataString(renderRef.slice(1))));
9048
+ }
9049
+ else {
9050
+ (components ||= []).push(vParent);
9051
+ vnode_setProp(vParent, OnRenderProp, renderRef);
9052
+ }
8951
9053
  }
8952
9054
  else if (peek() === VNodeDataChar.ID) {
8953
9055
  if (!container) {
@@ -8961,17 +9063,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8961
9063
  vnode_setProp(vParent, ELEMENT_PROPS, consumeValue());
8962
9064
  }
8963
9065
  else if (peek() === VNodeDataChar.KEY) {
8964
- const isEscapedValue = getChar(nextToConsumeIdx + 1) === VNodeDataChar.SEPARATOR;
8965
- let value;
8966
- if (isEscapedValue) {
8967
- consume();
8968
- value = decodeURIComponent(decodeVNodeDataString(consumeValue()));
8969
- consume();
8970
- }
8971
- else {
8972
- value = consumeValue();
8973
- }
8974
- vParent.key = value;
9066
+ vParent.key = consumeEncodedValue();
8975
9067
  }
8976
9068
  else if (peek() === VNodeDataChar.SEQ) {
8977
9069
  vnode_setProp(vParent, ELEMENT_SEQ, consumeValue());
@@ -9005,8 +9097,8 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
9005
9097
  else if (peek() === VNodeDataChar.SEPARATOR) {
9006
9098
  // Custom attribute: |key|value
9007
9099
  const keyValue = consumeValue();
9008
- const key = decodeVNodeDataString(keyValue);
9009
- const valueSeparatorIdx = nextToConsumeIdx + keyValue.length + 1;
9100
+ const key = decodeURIComponent(decodeVNodeDataString(keyValue));
9101
+ const valueSeparatorIdx = dataIdx + keyValue.length + 1;
9010
9102
  const isEscapedValue = getChar(valueSeparatorIdx + 1) === VNodeDataChar.SEPARATOR;
9011
9103
  let value;
9012
9104
  if (isEscapedValue) {
@@ -9029,8 +9121,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
9029
9121
  vParent = stack.pop();
9030
9122
  }
9031
9123
  else if (peek() === VNodeDataChar.SLOT) {
9032
- const value = decodeVNodeDataString(consumeValue());
9033
- vnode_setProp(vParent, QSlot, value);
9124
+ vnode_setProp(vParent, QSlot, consumeEncodedValue());
9034
9125
  }
9035
9126
  else {
9036
9127
  // skip over style or non-qwik elements in front of text nodes, where text node is the first child (except the style node)
@@ -9141,12 +9232,12 @@ function clearEffectSubscription(container, effect) {
9141
9232
  return;
9142
9233
  }
9143
9234
  for (const producer of backRefs) {
9144
- // Check AsyncSignalImpl before SignalImpl since it extends SignalImpl
9145
- if (producer instanceof AsyncSignalImpl) {
9146
- clearAsyncSignal(producer, effect);
9147
- }
9148
- else if (producer instanceof SignalImpl) {
9235
+ if (producer instanceof SignalImpl) {
9149
9236
  clearSignal(container, producer, effect);
9237
+ if (producer instanceof ComputedSignalImpl) {
9238
+ // computeds can carry async engine state (loading/error subscribers)
9239
+ clearAsyncSignalEffects(producer, effect);
9240
+ }
9150
9241
  }
9151
9242
  else if (isPropsProxy(producer)) {
9152
9243
  const propsHandler = producer[_PROPS_HANDLER];
@@ -9171,11 +9262,7 @@ function clearSignal(container, producer, effect) {
9171
9262
  clearAllEffects(container, producer);
9172
9263
  }
9173
9264
  }
9174
- function clearAsyncSignal(producer, effect) {
9175
- const effects = producer.$effects$;
9176
- if (effects && effects.has(effect)) {
9177
- effects.delete(effect);
9178
- }
9265
+ function clearAsyncSignalEffects(producer, effect) {
9179
9266
  const pendingEffects = producer.$loadingEffects$;
9180
9267
  if (pendingEffects && pendingEffects.has(effect)) {
9181
9268
  pendingEffects.delete(effect);
@@ -9211,7 +9298,7 @@ const useTaskQrl = (qrl, opts) => {
9211
9298
  const taskFlags =
9212
9299
  // enabled by default
9213
9300
  opts?.deferUpdates === false ? 0 : 8 /* TaskFlags.RENDER_BLOCKING */;
9214
- const task = new Task(4 /* TaskFlags.DIRTY */ | 2 /* TaskFlags.TASK */ | taskFlags, i, iCtx.$hostElement$, qrl, undefined, null);
9301
+ const task = new Task(4 /* TaskFlags.DIRTY */ | 2 /* TaskFlags.TASK */ | taskFlags, i, iCtx.$hostElement$, qrl, null);
9215
9302
  // In V2 we add the task to the sequential scope. We need to do this
9216
9303
  // in order to be able to retrieve it later when the parent element is
9217
9304
  // deleted and we need to be able to release the task subscriptions.
@@ -9270,17 +9357,15 @@ class Task extends BackRef {
9270
9357
  $index$;
9271
9358
  $el$;
9272
9359
  $qrl$;
9273
- $state$;
9274
9360
  $destroy$;
9275
9361
  $destroyPromise$;
9276
9362
  $taskPromise$ = null;
9277
- constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
9363
+ constructor($flags$, $index$, $el$, $qrl$, $destroy$) {
9278
9364
  super();
9279
9365
  this.$flags$ = $flags$;
9280
9366
  this.$index$ = $index$;
9281
9367
  this.$el$ = $el$;
9282
9368
  this.$qrl$ = $qrl$;
9283
- this.$state$ = $state$;
9284
9369
  this.$destroy$ = $destroy$;
9285
9370
  }
9286
9371
  }
@@ -9296,12 +9381,14 @@ const isTask = (value) => {
9296
9381
  */
9297
9382
  function scheduleTask(_event, element) {
9298
9383
  const container = getDomContainer(element);
9299
- if (typeof this === 'string') {
9300
- setCaptures(deserializeCaptures(container, this));
9301
- }
9302
- const task = _captures[0];
9303
- task.$flags$ |= 4 /* TaskFlags.DIRTY */;
9304
- markVNodeDirty(container, task.$el$, 1 /* ChoreBits.TASKS */);
9384
+ return whenContainerDataReady(container, () => {
9385
+ if (typeof this === 'string') {
9386
+ setCaptures(deserializeCaptureDeltas(container, this));
9387
+ }
9388
+ const task = _captures[0];
9389
+ task.$flags$ |= 4 /* TaskFlags.DIRTY */;
9390
+ markVNodeDirty(container, task.$el$, 1 /* ChoreBits.TASKS */);
9391
+ });
9305
9392
  }
9306
9393
 
9307
9394
  const throwIfQRLNotResolved = (qrl) => {
@@ -9413,7 +9500,7 @@ const isSerializerObj = (obj) => {
9413
9500
  return isObject(obj) && typeof obj[SerializerSymbol] === 'function';
9414
9501
  };
9415
9502
  const getComputedSignalFlags = (serializationStrategy) => {
9416
- let flags = 1 /* SignalFlags.INVALID */;
9503
+ let flags = 1 /* ComputedSignalFlags.INVALID */;
9417
9504
  switch (serializationStrategy) {
9418
9505
  // TODO: implement this in the future
9419
9506
  // case 'auto':
@@ -9428,6 +9515,18 @@ const getComputedSignalFlags = (serializationStrategy) => {
9428
9515
  }
9429
9516
  return flags;
9430
9517
  };
9518
+ /**
9519
+ * Mark this signal as owned outside of the component that read it.
9520
+ *
9521
+ * Externally owned signals are preserved when found in a component's sequential scope during
9522
+ * component cleanup.
9523
+ *
9524
+ * @internal
9525
+ */
9526
+ const _markSignalAsExternallyOwned = (signal) => {
9527
+ signal.$flags$ |=
9528
+ 4 /* ComputedSignalFlags.PRESERVE_ON_SEQ_CLEANUP */;
9529
+ };
9431
9530
 
9432
9531
  const stringifyPath = [];
9433
9532
  function qwikDebugToString(value) {
@@ -9787,6 +9886,7 @@ function getEffects(target, prop, storeEffects) {
9787
9886
 
9788
9887
  /** Used to represent an undefined value that must be serialized */
9789
9888
  const explicitUndefined = Symbol('undefined');
9889
+ const EMPTY_OBJECT_PAYLOAD = 0;
9790
9890
  // Used for allocate, make sure they are in sync with Constants
9791
9891
  const _constants = [
9792
9892
  undefined,
@@ -9807,6 +9907,10 @@ const _constants = [
9807
9907
  Number.MAX_SAFE_INTEGER,
9808
9908
  Number.MAX_SAFE_INTEGER - 1,
9809
9909
  Number.MIN_SAFE_INTEGER,
9910
+ ':',
9911
+ '.',
9912
+ 'id',
9913
+ 'ref',
9810
9914
  ];
9811
9915
  // Used for dumpState, make sure they are in sync with Constants
9812
9916
  const _constantNames = [
@@ -9828,6 +9932,10 @@ const _constantNames = [
9828
9932
  'MAX_SAFE_INTEGER',
9829
9933
  'MAX_SAFE_INTEGER-1',
9830
9934
  'MIN_SAFE_INTEGER',
9935
+ "':'",
9936
+ "'.'",
9937
+ "'id'",
9938
+ "'ref'",
9831
9939
  ];
9832
9940
  // Used for dumpState, make sure they are in sync with TypeIds
9833
9941
  const _typeIdNames = [
@@ -9873,6 +9981,9 @@ const _typeIdNames = [
9873
9981
  'SubscriptionData',
9874
9982
  'EffectSubscription',
9875
9983
  'SubscriptionPatch',
9984
+ 'SubscriptionDataConstTrue',
9985
+ 'SubscriptionDataConstFalse',
9986
+ 'EffectSubscriptionNoData',
9876
9987
  ];
9877
9988
 
9878
9989
  function qrlToString(serializationContext, qrl, raw) {
@@ -9919,17 +10030,25 @@ function qrlToString(serializationContext, qrl, raw) {
9919
10030
  symbol = String(serializationContext.$addSyncFn$(null, 0, fn));
9920
10031
  }
9921
10032
  const captures = qrl.getCaptured();
9922
- let captureIds = null;
10033
+ let captureDeltas = null;
9923
10034
  if (captures && captures.length > 0) {
9924
10035
  // We refer by id so every capture needs to be a root
9925
- captureIds = captures.map((ref) => `${serializationContext.$addRoot$(ref)}`).join(' ');
10036
+ let previous = 0;
10037
+ let output = '';
10038
+ for (let i = 0; i < captures.length; i++) {
10039
+ const captureId = serializationContext.$addRoot$(captures[i]);
10040
+ const delta = captureId - previous;
10041
+ previous = captureId;
10042
+ output += (i === 0 ? '' : ' ') + delta;
10043
+ }
10044
+ captureDeltas = output;
9926
10045
  }
9927
10046
  if (raw) {
9928
- return [chunk, symbol, captureIds];
10047
+ return [chunk, symbol, captureDeltas];
9929
10048
  }
9930
10049
  let qrlStringInline = `${chunk}#${symbol}`;
9931
- if (captureIds) {
9932
- qrlStringInline += `#${captureIds}`;
10050
+ if (captureDeltas) {
10051
+ qrlStringInline += `#${captureDeltas}`;
9933
10052
  }
9934
10053
  return qrlStringInline;
9935
10054
  }
@@ -9941,12 +10060,23 @@ function qrlToChunks(serializationContext, qrl) {
9941
10060
  return prefix;
9942
10061
  }
9943
10062
  const chunks = [prefix, '#'];
9944
- const captureIds = captures.split(' ');
9945
- for (let i = 0; i < captureIds.length; i++) {
9946
- if (i > 0) {
9947
- chunks.push(' ');
10063
+ let previousCaptureId = 0;
10064
+ let start = 0;
10065
+ for (let i = 0; i <= captures.length; i++) {
10066
+ if (i === captures.length || captures[i] === ' ') {
10067
+ if (i > start) {
10068
+ const captureId = previousCaptureId + Number(captures.slice(start, i));
10069
+ if (start > 0) {
10070
+ chunks.push(' ');
10071
+ chunks.push({ id: captureId, base: previousCaptureId });
10072
+ }
10073
+ else {
10074
+ chunks.push(captureId);
10075
+ }
10076
+ previousCaptureId = captureId;
10077
+ }
10078
+ start = i + 1;
9948
10079
  }
9949
- chunks.push(Number(captureIds[i]));
9950
10080
  }
9951
10081
  return chunks;
9952
10082
  }
@@ -9962,10 +10092,14 @@ function createQRLWithBackChannel(chunk, symbol, captures, container) {
9962
10092
  }
9963
10093
  return createQRL(chunk, symbol, null, qrlImporter, captures, container);
9964
10094
  }
9965
- /** Parses "chunk#hash#...rootRef" */
10095
+ /** Parses "chunk#hash#...captureDelta" */
9966
10096
  function parseQRL(qrl, container) {
9967
- const [chunk, symbol, captures] = qrl.split('#');
9968
- return createQRLWithBackChannel(chunk, symbol, captures || null, container);
10097
+ const firstHash = qrl.indexOf('#');
10098
+ const secondHash = qrl.indexOf('#', firstHash + 1);
10099
+ const chunk = qrl.slice(0, firstHash);
10100
+ const symbol = secondHash === -1 ? qrl.slice(firstHash + 1) : qrl.slice(firstHash + 1, secondHash);
10101
+ const captures = secondHash !== -1 && secondHash + 1 < qrl.length ? qrl.slice(secondHash + 1) : null;
10102
+ return createQRLWithBackChannel(chunk, symbol, captures, container);
9969
10103
  }
9970
10104
  const QRL_RUNTIME_CHUNK = 'mock-chunk';
9971
10105
 
@@ -10117,7 +10251,37 @@ class Serializer {
10117
10251
  return key;
10118
10252
  }
10119
10253
  }
10120
- return Number(key);
10254
+ return Number(key);
10255
+ }
10256
+ rebaseQrlCaptureDeltas$(baseRootId, captureDeltas) {
10257
+ let previousCaptureId = 0;
10258
+ let previousOutputBase = baseRootId;
10259
+ const deltaList = captureDeltas.split(' ');
10260
+ for (let i = 0; i < deltaList.length; i++) {
10261
+ const captureId = previousCaptureId + Number(deltaList[i]);
10262
+ deltaList[i] = String(captureId - previousOutputBase);
10263
+ previousCaptureId = captureId;
10264
+ previousOutputBase = captureId;
10265
+ }
10266
+ return deltaList.join(' ');
10267
+ }
10268
+ outputShortStringConstant$(value) {
10269
+ switch (value) {
10270
+ case ':':
10271
+ this.output(3 /* TypeIds.Constant */, 18 /* Constants.Colon */);
10272
+ return true;
10273
+ case '.':
10274
+ this.output(3 /* TypeIds.Constant */, 19 /* Constants.Dot */);
10275
+ return true;
10276
+ case 'id':
10277
+ this.output(3 /* TypeIds.Constant */, 20 /* Constants.Id */);
10278
+ return true;
10279
+ case 'ref':
10280
+ this.output(3 /* TypeIds.Constant */, 21 /* Constants.Ref */);
10281
+ return true;
10282
+ default:
10283
+ return false;
10284
+ }
10121
10285
  }
10122
10286
  outputString(value) {
10123
10287
  const s = this.stringNeedsJsonEscape$(value) ? JSON.stringify(value) : QUOTE + value + QUOTE;
@@ -10136,6 +10300,9 @@ class Serializer {
10136
10300
  writeRootRefPath(path) {
10137
10301
  this.$writer$.writeRootRefPath(path);
10138
10302
  }
10303
+ writeRootRefDelta(id, base) {
10304
+ this.$writer$.writeRootRefDelta(id, base);
10305
+ }
10139
10306
  outputStringChunks(chunks) {
10140
10307
  this.$writer$.write(QUOTE);
10141
10308
  for (let i = 0; i < chunks.length; i++) {
@@ -10146,6 +10313,9 @@ class Serializer {
10146
10313
  else if (typeof chunk === 'number') {
10147
10314
  this.writeRootRef(chunk);
10148
10315
  }
10316
+ else if ('base' in chunk) {
10317
+ this.writeRootRefDelta(chunk.id, chunk.base);
10318
+ }
10149
10319
  else {
10150
10320
  this.writeRootRefPath(chunk.path);
10151
10321
  }
@@ -10275,6 +10445,9 @@ class Serializer {
10275
10445
  if (value.length === 0) {
10276
10446
  this.output(3 /* TypeIds.Constant */, 4 /* Constants.EmptyString */);
10277
10447
  }
10448
+ else if (this.outputShortStringConstant$(value)) {
10449
+ break;
10450
+ }
10278
10451
  else {
10279
10452
  // If the string is short, we output directly
10280
10453
  // Very short strings add overhead to tracking
@@ -10314,27 +10487,38 @@ class Serializer {
10314
10487
  }
10315
10488
  else if (isQrl(value)) {
10316
10489
  if (this.getSeenRefOrOutput(value, index)) {
10317
- const [chunk, symbol, captures] = qrlToString(this.$serializationContext$, value, true);
10490
+ const [chunk, symbol, captureDeltasFromZero] = qrlToString(this.$serializationContext$, value, true);
10318
10491
  let data;
10319
10492
  if (chunk !== '') {
10320
10493
  // not a sync QRL, replace all parts with string references
10321
- data = [
10322
- this.$serializationContext$.$addRoot$(chunk),
10323
- '#',
10324
- this.$serializationContext$.$addRoot$(symbol),
10325
- ];
10326
- if (captures) {
10327
- const captureIds = captures.split(' ');
10494
+ const chunkRootId = this.$serializationContext$.$addRoot$(chunk);
10495
+ const symbolRootId = this.$serializationContext$.$addRoot$(symbol);
10496
+ const captureDeltas = captureDeltasFromZero
10497
+ ? this.rebaseQrlCaptureDeltas$(symbolRootId, captureDeltasFromZero)
10498
+ : null;
10499
+ data = [chunkRootId, '#', { id: symbolRootId, base: chunkRootId }];
10500
+ if (captureDeltasFromZero) {
10501
+ let previousRootId = 0;
10502
+ let previousOutputBase = symbolRootId;
10503
+ let start = 0;
10328
10504
  data.push('#');
10329
- for (let i = 0; i < captureIds.length; i++) {
10330
- if (i > 0) {
10331
- data.push(' ');
10505
+ for (let i = 0; i <= captureDeltasFromZero.length; i++) {
10506
+ if (i === captureDeltasFromZero.length || captureDeltasFromZero[i] === ' ') {
10507
+ if (i > start) {
10508
+ const captureId = previousRootId + Number(captureDeltasFromZero.slice(start, i));
10509
+ if (start > 0) {
10510
+ data.push(' ');
10511
+ }
10512
+ data.push({ id: captureId, base: previousOutputBase });
10513
+ previousRootId = captureId;
10514
+ previousOutputBase = captureId;
10515
+ }
10516
+ start = i + 1;
10332
10517
  }
10333
- data.push(Number(captureIds[i]));
10334
10518
  }
10335
10519
  }
10336
10520
  // Since we map QRLs to strings, we need to keep track of this secondary mapping
10337
- const qrlKey = data.join('');
10521
+ const qrlKey = `${chunkRootId}#${symbolRootId - chunkRootId}${captureDeltas ? '#' + captureDeltas : ''}`;
10338
10522
  const existing = this.$qrlMap$.get(qrlKey);
10339
10523
  if (existing) {
10340
10524
  // We encountered the same QRL again, make it a root
@@ -10402,15 +10586,21 @@ class Serializer {
10402
10586
  ]);
10403
10587
  }
10404
10588
  else if (value instanceof SubscriptionData) {
10405
- // TODO make everything optional or use two types
10406
- this.output(39 /* TypeIds.SubscriptionData */, [
10407
- value.data.$scopedStyleIdPrefix$,
10408
- value.data.$isConst$,
10409
- ]);
10589
+ const { $scopedStyleIdPrefix$, $isConst$ } = value.data;
10590
+ if ($scopedStyleIdPrefix$ === null) {
10591
+ this.output($isConst$ ? 42 /* TypeIds.SubscriptionDataConstTrue */ : 43 /* TypeIds.SubscriptionDataConstFalse */, 0);
10592
+ }
10593
+ else {
10594
+ this.output(39 /* TypeIds.SubscriptionData */, [$scopedStyleIdPrefix$, $isConst$]);
10595
+ }
10410
10596
  }
10411
10597
  else if (value instanceof EffectSubscription) {
10412
- // TODO no data if [null, true]
10413
- this.output(40 /* TypeIds.EffectSubscription */, [value.consumer, value.property, value.data]);
10598
+ if (value.data === null) {
10599
+ this.output(44 /* TypeIds.EffectSubscriptionNoData */, [value.consumer, value.property]);
10600
+ }
10601
+ else {
10602
+ this.output(40 /* TypeIds.EffectSubscription */, [value.consumer, value.property, value.data]);
10603
+ }
10414
10604
  }
10415
10605
  else if (value instanceof SubscriptionPatch) {
10416
10606
  this.output(41 /* TypeIds.SubscriptionPatch */, [value.rootId, value.subscriptions]);
@@ -10464,7 +10654,7 @@ class Serializer {
10464
10654
  }
10465
10655
  }
10466
10656
  }
10467
- this.output(5 /* TypeIds.Object */, out.length ? out : 0);
10657
+ this.output(5 /* TypeIds.Object */, out.length ? out : EMPTY_OBJECT_PAYLOAD);
10468
10658
  }
10469
10659
  }
10470
10660
  else if (this.$serializationContext$.$isDomRef$(value)) {
@@ -10497,16 +10687,17 @@ class Serializer {
10497
10687
  let v = value.$untrackedValue$;
10498
10688
  const shouldAlwaysSerialize = value.$flags$ & 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
10499
10689
  const shouldNeverSerialize = value.$flags$ & 8 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
10500
- const isInvalid = value.$flags$ & 1 /* SignalFlags.INVALID */;
10690
+ const isInvalid = value.$flags$ & 1 /* ComputedSignalFlags.INVALID */;
10501
10691
  const isSkippable = fastSkipSerialize(value.$untrackedValue$);
10502
- const isAsync = value instanceof AsyncSignalImpl;
10503
- const expires = isAsync && value.$expires$ !== 0 ? value.$expires$ : undefined;
10692
+ // async-mode computeds carry the same engine state and must round-trip like AsyncSignals
10693
+ const isAsync = !!(value.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */);
10694
+ const isErrored = isAsync && !!value.$untrackedError$;
10504
10695
  const concurrency = isAsync && value.$concurrency$ !== 1 ? value.$concurrency$ : undefined;
10505
10696
  const timeout = isAsync && value.$timeoutMs$ !== 0 ? value.$timeoutMs$ : undefined;
10506
10697
  // Send the flags but remove the serialization bits and default to 0 when undefined
10507
10698
  const asyncFlags = (isAsync && value.$flags$ & -25 /* SerializationSignalFlags.SERIALIZATION_ALL_STRATEGIES */) ||
10508
10699
  undefined;
10509
- if (isInvalid || isSkippable) {
10700
+ if (isInvalid || isSkippable || isErrored) {
10510
10701
  v = NEEDS_COMPUTATION;
10511
10702
  }
10512
10703
  else if (shouldAlwaysSerialize) {
@@ -10521,10 +10712,7 @@ class Serializer {
10521
10712
  out.push(value.$loadingEffects$, value.$errorEffects$, value.$untrackedError$);
10522
10713
  out.push(asyncFlags || undefined);
10523
10714
  }
10524
- if (v !== NEEDS_COMPUTATION ||
10525
- expires !== undefined ||
10526
- concurrency !== undefined ||
10527
- timeout !== undefined) {
10715
+ if (v !== NEEDS_COMPUTATION || concurrency !== undefined || timeout !== undefined) {
10528
10716
  /**
10529
10717
  * If value is undefined, we need to keep it in the output. If we don't do that, later
10530
10718
  * during resuming, the value will be set to symbol(invalid) with flag invalid, and thats
@@ -10533,7 +10721,6 @@ class Serializer {
10533
10721
  out.push(v === undefined ? explicitUndefined : v);
10534
10722
  }
10535
10723
  if (isAsync) {
10536
- out.push(expires);
10537
10724
  out.push(concurrency);
10538
10725
  out.push(timeout);
10539
10726
  }
@@ -10554,30 +10741,6 @@ class Serializer {
10554
10741
  else if (value instanceof Date) {
10555
10742
  this.output(7 /* TypeIds.Date */, Number.isNaN(value.valueOf()) ? '' : value.valueOf());
10556
10743
  }
10557
- else if (this.$hasTemporal$ && value instanceof Temporal.Duration) {
10558
- this.output(15 /* TypeIds.TemporalDuration */, value.toJSON());
10559
- }
10560
- else if (this.$hasTemporal$ && value instanceof Temporal.Instant) {
10561
- this.output(16 /* TypeIds.TemporalInstant */, value.toJSON());
10562
- }
10563
- else if (this.$hasTemporal$ && value instanceof Temporal.PlainDate) {
10564
- this.output(17 /* TypeIds.TemporalPlainDate */, value.toJSON());
10565
- }
10566
- else if (this.$hasTemporal$ && value instanceof Temporal.PlainDateTime) {
10567
- this.output(18 /* TypeIds.TemporalPlainDateTime */, value.toJSON());
10568
- }
10569
- else if (this.$hasTemporal$ && value instanceof Temporal.PlainMonthDay) {
10570
- this.output(19 /* TypeIds.TemporalPlainMonthDay */, value.toJSON());
10571
- }
10572
- else if (this.$hasTemporal$ && value instanceof Temporal.PlainTime) {
10573
- this.output(20 /* TypeIds.TemporalPlainTime */, value.toJSON());
10574
- }
10575
- else if (this.$hasTemporal$ && value instanceof Temporal.PlainYearMonth) {
10576
- this.output(21 /* TypeIds.TemporalPlainYearMonth */, value.toJSON());
10577
- }
10578
- else if (this.$hasTemporal$ && value instanceof Temporal.ZonedDateTime) {
10579
- this.output(22 /* TypeIds.TemporalZonedDateTime */, value.toJSON());
10580
- }
10581
10744
  else if (value instanceof RegExp) {
10582
10745
  this.output(8 /* TypeIds.Regex */, value.toString());
10583
10746
  }
@@ -10650,7 +10813,7 @@ class Serializer {
10650
10813
  this.output(37 /* TypeIds.JSXNode */, out);
10651
10814
  }
10652
10815
  else if (value instanceof Task) {
10653
- const out = [value.$qrl$, value.$flags$, value.$index$, value.$el$, value.$state$];
10816
+ const out = [value.$qrl$, value.$flags$, value.$index$, value.$el$];
10654
10817
  while (out[out.length - 1] === undefined) {
10655
10818
  out.pop();
10656
10819
  }
@@ -10704,6 +10867,30 @@ class Serializer {
10704
10867
  this.output(2 /* TypeIds.ForwardRef */, this.getForwardRefId(forwardRefId));
10705
10868
  }
10706
10869
  }
10870
+ else if (this.$hasTemporal$ && value instanceof Temporal.Duration) {
10871
+ this.output(15 /* TypeIds.TemporalDuration */, value.toJSON());
10872
+ }
10873
+ else if (this.$hasTemporal$ && value instanceof Temporal.Instant) {
10874
+ this.output(16 /* TypeIds.TemporalInstant */, value.toJSON());
10875
+ }
10876
+ else if (this.$hasTemporal$ && value instanceof Temporal.PlainDate) {
10877
+ this.output(17 /* TypeIds.TemporalPlainDate */, value.toJSON());
10878
+ }
10879
+ else if (this.$hasTemporal$ && value instanceof Temporal.PlainDateTime) {
10880
+ this.output(18 /* TypeIds.TemporalPlainDateTime */, value.toJSON());
10881
+ }
10882
+ else if (this.$hasTemporal$ && value instanceof Temporal.PlainMonthDay) {
10883
+ this.output(19 /* TypeIds.TemporalPlainMonthDay */, value.toJSON());
10884
+ }
10885
+ else if (this.$hasTemporal$ && value instanceof Temporal.PlainTime) {
10886
+ this.output(20 /* TypeIds.TemporalPlainTime */, value.toJSON());
10887
+ }
10888
+ else if (this.$hasTemporal$ && value instanceof Temporal.PlainYearMonth) {
10889
+ this.output(21 /* TypeIds.TemporalPlainYearMonth */, value.toJSON());
10890
+ }
10891
+ else if (this.$hasTemporal$ && value instanceof Temporal.ZonedDateTime) {
10892
+ this.output(22 /* TypeIds.TemporalZonedDateTime */, value.toJSON());
10893
+ }
10707
10894
  else if (vnode_isVNode(value)) {
10708
10895
  this.output(3 /* TypeIds.Constant */, 0 /* Constants.Undefined */);
10709
10896
  }
@@ -10870,7 +11057,9 @@ const discoverValuesForVNodeData = (vnodeData, callback) => {
10870
11057
  if (attrValue == null ||
10871
11058
  typeof attrValue === 'string' ||
10872
11059
  (typeof attrValue === 'number' && key === ELEMENT_ID) ||
10873
- (key === ELEMENT_PROPS && isObjectEmpty(attrValue))) {
11060
+ (key === ELEMENT_PROPS &&
11061
+ (isObjectEmpty(attrValue) ||
11062
+ shouldSkipComponentProps(value, attrValue)))) {
10874
11063
  continue;
10875
11064
  }
10876
11065
  callback(attrValue);
@@ -10879,6 +11068,21 @@ const discoverValuesForVNodeData = (vnodeData, callback) => {
10879
11068
  }
10880
11069
  };
10881
11070
  const isSsrAttrs = (value) => typeof value === 'object' && value !== null && !isObjectEmpty(value);
11071
+ const shouldSkipComponentProps = (attrs, value, isDevMode = isDev) => {
11072
+ if (isDevMode || !Object.prototype.hasOwnProperty.call(attrs, OnRenderProp)) {
11073
+ return false;
11074
+ }
11075
+ const owner = value[_OWNER];
11076
+ return (isObjectEmpty(owner.varProps) &&
11077
+ !hasPropsEffects(value) &&
11078
+ !hasComponentBackRef(attrs[QBackRefs]));
11079
+ };
11080
+ const hasPropsEffects = (propsProxy) => {
11081
+ return !!propsProxy[_PROPS_HANDLER].$effects$?.size;
11082
+ };
11083
+ const hasComponentBackRef = (backRefs) => {
11084
+ return backRefs instanceof Map && backRefs.has(":" /* EffectProperty.COMPONENT */);
11085
+ };
10882
11086
  /**
10883
11087
  * When serializing the object we need check if it is URL, RegExp, Map, Set, etc. This is time
10884
11088
  * consuming. So if we could know that this is a basic object literal we could skip the check, and
@@ -11377,6 +11581,12 @@ const constantToName = (code) => {
11377
11581
  * @internal
11378
11582
  */
11379
11583
  function preprocessState(data, container, segmentId, startIndex = 0) {
11584
+ const iterator = preprocessStateIterator(data, container, startIndex);
11585
+ while (!iterator.next().done) {
11586
+ // Run synchronously for non-browser and non-container deserialization paths.
11587
+ }
11588
+ }
11589
+ function* preprocessStateIterator(data, container, startIndex = 0) {
11380
11590
  const isRootDeepRef = (type, value) => {
11381
11591
  return type === 1 /* TypeIds.RootRef */ && typeof value === 'string' && value.indexOf(' ') !== -1;
11382
11592
  };
@@ -11419,11 +11629,39 @@ function preprocessState(data, container, segmentId, startIndex = 0) {
11419
11629
  else if (isForwardRefsMap(data[i])) {
11420
11630
  container.$forwardRefs$ = data[i + 1];
11421
11631
  }
11632
+ yield;
11422
11633
  }
11423
11634
  }
11424
11635
 
11425
11636
  const resolvers = new WeakMap();
11426
- const pendingStoreTargets = new Map();
11637
+ const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
11638
+ const BASE64_REGEXP = /^[A-Za-z0-9+/]*$/;
11639
+ // Validate canonical unpadded base64 before its length controls allocation.
11640
+ const isCanonicalBase64 = (value) => {
11641
+ if (typeof value !== 'string' || !BASE64_REGEXP.test(value)) {
11642
+ return false;
11643
+ }
11644
+ const rest = value.length % 4;
11645
+ if (rest === 0) {
11646
+ return true;
11647
+ }
11648
+ if (rest === 1) {
11649
+ return false;
11650
+ }
11651
+ const lastValue = BASE64_ALPHABET.indexOf(value[value.length - 1]);
11652
+ return (lastValue & (rest === 2 ? 15 : 3)) === 0;
11653
+ };
11654
+ const beginDeserialization = (container) => {
11655
+ const ownsPendingStoreTargets = container.$pendingStoreTargets$ === undefined;
11656
+ container.$pendingStoreTargets$ ??= new Map();
11657
+ return ownsPendingStoreTargets;
11658
+ };
11659
+ const endDeserialization = (container, ownsPendingStoreTargets) => {
11660
+ if (ownsPendingStoreTargets) {
11661
+ container.$pendingStoreTargets$.clear();
11662
+ container.$pendingStoreTargets$ = undefined;
11663
+ }
11664
+ };
11427
11665
  const allocate = (container, typeId, value) => {
11428
11666
  switch (typeId) {
11429
11667
  case 0 /* TypeIds.Plain */:
@@ -11449,10 +11687,17 @@ const allocate = (container, typeId, value) => {
11449
11687
  case 9 /* TypeIds.QRL */: {
11450
11688
  let qrl;
11451
11689
  if (typeof value === 'string') {
11452
- const [chunkId, symbolId, captureIds] = value.split('#');
11453
- const chunk = container.$getObjectById$(chunkId);
11454
- const symbol = container.$getObjectById$(symbolId);
11455
- qrl = createQRLWithBackChannel(chunk, symbol, captureIds || null, container);
11690
+ const serializedQrl = value;
11691
+ const firstHash = serializedQrl.indexOf('#');
11692
+ const secondHash = serializedQrl.indexOf('#', firstHash + 1);
11693
+ const chunkRootId = Number(serializedQrl.slice(0, firstHash));
11694
+ const symbolDelta = secondHash === -1
11695
+ ? Number(serializedQrl.slice(firstHash + 1))
11696
+ : Number(serializedQrl.slice(firstHash + 1, secondHash));
11697
+ const symbolRootId = chunkRootId + symbolDelta;
11698
+ const chunk = container.$getObjectById$(chunkRootId);
11699
+ const symbol = container.$getObjectById$(symbolRootId);
11700
+ qrl = createQRLWithBackChannel(chunk, symbol, secondHash !== -1 ? serializedQrl : null, container);
11456
11701
  }
11457
11702
  else {
11458
11703
  // Sync qrl
@@ -11461,7 +11706,7 @@ const allocate = (container, typeId, value) => {
11461
11706
  return qrl;
11462
11707
  }
11463
11708
  case 28 /* TypeIds.Task */:
11464
- return new Task(-1, -1, null, null, null, null);
11709
+ return new Task(-1, -1, null, null, null);
11465
11710
  case 6 /* TypeIds.URL */:
11466
11711
  return new URL(value);
11467
11712
  case 7 /* TypeIds.Date */:
@@ -11509,7 +11754,7 @@ const allocate = (container, typeId, value) => {
11509
11754
  const storeValue = allocate(container, t, v);
11510
11755
  const store = getOrCreateStore(storeValue, 0 /* StoreFlags.NONE */, container);
11511
11756
  if (needsInflation(t)) {
11512
- pendingStoreTargets.set(storeValue, { t, v });
11757
+ container.$pendingStoreTargets$.set(storeValue, { t, v });
11513
11758
  }
11514
11759
  // We must store the reference so it doesn't get deserialized again in inflate()
11515
11760
  data[0] = 0 /* TypeIds.Plain */;
@@ -11539,12 +11784,16 @@ const allocate = (container, typeId, value) => {
11539
11784
  // Don't leave unhandled promise rejections
11540
11785
  promise.catch(() => { });
11541
11786
  return promise;
11542
- case 27 /* TypeIds.Uint8Array */:
11787
+ case 27 /* TypeIds.Uint8Array */: {
11788
+ if (!isCanonicalBase64(value)) {
11789
+ throw qError(38 /* QError.invalidUint8ArrayPayload */);
11790
+ }
11543
11791
  const encodedLength = value.length;
11544
- const blocks = encodedLength >>> 2;
11545
- const rest = encodedLength & 3;
11792
+ const blocks = Math.floor(encodedLength / 4);
11793
+ const rest = encodedLength % 4;
11546
11794
  const decodedLength = blocks * 3 + (rest ? rest - 1 : 0);
11547
11795
  return new Uint8Array(decodedLength);
11796
+ }
11548
11797
  case 38 /* TypeIds.PropsProxy */:
11549
11798
  return createPropsProxy(null);
11550
11799
  case 10 /* TypeIds.VNode */:
@@ -11585,7 +11834,12 @@ const allocate = (container, typeId, value) => {
11585
11834
  }
11586
11835
  case 39 /* TypeIds.SubscriptionData */:
11587
11836
  return new SubscriptionData({});
11837
+ case 42 /* TypeIds.SubscriptionDataConstTrue */:
11838
+ return new SubscriptionData({ $scopedStyleIdPrefix$: null, $isConst$: true });
11839
+ case 43 /* TypeIds.SubscriptionDataConstFalse */:
11840
+ return new SubscriptionData({ $scopedStyleIdPrefix$: null, $isConst$: false });
11588
11841
  case 40 /* TypeIds.EffectSubscription */:
11842
+ case 44 /* TypeIds.EffectSubscriptionNoData */:
11589
11843
  return new EffectSubscription(null, null, null, null);
11590
11844
  case 41 /* TypeIds.SubscriptionPatch */:
11591
11845
  return new SubscriptionPatch();
@@ -11794,6 +12048,16 @@ const applyQwikComponentBody = (ssr, jsx, component) => {
11794
12048
 
11795
12049
  class MaybeAsyncSignal {
11796
12050
  }
12051
+ const markPromiseHandled = (ssr, promise, host = null) => {
12052
+ promise.catch((reason) => {
12053
+ try {
12054
+ ssr.handleError(reason, host);
12055
+ }
12056
+ catch {
12057
+ // Original promise remains awaited later.
12058
+ }
12059
+ });
12060
+ };
11797
12061
  function setParentOptions(mutable, styleScoped, parentComponentFrame) {
11798
12062
  return () => {
11799
12063
  mutable.currentStyleScoped = styleScoped;
@@ -11804,6 +12068,11 @@ function setParentOptions(mutable, styleScoped, parentComponentFrame) {
11804
12068
  async function _walkJSX(ssr, value, options) {
11805
12069
  const stack = [value];
11806
12070
  const enqueue = (value) => stack.push(value);
12071
+ const enqueuePromise = (promise) => {
12072
+ markPromiseHandled(ssr, promise);
12073
+ stack.push(promise);
12074
+ stack.push(Promise);
12075
+ };
11807
12076
  const drain = async () => {
11808
12077
  while (stack.length) {
11809
12078
  try {
@@ -11826,7 +12095,7 @@ async function _walkJSX(ssr, value, options) {
11826
12095
  }
11827
12096
  continue;
11828
12097
  }
11829
- processJSXNode(ssr, enqueue, value, options);
12098
+ processJSXNode(ssr, enqueue, enqueuePromise, value, options);
11830
12099
  }
11831
12100
  finally {
11832
12101
  const pendingFlush = ssr.streamHandler.waitForPendingFlush();
@@ -11838,7 +12107,7 @@ async function _walkJSX(ssr, value, options) {
11838
12107
  };
11839
12108
  await drain();
11840
12109
  }
11841
- function processJSXNode(ssr, enqueue, value, options) {
12110
+ function processJSXNode(ssr, enqueue, enqueuePromise, value, options) {
11842
12111
  // console.log('processJSXNode', value);
11843
12112
  if (value == null) {
11844
12113
  ssr.textNode('');
@@ -11859,7 +12128,6 @@ function processJSXNode(ssr, enqueue, value, options) {
11859
12128
  }
11860
12129
  }
11861
12130
  else if (isSignal(value)) {
11862
- maybeAddPollingAsyncSignalToEagerResume(ssr.serializationCtx, value);
11863
12131
  ssr.openFragment(isDev ? { [DEBUG_TYPE]: "S" /* VirtualType.WrappedSignal */ } : EMPTY_OBJ);
11864
12132
  const signalNode = ssr.getOrCreateLastNode();
11865
12133
  const unwrappedSignal = value instanceof WrappedSignalImpl ? value.$unwrapIfSignal$() : value;
@@ -11870,8 +12138,7 @@ function processJSXNode(ssr, enqueue, value, options) {
11870
12138
  else if (isPromise(value)) {
11871
12139
  ssr.openFragment(isDev ? { [DEBUG_TYPE]: "A" /* VirtualType.Awaited */ } : EMPTY_OBJ);
11872
12140
  enqueue(ssr.closeFragment);
11873
- enqueue(value);
11874
- enqueue(Promise);
12141
+ enqueuePromise(value);
11875
12142
  enqueue(() => ssr.streamHandler.flush());
11876
12143
  }
11877
12144
  else if (isAsyncGenerator(value)) {
@@ -11942,11 +12209,9 @@ function processJSXNode(ssr, enqueue, value, options) {
11942
12209
  const componentFrame = options.parentComponentFrame;
11943
12210
  if (componentFrame) {
11944
12211
  const compId = componentFrame.componentNode.id || '';
11945
- const projectionAttrs = isDev ? { [DEBUG_TYPE]: "P" /* VirtualType.Projection */ } : {};
11946
- const cursorBoundary = directGetPropsProxyProp(jsx, QCursorBoundary);
11947
- if (cursorBoundary) {
11948
- projectionAttrs[QCursorBoundary] = cursorBoundary;
11949
- }
12212
+ const projectionAttrs = isDev
12213
+ ? { [DEBUG_TYPE]: "P" /* VirtualType.Projection */ }
12214
+ : {};
11950
12215
  projectionAttrs[QSlotParent] = compId;
11951
12216
  ssr.openProjection(projectionAttrs);
11952
12217
  const host = componentFrame.componentNode;
@@ -11994,8 +12259,12 @@ function processJSXNode(ssr, enqueue, value, options) {
11994
12259
  else {
11995
12260
  value = generator;
11996
12261
  }
11997
- enqueue(value);
11998
- isPromise(value) && enqueue(Promise);
12262
+ if (isPromise(value)) {
12263
+ enqueuePromise(value);
12264
+ }
12265
+ else {
12266
+ enqueue(value);
12267
+ }
11999
12268
  }
12000
12269
  else if (type === SSRRaw) {
12001
12270
  ssr.htmlNode(directGetPropsProxyProp(jsx, 'data'));
@@ -12019,8 +12288,10 @@ function processJSXNode(ssr, enqueue, value, options) {
12019
12288
  enqueue(setParentOptions(options, options.currentStyleScoped, options.parentComponentFrame));
12020
12289
  enqueue(() => ssr.closeComponent());
12021
12290
  if (isPromise(jsxOutput)) {
12291
+ markPromiseHandled(ssr, jsxOutput, host);
12022
12292
  // Defer reading QScopedStyle until after the promise resolves
12023
12293
  enqueue(async () => {
12294
+ await ssr.streamHandler.flush();
12024
12295
  const resolvedOutput = await jsxOutput;
12025
12296
  const compStyleComponentId = addComponentStylePrefix(host.getProp(QScopedStyle));
12026
12297
  enqueue(resolvedOutput);
@@ -12042,27 +12313,17 @@ function processJSXNode(ssr, enqueue, value, options) {
12042
12313
  enqueue(ssr.closeFragment);
12043
12314
  const component = ssr.getParentComponentFrame();
12044
12315
  const jsxOutput = applyInlineComponent(ssr, component && component.componentNode, type, jsx);
12045
- enqueue(jsxOutput);
12046
- isPromise(jsxOutput) && enqueue(Promise);
12316
+ if (isPromise(jsxOutput)) {
12317
+ enqueuePromise(jsxOutput);
12318
+ }
12319
+ else {
12320
+ enqueue(jsxOutput);
12321
+ }
12047
12322
  }
12048
12323
  }
12049
12324
  }
12050
12325
  }
12051
12326
  }
12052
- function maybeAddPollingAsyncSignalToEagerResume(serializationCtx, signal) {
12053
- // Unwrap if it's a WrappedSignalImpl
12054
- const unwrappedSignal = signal instanceof WrappedSignalImpl ? signal.$unwrapIfSignal$() : signal;
12055
- if (unwrappedSignal instanceof AsyncSignalImpl) {
12056
- const expires = unwrappedSignal.$expires$;
12057
- // Don't check for $effects$ here - effects are added later during tracking.
12058
- // The AsyncSignal's polling mechanism will check for effects before scheduling.
12059
- // Only eager-resume for polling signals, not stale-only ones.
12060
- if (expires && !(unwrappedSignal.$flags$ & 256 /* AsyncSignalFlags.NO_POLL */)) {
12061
- serializationCtx.$addRoot$(unwrappedSignal);
12062
- serializationCtx.$eagerResume$.add(unwrappedSignal);
12063
- }
12064
- }
12065
- }
12066
12327
  function appendQwikInspectorAttribute(jsx, qwikInspectorAttrValue) {
12067
12328
  if (qwikInspectorAttrValue && (!jsx.constProps || !(qwikInspectorAttr in jsx.constProps))) {
12068
12329
  (jsx.constProps ||= {})[qwikInspectorAttr] = qwikInspectorAttrValue;
@@ -12235,7 +12496,8 @@ function setEvent(serializationCtx, key, rawValue, hasMovedCaptures) {
12235
12496
  *
12236
12497
  * For internal qrls (starting with `_`) we assume that they do the right thing.
12237
12498
  */
12238
- if (!qrl.$symbol$.startsWith('_') && (qrl.$captures$?.length || hasMovedCaptures)) {
12499
+ if (!qrl.$symbol$.startsWith('_') &&
12500
+ (Array.isArray(qrl.$captures$) || (hasMovedCaptures && qrl.$hasMovedCaptures$))) {
12239
12501
  qrl = createQRL(null, '_run', _run, null, [qrl]);
12240
12502
  }
12241
12503
  return qrlToChunks(serializationCtx, qrl);
@@ -12449,22 +12711,24 @@ const useOnEventsSequentialScope = () => {
12449
12711
  const _hmr = function (event, element) {
12450
12712
  // Deserialize captures from `this` when called via qwikloader/attribute dispatch
12451
12713
  const container = getDomContainer(element);
12452
- if (typeof this === 'string') {
12453
- setCaptures(deserializeCaptures(container, this));
12454
- }
12455
- const devPath = _captures?.[1];
12456
- const hmrPath = devPath ?? element.getAttribute('data-qwik-inspector');
12457
- if (!hmrPath || !event.detail.files.some((file) => hmrPath.startsWith(file))) {
12458
- return;
12459
- }
12460
- const host = _captures?.[0];
12461
- if (!host || host.flags & 32 /* VNodeFlags.Deleted */) {
12462
- return;
12463
- }
12464
- markVNodeDirty(container, host, 4 /* ChoreBits.COMPONENT */);
12465
- // Mark HMR as handled
12466
- const doc = element.ownerDocument;
12467
- doc.__hmrDone = doc.__hmrT;
12714
+ return whenContainerDataReady(container, () => {
12715
+ if (typeof this === 'string') {
12716
+ setCaptures(deserializeCaptureDeltas(container, this));
12717
+ }
12718
+ const devPath = _captures?.[1];
12719
+ const hmrPath = devPath ?? element.getAttribute('data-qwik-inspector');
12720
+ if (!hmrPath || !event.detail.files.some((file) => hmrPath.startsWith(file))) {
12721
+ return;
12722
+ }
12723
+ const host = _captures?.[0];
12724
+ if (!host || host.flags & 32 /* VNodeFlags.Deleted */) {
12725
+ return;
12726
+ }
12727
+ markVNodeDirty(container, host, 4 /* ChoreBits.COMPONENT */);
12728
+ // Mark HMR as handled
12729
+ const doc = element.ownerDocument;
12730
+ doc.__hmrDone = doc.__hmrT;
12731
+ });
12468
12732
  };
12469
12733
  let hmrQrl;
12470
12734
  /**
@@ -12569,22 +12833,93 @@ const isSafeObjectKV = (key, value) => {
12569
12833
  (typeof value !== 'function' || !dangerousObjectKeys.has(key)));
12570
12834
  };
12571
12835
  const inflate = (container, target, typeId, data) => {
12836
+ const ownsPendingStoreTargets = beginDeserialization(container);
12837
+ try {
12838
+ const iterator = inflateIterator(container, target, typeId, data);
12839
+ while (!iterator.next().done) {
12840
+ // Run synchronously for lazy deserialization paths.
12841
+ }
12842
+ }
12843
+ finally {
12844
+ endDeserialization(container, ownsPendingStoreTargets);
12845
+ }
12846
+ };
12847
+ function* eagerDeserializeArrayIterator(container, data, output = Array(data.length / 2)) {
12848
+ for (let i = 0; i < data.length; i += 2) {
12849
+ output[i / 2] = yield* deserializeDataIterator(container, data[i], data[i + 1]);
12850
+ yield;
12851
+ }
12852
+ return output;
12853
+ }
12854
+ function* eagerDeserializeStateIterator(container, data, output = Array(data.length / 2)) {
12855
+ const ownsPendingStoreTargets = beginDeserialization(container);
12856
+ const length = data.length / 2;
12857
+ const allocated = new Uint8Array(length);
12858
+ const previousGetObjectById = container.$getObjectById$;
12859
+ const allocateRoot = (index) => {
12860
+ if (!allocated[index]) {
12861
+ allocated[index] = 1;
12862
+ const typeIndex = index * 2;
12863
+ const typeId = data[typeIndex];
12864
+ const value = data[typeIndex + 1];
12865
+ output[index] = typeId === 0 /* TypeIds.Plain */ ? value : allocate(container, typeId, value);
12866
+ }
12867
+ return output[index];
12868
+ };
12869
+ container.$getObjectById$ = (id) => allocateRoot(typeof id === 'string' ? parseInt(id, 10) : id);
12870
+ try {
12871
+ for (let i = 0; i < length; i++) {
12872
+ allocateRoot(i);
12873
+ yield;
12874
+ }
12875
+ for (let i = 0; i < length; i++) {
12876
+ const typeIndex = i * 2;
12877
+ const typeId = data[typeIndex];
12878
+ const value = data[typeIndex + 1];
12879
+ const propValue = output[i];
12880
+ data[typeIndex] = 0 /* TypeIds.Plain */;
12881
+ data[typeIndex + 1] = propValue;
12882
+ if (needsInflation(typeId)) {
12883
+ yield* inflateIterator(container, propValue, typeId, value);
12884
+ }
12885
+ yield;
12886
+ }
12887
+ }
12888
+ finally {
12889
+ container.$getObjectById$ = previousGetObjectById;
12890
+ endDeserialization(container, ownsPendingStoreTargets);
12891
+ }
12892
+ return output;
12893
+ }
12894
+ function* deserializeDataIterator(container, typeId, value) {
12895
+ if (typeId === 0 /* TypeIds.Plain */) {
12896
+ return value;
12897
+ }
12898
+ const propValue = allocate(container, typeId, value);
12899
+ if (needsInflation(typeId)) {
12900
+ yield* inflateIterator(container, propValue, typeId, value);
12901
+ }
12902
+ return propValue;
12903
+ }
12904
+ function* inflateIterator(container, target, typeId, data) {
12572
12905
  if (typeId === 0 /* TypeIds.Plain */) {
12573
12906
  // Already processed
12574
12907
  return;
12575
12908
  }
12909
+ if (typeId === 5 /* TypeIds.Object */ && data !== EMPTY_OBJECT_PAYLOAD && !Array.isArray(data)) {
12910
+ throw new Error('Invalid Object payload');
12911
+ }
12576
12912
  // Restore the complex data, special case for Array
12577
12913
  if (typeId !== 4 /* TypeIds.Array */ && Array.isArray(data)) {
12578
- data = _eagerDeserializeArray(container, data);
12914
+ data = yield* eagerDeserializeArrayIterator(container, data);
12579
12915
  }
12580
12916
  switch (typeId) {
12581
12917
  case 4 /* TypeIds.Array */:
12582
12918
  // Arrays are special, we need to fill the array in place
12583
- _eagerDeserializeArray(container, data, target);
12919
+ yield* eagerDeserializeArrayIterator(container, data, target);
12584
12920
  break;
12585
12921
  case 5 /* TypeIds.Object */:
12586
- if (data === 0) {
12587
- // Special case, was an empty object
12922
+ if (data === EMPTY_OBJECT_PAYLOAD) {
12588
12923
  break;
12589
12924
  }
12590
12925
  for (let i = 0; i < data.length; i += 2) {
@@ -12596,37 +12931,65 @@ const inflate = (container, target, typeId, data) => {
12596
12931
  target[key] = value;
12597
12932
  }
12598
12933
  break;
12599
- case 28 /* TypeIds.Task */:
12934
+ case 25 /* TypeIds.Set */: {
12935
+ const set = target;
12936
+ const d = data;
12937
+ for (let i = 0; i < d.length; i++) {
12938
+ set.add(d[i]);
12939
+ yield;
12940
+ }
12941
+ break;
12942
+ }
12943
+ case 26 /* TypeIds.Map */: {
12944
+ const map = target;
12945
+ const d = data;
12946
+ for (let i = 0; i < d.length; i++) {
12947
+ map.set(d[i++], d[i]);
12948
+ yield;
12949
+ }
12950
+ break;
12951
+ }
12952
+ case 24 /* TypeIds.Promise */: {
12953
+ const promise = target;
12954
+ const [resolved, result] = data;
12955
+ const [resolve, reject] = resolvers.get(promise);
12956
+ if (resolved) {
12957
+ // Native Promises cannot fulfill with another Promise.
12958
+ if (resolvers.has(result)) {
12959
+ throw qError(37 /* QError.invalidPromiseDependency */);
12960
+ }
12961
+ resolve(result);
12962
+ }
12963
+ else {
12964
+ reject(result);
12965
+ }
12966
+ break;
12967
+ }
12968
+ case 23 /* TypeIds.Error */: {
12969
+ const d = data;
12970
+ target.message = d[0];
12971
+ for (let i = 1; i < d.length; i += 2) {
12972
+ const key = d[i];
12973
+ const value = d[i + 1];
12974
+ if (isSafeObjectKV(key, value)) {
12975
+ target[key] = value;
12976
+ }
12977
+ yield;
12978
+ }
12979
+ break;
12980
+ }
12981
+ case 28 /* TypeIds.Task */: {
12600
12982
  const task = target;
12601
12983
  const v = data;
12602
12984
  task.$qrl$ = v[0];
12603
12985
  task.$flags$ = v[1];
12604
12986
  task.$index$ = v[2];
12605
12987
  task.$el$ = v[3];
12606
- task.$state$ = v[4];
12607
12988
  break;
12989
+ }
12608
12990
  case 29 /* TypeIds.Component */:
12609
12991
  target[SERIALIZABLE_STATE][0] = data[0];
12610
12992
  break;
12611
- case 35 /* TypeIds.Store */: {
12612
- // Inflate the store target
12613
- const store = unwrapStore(target);
12614
- const storeTarget = pendingStoreTargets.get(store);
12615
- if (storeTarget) {
12616
- pendingStoreTargets.delete(store);
12617
- inflate(container, store, storeTarget.t, storeTarget.v);
12618
- }
12619
- /**
12620
- * Note that we don't do anything with the innerstores we added during serialization, because
12621
- * they are already inflated in the deserialize of the data, above.
12622
- */
12623
- const [, flags, effects] = data;
12624
- const storeHandler = getStoreHandler(target);
12625
- storeHandler.$flags$ = flags;
12626
- storeHandler.$effects$ = effects;
12627
- restoreEffectBackRefForEffectsMap(storeHandler.$effects$, store);
12628
- break;
12629
- }
12630
12993
  case 30 /* TypeIds.Signal */: {
12631
12994
  const signal = target;
12632
12995
  const d = data;
@@ -12642,7 +13005,7 @@ const inflate = (container, target, typeId, data) => {
12642
13005
  signal.$args$ = d[1];
12643
13006
  signal.$untrackedValue$ = NEEDS_COMPUTATION;
12644
13007
  signal.$flags$ = d[2];
12645
- signal.$flags$ |= 1 /* SignalFlags.INVALID */;
13008
+ signal.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
12646
13009
  signal.$hostElement$ = d[3];
12647
13010
  signal.$effects$ = new Set(d.slice(4));
12648
13011
  inflateWrappedSignalValue(signal);
@@ -12668,7 +13031,7 @@ const inflate = (container, target, typeId, data) => {
12668
13031
  asyncSignal.$flags$ = d[5] ?? 0;
12669
13032
  if (asyncSignal.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
12670
13033
  // If it's client only, it was serialized because it pretended to be loading
12671
- asyncSignal.$untrackedLoading$ = true;
13034
+ asyncSignal.$untrackedPending$ = true;
12672
13035
  }
12673
13036
  const hasValue = d.length > 6;
12674
13037
  if (hasValue) {
@@ -12676,19 +13039,13 @@ const inflate = (container, target, typeId, data) => {
12676
13039
  }
12677
13040
  // can happen when never serialize etc
12678
13041
  if (asyncSignal.$untrackedValue$ === NEEDS_COMPUTATION) {
12679
- asyncSignal.$flags$ |= 1 /* SignalFlags.INVALID */;
13042
+ asyncSignal.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
12680
13043
  }
12681
- // Handle old format (negative = no poll) and new format (always positive, flag in d[5])
12682
- const rawExpires = (d[7] ?? 0);
12683
- asyncSignal.expires = Math.abs(rawExpires);
12684
- if (rawExpires < 0) {
12685
- asyncSignal.$flags$ |= 256 /* AsyncSignalFlags.NO_POLL */;
12686
- }
12687
- if (d[8] !== undefined && d[8] !== 1) {
12688
- asyncSignal.$concurrency$ = (d[8] ?? 1);
13044
+ if (d[7] !== undefined && d[7] !== 1) {
13045
+ asyncSignal.$concurrency$ = (d[7] ?? 1);
12689
13046
  asyncSignal.$jobs$ = [];
12690
13047
  }
12691
- asyncSignal.$timeoutMs$ = (d[9] ?? 0);
13048
+ asyncSignal.$timeoutMs$ = (d[8] ?? 0);
12692
13049
  restoreEffectBackRefForEffects(asyncSignal.$effects$, asyncSignal);
12693
13050
  restoreEffectBackRefForEffects(asyncSignal.$loadingEffects$, asyncSignal);
12694
13051
  restoreEffectBackRefForEffects(asyncSignal.$errorEffects$, asyncSignal);
@@ -12719,24 +13076,17 @@ const inflate = (container, target, typeId, data) => {
12719
13076
  if (typeId !== 34 /* TypeIds.SerializerSignal */ && computed.$untrackedValue$ !== NEEDS_COMPUTATION) {
12720
13077
  // If we have a value after SSR, it will always be mean the signal was not invalid
12721
13078
  // The serialized signal is always left invalid so it can recreate the custom object
12722
- computed.$flags$ &= -2 /* SignalFlags.INVALID */;
13079
+ computed.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
12723
13080
  }
12724
13081
  restoreEffectBackRefForEffects(computed.$effects$, computed);
12725
13082
  break;
12726
13083
  }
12727
- case 23 /* TypeIds.Error */: {
12728
- const d = data;
12729
- target.message = d[0];
12730
- for (let i = 1; i < d.length; i += 2) {
12731
- target[d[i]] = d[i + 1];
12732
- }
12733
- break;
12734
- }
12735
13084
  case 36 /* TypeIds.FormData */: {
12736
13085
  const formData = target;
12737
13086
  const d = data;
12738
13087
  for (let i = 0; i < d.length; i++) {
12739
13088
  formData.append(d[i++], d[i]);
13089
+ yield;
12740
13090
  }
12741
13091
  break;
12742
13092
  }
@@ -12751,49 +13101,16 @@ const inflate = (container, target, typeId, data) => {
12751
13101
  jsx.toSort = !!toSort;
12752
13102
  break;
12753
13103
  }
12754
- case 25 /* TypeIds.Set */: {
12755
- const set = target;
12756
- const d = data;
12757
- for (let i = 0; i < d.length; i++) {
12758
- set.add(d[i]);
12759
- }
12760
- break;
12761
- }
12762
- case 26 /* TypeIds.Map */: {
12763
- const map = target;
12764
- const d = data;
12765
- for (let i = 0; i < d.length; i++) {
12766
- map.set(d[i++], d[i]);
12767
- }
12768
- break;
12769
- }
12770
- case 24 /* TypeIds.Promise */: {
12771
- const promise = target;
12772
- const [resolved, result] = data;
12773
- const [resolve, reject] = resolvers.get(promise);
12774
- if (resolved) {
12775
- resolve(result);
12776
- }
12777
- else {
12778
- reject(result);
12779
- }
12780
- break;
12781
- }
12782
- case 27 /* TypeIds.Uint8Array */:
12783
- const bytes = target;
12784
- const buf = atob(data);
12785
- let i = 0;
12786
- for (let j = 0; j < buf.length; j++) {
12787
- const s = buf[j];
12788
- bytes[i++] = s.charCodeAt(0);
12789
- }
12790
- break;
12791
- case 38 /* TypeIds.PropsProxy */:
13104
+ case 38 /* TypeIds.PropsProxy */: {
12792
13105
  const propsProxy = target;
12793
13106
  const d = data;
12794
13107
  let owner = d[0];
12795
13108
  if (owner === _UNINITIALIZED) {
12796
- owner = new JSXNodeImpl(Fragment, d[1], d[2], null, 0, null);
13109
+ owner = new JSXNodeImpl(Fragment, null, null, null, 0, null);
13110
+ // Important: var/const props can be eager-deserialization placeholders. (empty objects)
13111
+ // Do not let JSXNodeImpl constructor normalize {} to EMPTY_OBJ/null.
13112
+ owner.varProps = d[1];
13113
+ owner.constProps = d[2];
12797
13114
  owner._proxy = propsProxy;
12798
13115
  }
12799
13116
  propsProxy[_OWNER] = owner;
@@ -12801,51 +13118,69 @@ const inflate = (container, target, typeId, data) => {
12801
13118
  propsHandler.$effects$ = d[3];
12802
13119
  restoreEffectBackRefForEffectsMap(propsHandler.$effects$, propsProxy);
12803
13120
  break;
13121
+ }
12804
13122
  case 39 /* TypeIds.SubscriptionData */: {
12805
13123
  const effectData = target;
12806
13124
  effectData.data.$scopedStyleIdPrefix$ = data[0];
12807
13125
  effectData.data.$isConst$ = data[1];
12808
13126
  break;
12809
13127
  }
12810
- case 40 /* TypeIds.EffectSubscription */: {
13128
+ case 42 /* TypeIds.SubscriptionDataConstTrue */:
13129
+ case 43 /* TypeIds.SubscriptionDataConstFalse */:
13130
+ break;
13131
+ case 40 /* TypeIds.EffectSubscription */: {
13132
+ const effectSub = target;
13133
+ const d = data;
13134
+ effectSub.consumer = d[0];
13135
+ effectSub.property = d[1];
13136
+ effectSub.data = d[2];
13137
+ restoreEffectBackRefForConsumer(effectSub);
13138
+ break;
13139
+ }
13140
+ case 41 /* TypeIds.SubscriptionPatch */: {
13141
+ const patch = target;
13142
+ const d = data;
13143
+ patch.rootId = d[0];
13144
+ patch.subscriptions = d[1];
13145
+ break;
13146
+ }
13147
+ case 44 /* TypeIds.EffectSubscriptionNoData */: {
12811
13148
  const effectSub = target;
12812
13149
  const d = data;
12813
13150
  effectSub.consumer = d[0];
12814
13151
  effectSub.property = d[1];
12815
- effectSub.data = d[2];
13152
+ effectSub.data = null;
12816
13153
  restoreEffectBackRefForConsumer(effectSub);
12817
13154
  break;
12818
13155
  }
12819
- case 41 /* TypeIds.SubscriptionPatch */: {
12820
- const patch = target;
12821
- const d = data;
12822
- patch.rootId = d[0];
12823
- patch.subscriptions = d[1];
13156
+ case 27 /* TypeIds.Uint8Array */: {
13157
+ const bytes = target;
13158
+ const buf = atob(data);
13159
+ for (let j = 0; j < buf.length; j++) {
13160
+ bytes[j] = buf.charCodeAt(j);
13161
+ if ((j & 31) === 31) {
13162
+ yield;
13163
+ }
13164
+ }
13165
+ break;
13166
+ }
13167
+ case 35 /* TypeIds.Store */: {
13168
+ const store = unwrapStore(target);
13169
+ const storeTarget = container.$pendingStoreTargets$.get(store);
13170
+ if (storeTarget) {
13171
+ container.$pendingStoreTargets$.delete(store);
13172
+ yield* inflateIterator(container, store, storeTarget.t, storeTarget.v);
13173
+ }
13174
+ const [, flags, effects] = data;
13175
+ const storeHandler = getStoreHandler(target);
13176
+ storeHandler.$flags$ = flags;
13177
+ storeHandler.$effects$ = effects;
13178
+ restoreEffectBackRefForEffectsMap(storeHandler.$effects$, store);
12824
13179
  break;
12825
13180
  }
12826
13181
  default:
12827
13182
  throw qError(16 /* QError.serializeErrorNotImplemented */, [typeId]);
12828
13183
  }
12829
- };
12830
- /**
12831
- * Restores an array eagerly. If you need it lazily, use `deserializeData(container, TypeIds.Array,
12832
- * array)` instead
12833
- */
12834
- const _eagerDeserializeArray = (container, data, output = Array(data.length / 2)) => {
12835
- for (let i = 0; i < data.length; i += 2) {
12836
- output[i / 2] = deserializeData(container, data[i], data[i + 1]);
12837
- }
12838
- return output;
12839
- };
12840
- function deserializeData(container, typeId, value) {
12841
- if (typeId === 0 /* TypeIds.Plain */) {
12842
- return value;
12843
- }
12844
- const propValue = allocate(container, typeId, value);
12845
- if (needsInflation(typeId)) {
12846
- inflate(container, propValue, typeId, value);
12847
- }
12848
- return propValue;
12849
13184
  }
12850
13185
  function inflateWrappedSignalValue(signal) {
12851
13186
  if (signal.$hostElement$ !== null && vnode_isVNode(signal.$hostElement$)) {
@@ -12956,15 +13291,21 @@ class DeserializationHandler {
12956
13291
  return value;
12957
13292
  }
12958
13293
  const container = this.$container$;
12959
- const propValue = allocate(container, typeId, value);
12960
- Reflect.set(target, property, propValue);
12961
- this.$data$[idx] = 0 /* TypeIds.Plain */;
12962
- this.$data$[idx + 1] = propValue;
12963
- /** We stored the reference, so now we can inflate, allowing cycles */
12964
- if (needsInflation(typeId)) {
12965
- inflate(container, propValue, typeId, value);
13294
+ const ownsPendingStoreTargets = beginDeserialization(container);
13295
+ try {
13296
+ const propValue = allocate(container, typeId, value);
13297
+ Reflect.set(target, property, propValue);
13298
+ this.$data$[idx] = 0 /* TypeIds.Plain */;
13299
+ this.$data$[idx + 1] = propValue;
13300
+ /** We stored the reference, so now we can inflate, allowing cycles */
13301
+ if (needsInflation(typeId)) {
13302
+ inflate(container, propValue, typeId, value);
13303
+ }
13304
+ return propValue;
13305
+ }
13306
+ finally {
13307
+ endDeserialization(container, ownsPendingStoreTargets);
12966
13308
  }
12967
- return propValue;
12968
13309
  }
12969
13310
  has(target, property) {
12970
13311
  if (property === SERIALIZER_PROXY_UNWRAP) {
@@ -12988,6 +13329,54 @@ class DeserializationHandler {
12988
13329
  }
12989
13330
  }
12990
13331
 
13332
+ const YIELD_INTERVAL = 10;
13333
+ const scheduleYieldingIterator = (state) => {
13334
+ if (!state.$scheduled$) {
13335
+ state.$scheduled$ = true;
13336
+ state.$schedule$();
13337
+ }
13338
+ };
13339
+ const runYieldingIterator = (state, onDone, onError) => {
13340
+ state.$scheduled$ = false;
13341
+ const deadline = performance.now() + YIELD_INTERVAL;
13342
+ let count = 0;
13343
+ try {
13344
+ while (true) {
13345
+ const result = state.$iterator$.next();
13346
+ if (result.done) {
13347
+ onDone(result.value);
13348
+ return;
13349
+ }
13350
+ // Sampling the clock every 32 steps keeps `performance.now()` out of the hottest path.
13351
+ if ((++count & 31) === 0 && performance.now() >= deadline) {
13352
+ scheduleYieldingIterator(state);
13353
+ return;
13354
+ }
13355
+ }
13356
+ }
13357
+ catch (error) {
13358
+ onError(error);
13359
+ }
13360
+ };
13361
+ const createYieldingIteratorState = (iterator, onDone, onError) => {
13362
+ const state = {
13363
+ $iterator$: iterator,
13364
+ $schedule$: undefined,
13365
+ $scheduled$: false,
13366
+ };
13367
+ const schedule = createMacroTask(() => {
13368
+ runYieldingIterator(state, (value) => {
13369
+ schedule.$destroy$?.();
13370
+ onDone(value);
13371
+ }, (error) => {
13372
+ schedule.$destroy$?.();
13373
+ onError(error);
13374
+ });
13375
+ });
13376
+ state.$schedule$ = schedule;
13377
+ return state;
13378
+ };
13379
+
12991
13380
  /**
12992
13381
  * Serialize data to string using SerializationContext.
12993
13382
  *
@@ -13000,12 +13389,12 @@ async function _serialize(data) {
13000
13389
  return serializationContext.$writer$.toString();
13001
13390
  }
13002
13391
  /**
13003
- * Deserialize data from string to an array of objects.
13392
+ * Deserialize data from string.
13004
13393
  *
13005
13394
  * @param rawStateData - Data to deserialize
13006
13395
  * @internal
13007
13396
  */
13008
- function _deserialize(rawStateData) {
13397
+ async function _deserialize(rawStateData) {
13009
13398
  if (rawStateData == null) {
13010
13399
  throw new Error('No state data to deserialize');
13011
13400
  }
@@ -13013,9 +13402,18 @@ function _deserialize(rawStateData) {
13013
13402
  if (!Array.isArray(stateData) || stateData.length < 2 || typeof stateData[0] !== 'number') {
13014
13403
  throw new Error('Invalid state data');
13015
13404
  }
13016
- const container = _createDeserializeContainer(stateData);
13017
- return deserializeData(container, stateData[0], stateData[1]);
13405
+ const state = Array(stateData.length / 2);
13406
+ const container = createBaseDeserializeContainer(stateData, () => state);
13407
+ container.$state$ = state;
13408
+ await runDeserializeIterator(eagerDeserializeStateIterator(container, stateData, state));
13409
+ return state[0];
13018
13410
  }
13411
+ const runDeserializeIterator = (iterator) => {
13412
+ return new Promise((resolve, reject) => {
13413
+ const state = createYieldingIteratorState(iterator, resolve, reject);
13414
+ scheduleYieldingIterator(state);
13415
+ });
13416
+ };
13019
13417
  function getObjectById(id, stateData) {
13020
13418
  if (typeof id === 'string') {
13021
13419
  id = parseInt(id, 10);
@@ -13029,8 +13427,14 @@ function getObjectById(id, stateData) {
13029
13427
  function _createDeserializeContainer(stateData) {
13030
13428
  // eslint-disable-next-line prefer-const
13031
13429
  let state;
13430
+ const container = createBaseDeserializeContainer(stateData, () => state);
13431
+ state = wrapDeserializerProxy(container, stateData);
13432
+ container.$state$ = state;
13433
+ return container;
13434
+ }
13435
+ function createBaseDeserializeContainer(stateData, getState) {
13032
13436
  const container = {
13033
- $getObjectById$: (id) => getObjectById(id, state),
13437
+ $getObjectById$: (id) => getObjectById(id, getState()),
13034
13438
  $getForwardRef$: (id) => container.$forwardRefs$?.[Number(id)],
13035
13439
  getSyncFn: (_) => {
13036
13440
  const fn = () => { };
@@ -13041,8 +13445,6 @@ function _createDeserializeContainer(stateData) {
13041
13445
  $forwardRefs$: null,
13042
13446
  };
13043
13447
  preprocessState(stateData, container);
13044
- state = wrapDeserializerProxy(container, stateData);
13045
- container.$state$ = state;
13046
13448
  return container;
13047
13449
  }
13048
13450
 
@@ -13425,7 +13827,7 @@ const isRecoverable = (err) => {
13425
13827
  };
13426
13828
 
13427
13829
  /** @internal */
13428
- const canRevealRegistration = (registration, isPending = (item) => item.boundary.pending.untrackedValue > 0) => {
13830
+ const canRevealRegistration = (registration, isPending = (item) => item.boundary.untrackedValue > 0) => {
13429
13831
  if (registration === null) {
13430
13832
  return true;
13431
13833
  }
@@ -13502,15 +13904,9 @@ class OutOfOrderRevealCoordinator {
13502
13904
  }
13503
13905
  /** @internal */
13504
13906
  const createOutOfOrderRevealCoordinator = (order, collapsed) => {
13505
- if (!isOutOfOrderStreaming()) {
13506
- return null;
13507
- }
13508
- const container = tryGetInvokeContext()?.$container$;
13509
- let id = 0;
13510
- if (container) {
13511
- id = (outOfOrderRevealIds.get(container) || 0) + 1;
13512
- outOfOrderRevealIds.set(container, id);
13513
- }
13907
+ const container = tryGetInvokeContext().$container$;
13908
+ const id = (outOfOrderRevealIds.get(container) || 0) + 1;
13909
+ outOfOrderRevealIds.set(container, id);
13514
13910
  return new OutOfOrderRevealCoordinator(id, order, collapsed);
13515
13911
  };
13516
13912
  const getOutOfOrderRevealOrderCode = (order) => {
@@ -13534,16 +13930,7 @@ const isOutOfOrderStreaming = () => {
13534
13930
  return container?.outOfOrderStreaming === true && !isOutOfOrderSegmentContainer(container);
13535
13931
  };
13536
13932
  /** @internal */
13537
- const nextOutOfOrderSuspenseId = () => {
13538
- if (!__EXPERIMENTAL__.suspense) {
13539
- return 0;
13540
- }
13541
- const container = tryGetInvokeContext()?.$container$;
13542
- if (container?.outOfOrderStreaming !== true) {
13543
- return 0;
13544
- }
13545
- return container?.nextOutOfOrderId?.() ?? 0;
13546
- };
13933
+ const nextOutOfOrderSuspenseId = () => tryGetInvokeContext().$container$.nextOutOfOrderId();
13547
13934
  /** @internal */
13548
13935
  const applySubscriptionPatches = (container, patches) => {
13549
13936
  if (!__EXPERIMENTAL__.suspense || !patches) {
@@ -13594,13 +13981,12 @@ const mergeSubscriptionSet = (container, producer, backRef, rootEffects, patchEf
13594
13981
  };
13595
13982
 
13596
13983
  const processedStatePatchScripts = new WeakMap();
13597
- /** @internal */
13598
- const processSegmentStateScripts = (container, segmentId) => {
13984
+ function* processSegmentStateScriptsIterator(container, segmentId) {
13599
13985
  if (!__EXPERIMENTAL__.suspense) {
13600
13986
  return;
13601
13987
  }
13602
13988
  const stateContainer = container;
13603
- const qwikStates = stateContainer.element.querySelectorAll(`${stateScriptSelector(stateContainer.$instanceHash$)}${QStatePatchAttrSelector}`);
13989
+ const qwikStates = stateContainer.element.querySelectorAll(`script[type="qwik/state"][q\\:instance="${stateContainer.$instanceHash$}"]${QStatePatchAttrSelector}`);
13604
13990
  const processedScripts = getProcessedStatePatchScripts(container);
13605
13991
  for (let i = 0; i < qwikStates.length; i++) {
13606
13992
  const stateScript = qwikStates[i];
@@ -13611,9 +13997,10 @@ const processSegmentStateScripts = (container, segmentId) => {
13611
13997
  continue;
13612
13998
  }
13613
13999
  processedScripts.add(stateScript);
13614
- processStatePatch(container, stateContainer, stateScript.textContent);
14000
+ yield* processStatePatch(container, stateContainer, stateScript.textContent);
14001
+ yield;
13615
14002
  }
13616
- };
14003
+ }
13617
14004
  const getProcessedStatePatchScripts = (container) => {
13618
14005
  let processedScripts = processedStatePatchScripts.get(container);
13619
14006
  if (!processedScripts) {
@@ -13622,20 +14009,18 @@ const getProcessedStatePatchScripts = (container) => {
13622
14009
  }
13623
14010
  return processedScripts;
13624
14011
  };
13625
- const stateScriptSelector = (instanceHash) => {
13626
- return `script[type="qwik/state"][q\\:instance="${instanceHash}"]`;
13627
- };
13628
- const processStatePatch = (container, stateContainer, textContent) => {
13629
- if (textContent) {
13630
- const [rootStart, rawStateData, forwardRefs, subscriptionPatchRootId] = JSON.parse(textContent);
13631
- appendStatePatchRoots(container, stateContainer, rootStart, rawStateData);
13632
- mergeForwardRefs(stateContainer, forwardRefs || undefined);
13633
- applySubscriptionPatches(container, subscriptionPatchRootId === undefined
13634
- ? undefined
13635
- : stateContainer.$getObjectById$(subscriptionPatchRootId));
14012
+ function* processStatePatch(container, stateContainer, textContent) {
14013
+ if (!textContent) {
14014
+ return;
13636
14015
  }
13637
- };
13638
- const appendStatePatchRoots = (container, stateContainer, rootStart, rawStateData) => {
14016
+ const [rootStart, rawStateData, forwardRefs, subscriptionPatchRootId] = JSON.parse(textContent);
14017
+ yield* appendStatePatchRoots(container, stateContainer, rootStart, rawStateData);
14018
+ mergeForwardRefs(stateContainer, forwardRefs || undefined);
14019
+ applySubscriptionPatches(container, subscriptionPatchRootId === undefined
14020
+ ? undefined
14021
+ : stateContainer.$getObjectById$(subscriptionPatchRootId));
14022
+ }
14023
+ function* appendStatePatchRoots(container, stateContainer, rootStart, rawStateData) {
13639
14024
  const currentRootCount = stateContainer.$rawStateData$.length / 2;
13640
14025
  if (rootStart !== currentRootCount) {
13641
14026
  if (qDev) {
@@ -13645,12 +14030,13 @@ const appendStatePatchRoots = (container, stateContainer, rootStart, rawStateDat
13645
14030
  }
13646
14031
  for (let i = 0; i < rawStateData.length; i++) {
13647
14032
  stateContainer.$rawStateData$[rootStart * 2 + i] = rawStateData[i];
14033
+ yield;
13648
14034
  }
13649
- preprocessState(stateContainer.$rawStateData$, container, undefined, rootStart * 2);
14035
+ yield* preprocessStateIterator(stateContainer.$rawStateData$, container, rootStart * 2);
13650
14036
  stateContainer.$stateData$ = wrapDeserializerProxy(container, stateContainer.$rawStateData$);
13651
14037
  stateContainer.$stateData$.length = stateContainer.$rawStateData$.length / 2;
13652
14038
  stateContainer.$rootForwardRefs$ = stateContainer.$forwardRefs$;
13653
- };
14039
+ }
13654
14040
  const mergeForwardRefs = (stateContainer, forwardRefs) => {
13655
14041
  if (!forwardRefs) {
13656
14042
  return;
@@ -13665,24 +14051,172 @@ const mergeForwardRefs = (stateContainer, forwardRefs) => {
13665
14051
  stateContainer.$forwardRefs$ = rootForwardRefs;
13666
14052
  };
13667
14053
 
13668
- // NOTE: we want to move this function to qwikloader, and therefore this function should not have any external dependencies
14054
+ const Q_SUSPENSE_RESOLVED = 'q:r';
14055
+ const Q_PATCH = 'q:patch';
14056
+ /**
14057
+ * Process the VNodeData script tags and store the VNodeData in the VNodeDataMap.
14058
+ *
14059
+ * The end result of this function is that each DOM element has the associated `VNodeData` attached
14060
+ * to it, to be used later `VNode` materialization. The "attachment" is done through the
14061
+ * `VNodeDataMap`.
14062
+ *
14063
+ * Run this function on startup to process the `<script type="qwik/vnode">` tags. The data in the
14064
+ * tags needs to be parsed and attached to the DOM elements. (We do this through `VNodeDataMap`)
14065
+ * `VNodeDataMap` is then used to lazily materialize the VNodes.
14066
+ *
14067
+ * Only one invocation of this function is needed per document/browser session.
14068
+ *
14069
+ * Below is an example of the kinds of constructs which need to be handled when dealing with
14070
+ * VNodeData deserialization.
14071
+ *
14072
+ * ```
14073
+ * <html q:container="paused">
14074
+ * <body>
14075
+ * <div q:container="paused">
14076
+ * <script type="qwik/vnode">...</script>
14077
+ * </div>
14078
+ * <div q:container="html">...</div>
14079
+ * before
14080
+ * <!--q:container=ABC-->
14081
+ * ...
14082
+ * <!--/q:container-->
14083
+ * after
14084
+ * <!--q:ignore=FOO-->
14085
+ * ...
14086
+ * <!--q:container-island=BAR-->
14087
+ * <div>some interactive island</div>
14088
+ * <!--/q:container-island-->
14089
+ * ...
14090
+ * <!--/q:ignore-->
14091
+ * <textarea q:container="text">...</textarea>
14092
+ * <script type="qwik/vnode">...</script>
14093
+ * </body>
14094
+ * </html>
14095
+ * ```
14096
+ *
14097
+ * Each `qwik/vnode` script assumes that the elements are numbered in depth first order. For this
14098
+ * reason, whenever the `processVNodeData` comes across a `q:container` it must ignore its
14099
+ * children.
14100
+ *
14101
+ * IMPLEMENTATION:
14102
+ *
14103
+ * - Stack to keep track of the current `q:container` being processed.
14104
+ * - Attach all `qwik/vnode` scripts (not the data contain within them) to the `q:container` element.
14105
+ * - Walk the tree and process each `q:container` element.
14106
+ */
14107
+ function processVNodeData(document, containerElement) {
14108
+ const qDocument = document;
14109
+ // Per-container gate: a walk already populated this container's refs → done.
14110
+ if (containerElement?.qVNodeRefs) {
14111
+ return;
14112
+ }
14113
+ // A document-wide walk is in flight; it will pick up this newly-added container too.
14114
+ if (qDocument.qVNodeDataStarted && !qDocument.qVNodeDataProcessed) {
14115
+ return;
14116
+ }
14117
+ qDocument.qVNodeDataStarted = true;
14118
+ qDocument.qVNodeData ||= new WeakMap();
14119
+ enqueueProcessVNodeDataJob(qDocument, processRootVNodeData(document));
14120
+ }
13669
14121
  function processOutOfOrderSegmentVNodeData(document, segmentId, contentNode) {
13670
14122
  if (!__EXPERIMENTAL__.suspense || !contentNode) {
13671
14123
  return;
13672
14124
  }
13673
14125
  const qContainerElement = contentNode.closest('[q\\:container]');
13674
- const script = qContainerElement?.querySelector(`script[type="qwik/vnode"][q\\:r="${segmentId}"]:not([q\\:patch])`);
13675
- processVNodeDataImpl(document, segmentId, qContainerElement, contentNode, script?.textContent || undefined);
13676
- const patches = qContainerElement?.querySelectorAll(`script[type="qwik/vnode"][q\\:r="${segmentId}"][q\\:patch]`);
13677
- for (let i = 0; patches && i < patches.length; i++) {
13678
- document.qProcessVNodeDataPatch?.(patches[i]);
14126
+ if (!qContainerElement) {
14127
+ return;
14128
+ }
14129
+ const script = qContainerElement.querySelector(`script[type="qwik/vnode"][q\\:r="${segmentId}"]:not([q\\:patch])`);
14130
+ const patches = qContainerElement.querySelectorAll(`script[type="qwik/vnode"][q\\:r="${segmentId}"][q\\:patch]`);
14131
+ enqueueProcessVNodeDataJob(document, processOutOfOrderSegmentVNodeDataIterator(document, segmentId, qContainerElement, contentNode, script?.textContent || undefined, patches));
14132
+ }
14133
+ const onVNodeDataReady = (document, callback) => {
14134
+ const qDocument = document;
14135
+ if (qDocument.qVNodeDataReady) {
14136
+ callback();
14137
+ }
14138
+ else {
14139
+ (qDocument.qVNodeDataCallbacks ||= []).push(callback);
14140
+ }
14141
+ };
14142
+ function enqueueProcessVNodeDataJob(qDocument, iterator) {
14143
+ const state = (qDocument.qVNodeDataState ||= {
14144
+ $queue$: [],
14145
+ $active$: null,
14146
+ });
14147
+ qDocument.qProcessVNodeDataPatch ||= (script) => enqueueProcessVNodeDataJob(qDocument, processVNodeDataPatch(qDocument, script));
14148
+ qDocument.qVNodeDataReady = false;
14149
+ state.$queue$.push(iterator);
14150
+ scheduleProcessVNodeData(qDocument, state);
14151
+ }
14152
+ function scheduleProcessVNodeData(qDocument, state) {
14153
+ if (state.$active$) {
14154
+ return;
14155
+ }
14156
+ const iterator = state.$queue$.shift();
14157
+ if (!iterator) {
14158
+ markVNodeDataReady(qDocument);
14159
+ return;
14160
+ }
14161
+ state.$active$ = createYieldingIteratorState(runProcessVNodeData(qDocument, state, iterator), () => {
14162
+ if (qDocument.qVNodeDataState === state) {
14163
+ state.$active$ = null;
14164
+ scheduleProcessVNodeData(qDocument, state);
14165
+ }
14166
+ }, (error) => {
14167
+ state.$active$ = null;
14168
+ logError(error);
14169
+ markVNodeDataReady(qDocument);
14170
+ });
14171
+ scheduleYieldingIterator(state.$active$);
14172
+ }
14173
+ function* runProcessVNodeData(qDocument, state, iterator) {
14174
+ while (qDocument.qVNodeDataState === state) {
14175
+ if (iterator.next().done) {
14176
+ return;
14177
+ }
14178
+ yield;
13679
14179
  }
13680
14180
  }
13681
- function processVNodeData(document) {
13682
- processVNodeDataImpl(document);
13683
- document.qVNodeDataProcessed = true;
14181
+ function markVNodeDataReady(document) {
14182
+ if (document.qVNodeDataReady) {
14183
+ return;
14184
+ }
14185
+ document.qVNodeDataReady = true;
14186
+ document.qVNodeDataState = undefined;
14187
+ const callbacks = document.qVNodeDataCallbacks;
14188
+ document.qVNodeDataCallbacks = undefined;
14189
+ if (callbacks) {
14190
+ for (let i = 0; i < callbacks.length; i++) {
14191
+ callbacks[i]();
14192
+ }
14193
+ }
14194
+ }
14195
+ function* processRootVNodeData(document) {
14196
+ yield* processVNodeDataImpl(document);
14197
+ const qDocument = document;
14198
+ qDocument.qVNodeDataProcessed = true;
14199
+ // Re-open the gate so a container added after this drain re-runs a walk.
14200
+ qDocument.qVNodeDataStarted = false;
14201
+ }
14202
+ function* processOutOfOrderSegmentVNodeDataIterator(document, segmentId, segmentContainer, segmentContent, segmentVNodeData, patches) {
14203
+ yield* processVNodeDataImpl(document, segmentId, segmentContainer, segmentContent, segmentVNodeData);
14204
+ for (let i = 0; i < patches.length; i++) {
14205
+ yield* processVNodeDataPatch(document, patches[i]);
14206
+ }
14207
+ }
14208
+ function* processVNodeDataPatch(document, script) {
14209
+ const qContainerElement = script?.closest('[q\\:container]');
14210
+ const patchSegment = script?.getAttribute(Q_SUSPENSE_RESOLVED);
14211
+ const contentNode = qContainerElement &&
14212
+ (patchSegment
14213
+ ? qContainerElement.querySelector(`[q\\:rp="${patchSegment}"]`)
14214
+ : qContainerElement);
14215
+ if (qContainerElement && contentNode) {
14216
+ yield* processVNodeDataImpl(document, patchSegment || undefined, qContainerElement, contentNode, script.textContent);
14217
+ }
13684
14218
  }
13685
- function processVNodeDataImpl(document, segmentId, segmentContainer, segmentContent, segmentVNodeData) {
14219
+ function* processVNodeDataImpl(document, segmentId, segmentContainer, segmentContent, segmentVNodeData) {
13686
14220
  const Q_CONTAINER = 'q:container';
13687
14221
  const Q_CONTAINER_END = '/' + Q_CONTAINER;
13688
14222
  const Q_PROPS_SEPARATOR = ':';
@@ -13691,9 +14225,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13691
14225
  const Q_IGNORE_END = '/' + Q_IGNORE;
13692
14226
  const Q_CONTAINER_ISLAND = 'q:container-island';
13693
14227
  const Q_CONTAINER_ISLAND_END = '/' + Q_CONTAINER_ISLAND;
13694
- const Q_SUSPENSE_RESOLVED = 'q:r';
13695
14228
  const Q_SUSPENSE_RESULT_PARENT = 'q:rp';
13696
- const Q_PATCH = 'q:patch';
13697
14229
  const qDocument = document;
13698
14230
  const vNodeDataMap = (qDocument.qVNodeData ||= new WeakMap());
13699
14231
  const prototype = document.body;
@@ -13711,7 +14243,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13711
14243
  const hasAttribute = prototype.hasAttribute;
13712
14244
  const getNodeType = getter(prototype, 'nodeType');
13713
14245
  // Process all of the `qwik/vnode` script tags by attaching them to the corresponding containers.
13714
- const attachVnodeDataAndRefs = (element) => {
14246
+ const attachVnodeDataAndRefs = function* (element) {
13715
14247
  const scripts = element.querySelectorAll('script[type="qwik/vnode"]');
13716
14248
  for (let i = 0; i < scripts.length; i++) {
13717
14249
  const script = scripts[i];
@@ -13719,6 +14251,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13719
14251
  qContainer.qVNodeRefs ||= new Map();
13720
14252
  if (script.hasAttribute(Q_PATCH)) {
13721
14253
  qDocument.qProcessVNodeDataPatch?.(script);
14254
+ yield;
13722
14255
  continue;
13723
14256
  }
13724
14257
  const scriptContent = script.textContent;
@@ -13729,14 +14262,21 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13729
14262
  else {
13730
14263
  qContainer.qVnodeData = scriptContent;
13731
14264
  }
14265
+ yield;
13732
14266
  }
13733
14267
  const shadowRoots = element.querySelectorAll('[q\\:shadowroot]');
13734
14268
  for (let i = 0; i < shadowRoots.length; i++) {
13735
14269
  const parent = shadowRoots[i];
13736
14270
  const shadowRoot = parent.shadowRoot;
13737
- shadowRoot && attachVnodeDataAndRefs(shadowRoot);
14271
+ if (shadowRoot) {
14272
+ yield* attachVnodeDataAndRefs(shadowRoot);
14273
+ }
14274
+ yield;
13738
14275
  }
13739
14276
  };
14277
+ ///////////////////////////////
14278
+ // Functions to consume the tree.
14279
+ ///////////////////////////////
13740
14280
  /**
13741
14281
  * Looks up which type of node this is in a monomorphic way which should be faster.
13742
14282
  *
@@ -13792,7 +14332,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13792
14332
  * @param exitNode The node which represents the last node and we should exit.
13793
14333
  * @param qVNodeRefs Place to store the VNodeRefs
13794
14334
  */
13795
- const walkContainer = (walker, containerNode, node, exitNode, vData, qVNodeRefs, qContainerElement, segmentId) => {
14335
+ const walkContainer = function* (walker, containerNode, node, exitNode, vData, qVNodeRefs, qContainerElement, segmentId) {
13796
14336
  const isSegment = __EXPERIMENTAL__.suspense && segmentId !== undefined;
13797
14337
  const vData_length = vData.length;
13798
14338
  /// Stores the current element index as the TreeWalker traverses the DOM.
@@ -13818,6 +14358,9 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13818
14358
  }
13819
14359
  return elementsToSkip;
13820
14360
  };
14361
+ if (!node) {
14362
+ return;
14363
+ }
13821
14364
  do {
13822
14365
  if (node === exitNode) {
13823
14366
  return;
@@ -13831,7 +14374,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13831
14374
  while (cursor && !(nextNode = nextSibling(cursor))) {
13832
14375
  cursor = cursor.parentNode;
13833
14376
  }
13834
- walkContainer(walker, container, node, nextNode, container.qVnodeData || '', container.qVNodeRefs, container);
14377
+ yield* walkContainer(walker, container, node, nextNode, container.qVnodeData || '', container.qVNodeRefs, container);
13835
14378
  }
13836
14379
  else if (nodeType === 16 /* NodeType.COMMENT_IGNORE_START */) {
13837
14380
  let islandNode = node;
@@ -13866,7 +14409,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13866
14409
  throw new Error(`<!--${node?.nodeValue}--> not closed!`);
13867
14410
  }
13868
14411
  } while (getFastNodeType(nextNode) !== 8 /* NodeType.COMMENT_SKIP_END */);
13869
- walkContainer(walker, node, node, nextNode, '', null, qContainerElement);
14412
+ yield* walkContainer(walker, node, node, nextNode, '', null, qContainerElement);
13870
14413
  }
13871
14414
  else if (nodeType === 6 /* NodeType.ELEMENT_SHADOW_ROOT_WRAPPER */) {
13872
14415
  // If we are in a shadow root, we need to get the shadow root element.
@@ -13874,7 +14417,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13874
14417
  const shadowRootContainer = node;
13875
14418
  const shadowRoot = shadowRootContainer?.shadowRoot;
13876
14419
  if (shadowRoot) {
13877
- walkContainer(
14420
+ yield* walkContainer(
13878
14421
  // we need to create a new walker for the shadow root
13879
14422
  document.createTreeWalker(shadowRoot, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */), null, shadowRoot, null, '', null, null);
13880
14423
  }
@@ -13937,51 +14480,42 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13937
14480
  }
13938
14481
  elementIdx++;
13939
14482
  if (__EXPERIMENTAL__.suspense && contentBoundaryId !== null) {
13940
- processSuspenseContentSegment(qContainerElement, node, contentBoundaryId);
14483
+ yield* processSuspenseContentSegment(qContainerElement, node, contentBoundaryId);
13941
14484
  nextNode = nextSibling(node);
13942
14485
  if (nextNode) {
13943
14486
  walker.currentNode = nextNode;
13944
14487
  }
13945
14488
  }
13946
14489
  }
14490
+ yield;
13947
14491
  } while ((node = nextNode || walker.nextNode()));
13948
14492
  };
13949
- const processVNodeDataScope = (qContainerElement, contentNode, vData, scopeSegmentId) => {
14493
+ const processVNodeDataScope = function* (qContainerElement, contentNode, vData, scopeSegmentId) {
13950
14494
  qContainerElement.qVNodeRefs ||= new Map();
13951
14495
  const scopeWalker = document.createTreeWalker(document, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */);
13952
14496
  scopeWalker.currentNode = contentNode;
13953
- walkContainer(scopeWalker, contentNode, contentNode, nextSibling(contentNode), vData, qContainerElement.qVNodeRefs, qContainerElement, scopeSegmentId);
14497
+ yield* walkContainer(scopeWalker, contentNode, contentNode, nextSibling(contentNode), vData, qContainerElement.qVNodeRefs, qContainerElement, scopeSegmentId);
13954
14498
  };
13955
- const processSuspenseContentSegment = __EXPERIMENTAL__.suspense
13956
- ? (qContainerElement, contentNode, boundaryId, vData) => {
13957
- vData ||= qContainerElement?.qSegmentVnodeData?.get(boundaryId);
13958
- if (qContainerElement && vData) {
13959
- processVNodeDataScope(qContainerElement, contentNode, vData, boundaryId);
13960
- }
13961
- }
13962
- : null;
13963
- qDocument.qProcessVNodeDataPatch = (script) => {
13964
- const qContainerElement = script?.closest('[q\\:container]');
13965
- const patchSegment = script?.getAttribute(Q_SUSPENSE_RESOLVED);
13966
- const contentNode = qContainerElement &&
13967
- (patchSegment
13968
- ? qContainerElement.querySelector(`[q\\:rp="${patchSegment}"]`)
13969
- : qContainerElement);
13970
- if (qContainerElement && contentNode) {
13971
- processVNodeDataScope(qContainerElement, contentNode, script.textContent, patchSegment || undefined);
14499
+ const processSuspenseContentSegment = function* (qContainerElement, contentNode, boundaryId, vData) {
14500
+ vData ||= qContainerElement?.qSegmentVnodeData?.get(boundaryId);
14501
+ if (qContainerElement && vData) {
14502
+ yield* processVNodeDataScope(qContainerElement, contentNode, vData, boundaryId);
13972
14503
  }
13973
14504
  };
13974
- if (__EXPERIMENTAL__.suspense && segmentId !== undefined) {
13975
- if (segmentContainer && segmentContent && segmentVNodeData) {
13976
- segmentContainer.qVNodeRefs ||= new Map();
13977
- processSuspenseContentSegment(segmentContainer, segmentContent, segmentId, segmentVNodeData);
14505
+ if (segmentContainer && segmentContent && segmentVNodeData !== undefined) {
14506
+ segmentContainer.qVNodeRefs ||= new Map();
14507
+ if (__EXPERIMENTAL__.suspense && segmentId !== undefined) {
14508
+ yield* processSuspenseContentSegment(segmentContainer, segmentContent, segmentId, segmentVNodeData);
14509
+ }
14510
+ else {
14511
+ yield* processVNodeDataScope(segmentContainer, segmentContent, segmentVNodeData);
13978
14512
  }
13979
14513
  return;
13980
14514
  }
13981
- attachVnodeDataAndRefs(document);
14515
+ yield* attachVnodeDataAndRefs(document);
13982
14516
  // Walk the tree and process each `q:container` element.
13983
14517
  const walker = document.createTreeWalker(document, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */);
13984
- walkContainer(walker, null, walker.firstChild(), null, '', null, null);
14518
+ yield* walkContainer(walker, null, walker.firstChild(), null, '', null, null);
13985
14519
  }
13986
14520
  const isSeparator = (ch) =>
13987
14521
  /* `!` */ VNodeDataSeparator.ADVANCE_1 <= ch && ch <= VNodeDataSeparator.ADVANCE_8192; /* `.` */
@@ -14016,6 +14550,87 @@ const findVDataSectionEnd = (vData, start, end) => {
14016
14550
  return start;
14017
14551
  };
14018
14552
 
14553
+ function isContainerReady(container) {
14554
+ return container.$containerDataProcessState$ === 3 /* ContainerDataProcessState.ProcessingStateDone */;
14555
+ }
14556
+ function processContainerStateData(container, iterator) {
14557
+ const state = (container.$containerStateDataState$ ||= {
14558
+ $queue$: [],
14559
+ $active$: null,
14560
+ });
14561
+ container.$containerDataProcessState$ = 2 /* ContainerDataProcessState.ProcessingState */;
14562
+ state.$queue$.push(iterator);
14563
+ scheduleProcessContainerStateData(container, state);
14564
+ }
14565
+ const onContainerDataReady = (container, callback) => {
14566
+ const domContainer = container;
14567
+ const checkReady = () => {
14568
+ if (!isContainerReady(domContainer)) {
14569
+ (domContainer.$containerStateReadyCallbacks$ ||= []).push(checkReady);
14570
+ return;
14571
+ }
14572
+ onVNodeDataReady(domContainer.document, () => {
14573
+ if (isContainerReady(domContainer)) {
14574
+ callback();
14575
+ }
14576
+ else {
14577
+ checkReady();
14578
+ }
14579
+ });
14580
+ };
14581
+ checkReady();
14582
+ };
14583
+ const whenContainerDataReady = (container, callback) => {
14584
+ const domContainer = container;
14585
+ if (isContainerReady(domContainer) && domContainer.document.qVNodeDataReady) {
14586
+ return callback();
14587
+ }
14588
+ return new Promise((resolve, reject) => {
14589
+ onContainerDataReady(domContainer, () => {
14590
+ try {
14591
+ resolve(callback());
14592
+ }
14593
+ catch (error) {
14594
+ reject(error);
14595
+ }
14596
+ });
14597
+ });
14598
+ };
14599
+ function scheduleProcessContainerStateData(container, state) {
14600
+ if (state.$active$) {
14601
+ return;
14602
+ }
14603
+ const iterator = state.$queue$.shift();
14604
+ if (!iterator) {
14605
+ markContainerDataReady(container);
14606
+ return;
14607
+ }
14608
+ state.$active$ = createYieldingIteratorState(iterator, () => {
14609
+ state.$active$ = null;
14610
+ scheduleProcessContainerStateData(container, state);
14611
+ }, (error) => {
14612
+ state.$active$ = null;
14613
+ logError(error);
14614
+ markContainerDataReady(container);
14615
+ });
14616
+ scheduleYieldingIterator(state.$active$);
14617
+ }
14618
+ function markContainerDataReady(container) {
14619
+ const state = container.$containerDataProcessState$;
14620
+ if (state !== 2 /* ContainerDataProcessState.ProcessingState */) {
14621
+ return;
14622
+ }
14623
+ container.$containerDataProcessState$ = 3 /* ContainerDataProcessState.ProcessingStateDone */;
14624
+ container.$containerStateDataState$ = undefined;
14625
+ const callbacks = container.$containerStateReadyCallbacks$;
14626
+ container.$containerStateReadyCallbacks$ = undefined;
14627
+ if (callbacks) {
14628
+ for (let i = 0; i < callbacks.length; i++) {
14629
+ callbacks[i]();
14630
+ }
14631
+ }
14632
+ }
14633
+
14019
14634
  /** @file Public APIs for the SSR */
14020
14635
  /** @public */
14021
14636
  function getDomContainer(element) {
@@ -14038,8 +14653,12 @@ function getOutOfOrderStreamingScript(boundaryId, content) {
14038
14653
  const qContainerElement = content?.closest(QContainerSelector);
14039
14654
  const qContainer = qContainerElement?.qContainer;
14040
14655
  if (qContainer) {
14041
- processOutOfOrderSegmentVNodeData(qContainer.element.ownerDocument, segmentId, content);
14042
- processSegmentStateScripts(qContainer, segmentId);
14656
+ const document = qContainer.element.ownerDocument;
14657
+ processOutOfOrderSegmentVNodeData(document, segmentId, content);
14658
+ onVNodeDataReady(document, () => {
14659
+ qContainer.element.qContainer === qContainer &&
14660
+ processContainerStateData(qContainer, processSegmentStateScriptsIterator(qContainer, segmentId));
14661
+ });
14043
14662
  }
14044
14663
  }
14045
14664
  /** @internal */
@@ -14054,6 +14673,9 @@ class DomContainer extends _SharedContainer {
14054
14673
  $instanceHash$;
14055
14674
  $forwardRefs$ = null;
14056
14675
  vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
14676
+ $containerDataProcessState$ = 0 /* ContainerDataProcessState.NotStarted */;
14677
+ $containerStateReadyCallbacks$ = undefined;
14678
+ $containerStateDataState$ = undefined;
14057
14679
  $rawStateData$;
14058
14680
  $stateData$;
14059
14681
  $rootForwardRefs$ = null;
@@ -14064,7 +14686,8 @@ class DomContainer extends _SharedContainer {
14064
14686
  if (!this.qContainer) {
14065
14687
  throw qError(25 /* QError.elementWithoutContainer */);
14066
14688
  }
14067
- this.document = element.ownerDocument;
14689
+ const document = element.ownerDocument;
14690
+ this.document = document;
14068
14691
  this.element = element;
14069
14692
  this.$buildBase$ = element.getAttribute(QBaseAttr);
14070
14693
  this.$instanceHash$ = element.getAttribute(QInstanceAttr);
@@ -14072,23 +14695,46 @@ class DomContainer extends _SharedContainer {
14072
14695
  this.rootVNode = vnode_newUnMaterializedElement(this.element);
14073
14696
  this.$rawStateData$ = [];
14074
14697
  this.$stateData$ = [];
14075
- const document = this.element.ownerDocument;
14076
- if (!document.qVNodeDataProcessed) {
14077
- processVNodeData(document);
14078
- }
14079
14698
  if (__EXPERIMENTAL__.suspense && document.querySelector('template[q\\:r]')) {
14080
14699
  document.qProcessOOOS ||= getOutOfOrderStreamingScript;
14081
14700
  }
14082
14701
  this.$qFuncs$ = getQFuncs(document, this.$instanceHash$) || EMPTY_ARRAY;
14083
14702
  this.$setServerData$();
14084
- element.setAttribute(QContainerAttr, "resumed" /* QContainerValue.RESUMED */);
14085
14703
  element.qContainer = this;
14086
14704
  element.qDestroy = () => this.$destroy$();
14087
- this.$processRootStateScript$();
14705
+ this.$containerDataProcessState$ = 1 /* ContainerDataProcessState.ProcessingVNode */;
14706
+ processVNodeData(document, element);
14707
+ onVNodeDataReady(document, () => {
14708
+ if (this.$containerDataProcessState$ === 1 /* ContainerDataProcessState.ProcessingVNode */) {
14709
+ processContainerStateData(this, this.$processContainerData$());
14710
+ }
14711
+ });
14712
+ }
14713
+ *$processContainerData$() {
14714
+ const element = this.element;
14715
+ if (element.qContainer !== this) {
14716
+ return;
14717
+ }
14718
+ const rootState = element.querySelector(`script[type="qwik/state"][q\\:instance="${this.$instanceHash$}"]:not(${QStatePatchAttrSelector})`) ||
14719
+ element.querySelector(`script[type="qwik/state"]:not([q\\:instance]):not(${QStatePatchAttrSelector})`);
14720
+ if (rootState) {
14721
+ this.$rawStateData$ = JSON.parse(rootState.textContent);
14722
+ yield* preprocessStateIterator(this.$rawStateData$, this);
14723
+ this.$rootForwardRefs$ = this.$forwardRefs$;
14724
+ const rootCount = this.$rawStateData$.length / 2;
14725
+ const statePrewarm = element.getAttribute(QStatePrewarmAttr);
14726
+ if (statePrewarm !== null && rootCount > 0 && rootCount >= Number(statePrewarm)) {
14727
+ this.$stateData$ = yield* eagerDeserializeStateIterator(this, this.$rawStateData$);
14728
+ }
14729
+ else {
14730
+ this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
14731
+ }
14732
+ }
14088
14733
  if (__EXPERIMENTAL__.suspense) {
14089
- processSegmentStateScripts(this);
14734
+ yield* processSegmentStateScriptsIterator(this);
14090
14735
  }
14091
14736
  this.$hoistStyles$();
14737
+ element.setAttribute(QContainerAttr, "resumed" /* QContainerValue.RESUMED */);
14092
14738
  if (!qTest && element.isConnected) {
14093
14739
  element.dispatchEvent(new CustomEvent('qresume', { bubbles: true }));
14094
14740
  }
@@ -14108,9 +14754,15 @@ class DomContainer extends _SharedContainer {
14108
14754
  }
14109
14755
  el.removeAttribute(QContainerAttr);
14110
14756
  const document = el.ownerDocument;
14757
+ this.$containerStateDataState$ = undefined;
14758
+ this.$containerDataProcessState$ = 0 /* ContainerDataProcessState.NotStarted */;
14111
14759
  const hasContainers = document.querySelector(QContainerSelector) !== null;
14112
14760
  if (!hasContainers) {
14113
14761
  document.qVNodeData = undefined;
14762
+ document.qVNodeDataStarted = undefined;
14763
+ document.qVNodeDataReady = undefined;
14764
+ document.qVNodeDataState = undefined;
14765
+ document.qVNodeDataCallbacks = undefined;
14114
14766
  document.qVNodeDataProcessed = undefined;
14115
14767
  document.qProcessVNodeDataPatch = undefined;
14116
14768
  }
@@ -14120,18 +14772,6 @@ class DomContainer extends _SharedContainer {
14120
14772
  }
14121
14773
  }
14122
14774
  }
14123
- $processRootStateScript$() {
14124
- const rootState = this.element.querySelector(`${this.$stateScriptSelector$()}:not(${QStatePatchAttrSelector})`);
14125
- if (rootState) {
14126
- this.$rawStateData$ = JSON.parse(rootState.textContent);
14127
- preprocessState(this.$rawStateData$, this);
14128
- this.$rootForwardRefs$ = this.$forwardRefs$;
14129
- this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
14130
- }
14131
- }
14132
- $stateScriptSelector$() {
14133
- return `script[type="qwik/state"][q\\:instance="${this.$instanceHash$}"]`;
14134
- }
14135
14775
  /**
14136
14776
  * The first time we render we need to hoist the styles. (Meaning we need to move all styles from
14137
14777
  * component inline to <head>)
@@ -14229,7 +14869,6 @@ class DomContainer extends _SharedContainer {
14229
14869
  case OnRenderProp:
14230
14870
  case QCtxAttr:
14231
14871
  case QBackRefs:
14232
- case QCursorBoundary:
14233
14872
  getObjectById = this.$getObjectById$;
14234
14873
  break;
14235
14874
  case ELEMENT_SEQ_IDX:
@@ -14420,8 +15059,7 @@ function invokeApply(context, fn, args) {
14420
15059
  _context = previousContext;
14421
15060
  }
14422
15061
  }
14423
- const newInvokeContextFromDOM = (event, element) => {
14424
- const domContainer = getDomContainer(element);
15062
+ const newInvokeContextFromDOM = (event, element, domContainer = getDomContainer(element)) => {
14425
15063
  const hostElement = vnode_locate(domContainer.rootVNode, element);
14426
15064
  const locale = domContainer.$locale$;
14427
15065
  locale && setLocale(locale);
@@ -14563,7 +15201,7 @@ const initLazyRefDev = (lazy) => {
14563
15201
  };
14564
15202
  /** Validate captured scope and register WeakRef tracking on a QRLClass instance. */
14565
15203
  const initQrlClassDev = (lazy, captures, qrl) => {
14566
- if (captures && typeof captures === 'object') {
15204
+ if (Array.isArray(captures)) {
14567
15205
  for (let i = 0; i < captures.length; i++) {
14568
15206
  const item = captures[i];
14569
15207
  verifySerializable(item, 'Captured variable in the closure can not be serialized');
@@ -14656,6 +15294,7 @@ const setupHmr = (LazyRefClass, setGetLazyRef) => {
14656
15294
  };
14657
15295
 
14658
15296
  // keep these imports above the rest to prevent circular dep issues
15297
+ let reportedChunkFailures;
14659
15298
  let getLazyRef = (chunk, symbol, symbolFn, ref, container) => {
14660
15299
  return new LazyRef(chunk, symbol, symbolFn, ref, container);
14661
15300
  };
@@ -14696,7 +15335,25 @@ class LazyRef {
14696
15335
  this.$ref$ = ref;
14697
15336
  if (isPromise(ref)) {
14698
15337
  ref.then((r) => (this.$ref$ = r), (err) => {
14699
- console.error(`qrl ${this.$symbol$} failed to load`, err);
15338
+ const errorMessage = `qrl ${this.$symbol$} failed to load`;
15339
+ if (qTest ? isServerPlatform() : isServer) {
15340
+ console.error(errorMessage, err);
15341
+ }
15342
+ else if (qTest ? !isServerPlatform() : isBrowser$1) {
15343
+ const failureKey = this.$chunk$ === null ? `symbol:${this.$symbol$}` : `chunk:${this.$chunk$}`;
15344
+ const container = this.$container$;
15345
+ let containerFailures = container && reportedChunkFailures?.get(container);
15346
+ if (!containerFailures?.has(failureKey)) {
15347
+ if (container) {
15348
+ if (!containerFailures) {
15349
+ containerFailures = new Set();
15350
+ (reportedChunkFailures ||= new WeakMap()).set(container, containerFailures);
15351
+ }
15352
+ containerFailures.add(failureKey);
15353
+ }
15354
+ console.error(errorMessage, err);
15355
+ }
15356
+ }
14700
15357
  // We shouldn't cache rejections, we can try again later
14701
15358
  this.$ref$ = null;
14702
15359
  });
@@ -14765,7 +15422,9 @@ class QRLClass {
14765
15422
  }
14766
15423
  // If it is plain value with deserialized or missing captures, resolve it immediately
14767
15424
  // Otherwise we keep using the async path so we can wait for qrls to load
14768
- if ($lazy$.$ref$ != null && typeof this.$captures$ !== 'string' && !isPromise($lazy$.$ref$)) {
15425
+ if ($lazy$.$ref$ != null &&
15426
+ (!this.$captures$ || typeof this.$captures$ !== 'string') &&
15427
+ !isPromise($lazy$.$ref$)) {
14769
15428
  this.resolved = bindCaptures(this, $lazy$.$ref$);
14770
15429
  }
14771
15430
  }
@@ -14787,6 +15446,10 @@ const qrlWithCaptures = function (captures) {
14787
15446
  const newQrl = new QRLClass(qrl.$lazy$, captures, qrl.$captures$ ? qrl.$container$ : undefined);
14788
15447
  return makeQrlFn(newQrl);
14789
15448
  };
15449
+ const qrlWithMovedCaptures = function () {
15450
+ this[QRL_STATE].$lazy$.$hasMovedCaptures$ = true;
15451
+ return this;
15452
+ };
14790
15453
  const qrlSetRef = function (ref) {
14791
15454
  const qrl = getInstance(this);
14792
15455
  qrl.$lazy$.$setRef$(ref);
@@ -14842,6 +15505,11 @@ const QRL_FUNCTION_PROTO = Object.create(Function.prototype, {
14842
15505
  this[QRL_STATE].$captures$ = value;
14843
15506
  },
14844
15507
  },
15508
+ $hasMovedCaptures$: {
15509
+ get() {
15510
+ return this[QRL_STATE].$lazy$.$hasMovedCaptures$;
15511
+ },
15512
+ },
14845
15513
  $container$: {
14846
15514
  get() {
14847
15515
  return this[QRL_STATE].$container$;
@@ -14890,6 +15558,9 @@ const QRL_FUNCTION_PROTO = Object.create(Function.prototype, {
14890
15558
  w: {
14891
15559
  value: qrlWithCaptures,
14892
15560
  },
15561
+ m: {
15562
+ value: qrlWithMovedCaptures,
15563
+ },
14893
15564
  s: {
14894
15565
  value: qrlSetRef,
14895
15566
  },
@@ -14920,31 +15591,50 @@ let _captures = null;
14920
15591
  const setCaptures = (captures) => {
14921
15592
  _captures = captures;
14922
15593
  };
14923
- const deserializeCaptures = (container, captures) => {
15594
+ const deserializeCaptureDeltas = (container, deltaString, startIndex = 0, previousRootId) => {
14924
15595
  const refs = [];
14925
- const captureIds = captures.split(' ');
14926
- for (let i = 0; i < captureIds.length; i++) {
14927
- const id = captureIds[i];
14928
- refs.push(container.$getObjectById$(id));
15596
+ let previous = previousRootId;
15597
+ let start = startIndex;
15598
+ for (let i = startIndex; i <= deltaString.length; i++) {
15599
+ if (i === deltaString.length || deltaString.charCodeAt(i) === 32) {
15600
+ if (i > start) {
15601
+ const delta = Number(deltaString.slice(start, i));
15602
+ previous = previous === undefined ? delta : previous + delta;
15603
+ refs.push(container.$getObjectById$(previous));
15604
+ }
15605
+ start = i + 1;
15606
+ }
14929
15607
  }
14930
15608
  return refs;
14931
15609
  };
15610
+ const deserializeQrlCaptureDeltas = (container, qrlString) => {
15611
+ const firstHash = qrlString.indexOf('#');
15612
+ if (firstHash === -1) {
15613
+ return deserializeCaptureDeltas(container, qrlString);
15614
+ }
15615
+ const secondHash = qrlString.indexOf('#', firstHash + 1);
15616
+ const previousRootId = Number(qrlString.slice(0, firstHash)) + Number(qrlString.slice(firstHash + 1, secondHash));
15617
+ return deserializeCaptureDeltas(container, qrlString, secondHash + 1, previousRootId);
15618
+ };
14932
15619
  /** Puts the qrl captures into `_captures`, and returns a Promise that should be awaited if possible */
14933
15620
  const ensureQrlCaptures = (qrl) => {
14934
15621
  // We read the captures once, synchronously, so no need to keep previous
14935
- _captures = qrl.$captures$;
15622
+ const serializedCaptures = qrl.$captures$;
14936
15623
  const container = qrl.$container$;
14937
- if (typeof _captures === 'string') {
15624
+ if (typeof serializedCaptures === 'string') {
14938
15625
  if (!container) {
14939
15626
  throw qError(13 /* QError.qrlMissingContainer */);
14940
15627
  }
14941
15628
  const prevLoading = loading;
14942
- _captures = qrl.$captures$ = deserializeCaptures(container, _captures);
15629
+ _captures = qrl.$captures$ = deserializeQrlCaptureDeltas(container, serializedCaptures);
14943
15630
  if (loading !== prevLoading) {
14944
15631
  // return the loading promise so callers can await it
14945
15632
  return loading;
14946
15633
  }
14947
15634
  }
15635
+ else {
15636
+ _captures = serializedCaptures || null;
15637
+ }
14948
15638
  };
14949
15639
  // Wrap functions to provide their lexical scope
14950
15640
  const bindCaptures = (qrl, ref) => {
@@ -15911,7 +16601,13 @@ const useComputedQrl = (qrl, options) => {
15911
16601
  * recalculated, and if the result changed, all tasks which are tracking the signal will be re-run
15912
16602
  * and all components that read the signal will be re-rendered.
15913
16603
  *
15914
- * The function must be synchronous and must not have any side effects.
16604
+ * Every synchronous signal or store read is tracked automatically. Reads after an `await` are not:
16605
+ * the tracking context is lost, so track them explicitly with the `track()` provided on the context
16606
+ * argument. When the function is async, the returned signal exposes the async API: reading an
16607
+ * unresolved `.value` throws the computation promise, and `.pending` and `.error` expose the
16608
+ * computation state.
16609
+ *
16610
+ * The function must not have any side effects.
15915
16611
  *
15916
16612
  * @public
15917
16613
  */
@@ -16009,7 +16705,7 @@ const useVisibleTaskQrl = (qrl, opts) => {
16009
16705
  // In SSR we defer execution until triggered in DOM
16010
16706
  flags = 1 /* TaskFlags.VISIBLE_TASK */;
16011
16707
  }
16012
- const task = new Task(flags, i, iCtx.$hostElement$, qrl, undefined, null);
16708
+ const task = new Task(flags, i, iCtx.$hostElement$, qrl, null);
16013
16709
  set(task);
16014
16710
  useRegisterTaskEvents(task, eagerness);
16015
16711
  };
@@ -16047,11 +16743,11 @@ const getTaskHandlerQrl = (task) => {
16047
16743
  * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
16048
16744
  * re-throw it (or a new Error), the resource status will never be `rejected`.
16049
16745
  *
16050
- * @deprecated Use `useAsync$` instead, which is more powerful and flexible. `useResource$` is still
16051
- * available for backward compatibility but it is recommended to migrate to `useAsync$` for new
16052
- * code and when updating existing code.
16746
+ * @deprecated Use `useComputed$` instead, which is more powerful and flexible. `useResource$` is
16747
+ * still available for backward compatibility but it is recommended to migrate to `useComputed$`
16748
+ * for new code and when updating existing code.
16053
16749
  * @public
16054
- * @see useAsync$
16750
+ * @see useComputed$
16055
16751
  * @see Resource
16056
16752
  * @see ResourceReturn
16057
16753
  */
@@ -16156,7 +16852,10 @@ const creator = (qrl, options) => {
16156
16852
  qrl.resolve();
16157
16853
  return createAsyncSignal(qrl, options);
16158
16854
  };
16159
- /** @internal */
16855
+ /**
16856
+ * @deprecated Use `useComputed$` instead, it has async support now.
16857
+ * @internal
16858
+ */
16160
16859
  const useAsyncQrl = (qrl, options) => {
16161
16860
  return useConstant((creator), qrl, options);
16162
16861
  };
@@ -16179,6 +16878,8 @@ const useAsyncQrl = (qrl, options) => {
16179
16878
  * will subscribe to it and return the last resolved value until the new value is ready. As soon as
16180
16879
  * the new value is ready, the subscribers will be updated.
16181
16880
  *
16881
+ * @deprecated Use `useComputed$` instead, it has async support now. It auto-tracks synchronous
16882
+ * reads; after the first `await`, use the provided `track()` like before.
16182
16883
  * @public
16183
16884
  */
16184
16885
  const useAsync$ = implicit$FirstArg(useAsyncQrl);
@@ -16197,10 +16898,10 @@ const eachCmpTask = async ({ track }) => {
16197
16898
  const context = tryGetInvokeContext();
16198
16899
  const host = context.$hostElement$;
16199
16900
  const container = context.$container$;
16200
- markVNodeDirty(container, host, 256 /* ChoreBits.RECONCILE */);
16901
+ const renderPromise = markVNodeDirty(container, host, 256 /* ChoreBits.RECONCILE */);
16201
16902
  const isSsr = qTest ? isServerPlatform() : isServer;
16202
16903
  if (isSsr) {
16203
- await container.$renderPromise$;
16904
+ await renderPromise;
16204
16905
  }
16205
16906
  };
16206
16907
  /** @internal */
@@ -16215,6 +16916,46 @@ const eachCmp = (props) => {
16215
16916
  const Each = /*#__PURE__*/ componentQrl(
16216
16917
  /*#__PURE__*/ inlinedQrl(eachCmp, '_eaC'));
16217
16918
 
16919
+ const invokeShowFn = (fn, context, arg) => {
16920
+ return isQrl(fn) ? fn.getFn(context)(arg) : fn(arg);
16921
+ };
16922
+ /** @internal */
16923
+ const showCmpTask = ({ track }) => {
16924
+ const props = _captures[0];
16925
+ const context = tryGetInvokeContext();
16926
+ const host = context.$hostElement$;
16927
+ const container = context.$container$;
16928
+ const isSsr = qTest ? isServerPlatform() : isServer;
16929
+ return maybeThen(track(() => invokeShowFn(props.when$, tryGetInvokeContext(), undefined)), (condition) => {
16930
+ const branch = condition ? props.then$ : props.else$;
16931
+ return maybeThen(branch ? invokeShowFn(branch, context, condition) : null, (output) => {
16932
+ const jsx = branch ? [output] : [];
16933
+ if (isSsr) {
16934
+ const ssr = container;
16935
+ return ssr.renderJSX(jsx, {
16936
+ currentStyleScoped: null,
16937
+ parentComponentFrame: ssr.getComponentFrame(0),
16938
+ });
16939
+ }
16940
+ else {
16941
+ setNodeDiffPayload(host, jsx);
16942
+ markVNodeDirty(container, host, 2 /* ChoreBits.NODE_DIFF */);
16943
+ }
16944
+ });
16945
+ });
16946
+ };
16947
+ /** @internal */
16948
+ const showCmp = (props) => {
16949
+ if (!__EXPERIMENTAL__.show) {
16950
+ throw new Error('Show is experimental and must be enabled with `experimental: ["show"]` in the `qwikVite` plugin.');
16951
+ }
16952
+ useTaskQrl(/*#__PURE__*/ inlinedQrl(showCmpTask, '_shT', [props]));
16953
+ return SkipRender;
16954
+ };
16955
+ /** @public @experimental */
16956
+ const Show = /*#__PURE__*/ componentQrl(
16957
+ /*#__PURE__*/ inlinedQrl(showCmp, '_shC'));
16958
+
16218
16959
  const RevealContext = /*#__PURE__*/ createContextId('qk-reveal');
16219
16960
  const createRevealContext = (props) => {
16220
16961
  return {
@@ -16319,19 +17060,19 @@ const _hf0 = (p0, p1, p2, p3) => ({
16319
17060
  display: p1.value === 'fallback' &&
16320
17061
  p0.fallback != null &&
16321
17062
  p0.fallback !== false &&
16322
- (p2 === null || p2.value || !p3.reveal.collapsed)
17063
+ (p2.value || !p3.reveal.collapsed)
16323
17064
  ? 'contents'
16324
17065
  : 'none',
16325
17066
  });
16326
- const _hf0_str = '{display:p1.value==="fallback"&&p0.fallback!=null&&p0.fallback!==false&&(p2===null||p2.value||!p3.reveal.collapsed)?"contents":"none"}';
16327
- const _hf1 = (p0, p1, p2) => ({
16328
- display: (p1.value === 'content' || p0.showStale) && (p2 === null || p2.value) ? 'contents' : 'none',
17067
+ const _hf0_str = '{display:p1.value==="fallback"&&p0.fallback!=null&&p0.fallback!==false&&(p2.value||!p3.reveal.collapsed)?"contents":"none"}';
17068
+ const _hf1 = (p0, p1) => ({
17069
+ display: p0.value === 'content' && p1.value ? 'contents' : 'none',
16329
17070
  });
16330
- const _hf1_str = '{display:(p1.value==="content"||p0.showStale)&&(p2===null||p2.value)?"contents":"none"}';
17071
+ const _hf1_str = '{display:p0.value==="content"&&p1.value?"contents":"none"}';
16331
17072
  /** @internal */
16332
17073
  const suspenseTask = ({ track, cleanup }) => {
16333
17074
  const cursorBoundary = _captures[0], props = _captures[1], state = _captures[2], revealRegistration = _captures[3];
16334
- const pendingCount = track(cursorBoundary.pending);
17075
+ const pendingCount = track(cursorBoundary);
16335
17076
  const isBrowserEnv = qTest ? !isServerPlatform() : isBrowser$1;
16336
17077
  if (revealRegistration !== null && isBrowserEnv) {
16337
17078
  revealRegistration.reveal.version.value++;
@@ -16341,7 +17082,7 @@ const suspenseTask = ({ track, cleanup }) => {
16341
17082
  return;
16342
17083
  }
16343
17084
  const delayTimer = setTimeout(() => {
16344
- if (cursorBoundary.pending.value > 0) {
17085
+ if (cursorBoundary.value > 0) {
16345
17086
  state.value = 'fallback';
16346
17087
  }
16347
17088
  }, props.delay ?? 0);
@@ -16370,19 +17111,17 @@ const suspenseCmp = (props) => {
16370
17111
  const outOfOrderRevealBoundary = isServerOutOfOrder
16371
17112
  ? (revealRegistration?.reveal.ooos?.register(revealRegistration) ?? null)
16372
17113
  : null;
16373
- const showOutOfOrderFallback = isServerOutOfOrder && shouldRenderFallback(props.fallback, outOfOrderRevealBoundary);
17114
+ const showOutOfOrderFallback = isServerOutOfOrder &&
17115
+ untrack(() => props.fallback != null && props.fallback !== false) &&
17116
+ (outOfOrderRevealBoundary === null || outOfOrderRevealBoundary.showFallback);
16374
17117
  const outOfOrderBoundaryState = showOutOfOrderFallback && isPositiveDelay(props.delay)
16375
- ? { contentResolved: false, delayTimer: null }
17118
+ ? { contentResolved: false, delay: props.delay, delayTimer: null }
16376
17119
  : null;
16377
- const outOfOrderFallbackStyle = isServerOutOfOrder
16378
- ? /*#__PURE__*/ _fnSignal(_hf0, [props, state, canReveal, revealRegistration], _hf0_str)
16379
- : null;
16380
- const contentStyle = /*#__PURE__*/ _fnSignal(_hf1, [props, state, canReveal], _hf1_str);
17120
+ const contentStyle = /*#__PURE__*/ _fnSignal(_hf1, [state, canReveal], _hf1_str);
16381
17121
  const fallbackHost = (isServerOutOfOrder
16382
17122
  ? /*#__PURE__*/ _jsxSorted(SSRFallback, {
16383
17123
  boundary: outOfOrderBoundaryState,
16384
- delay: props.delay,
16385
- fallbackStyle: outOfOrderFallbackStyle,
17124
+ fallbackStyle: _fnSignal(_hf0, [props, state, canReveal, revealRegistration], _hf0_str),
16386
17125
  showFallback: showOutOfOrderFallback,
16387
17126
  state,
16388
17127
  }, null, _wrapProp(props, 'fallback'), 1, null)
@@ -16418,15 +17157,16 @@ const Suspense = /*#__PURE__*/ componentQrl(
16418
17157
  const SSRFallback = __EXPERIMENTAL__.suspense
16419
17158
  ? /*#__PURE__*/ createInternalServerComponent((ssr, jsx, _options, enqueue) => {
16420
17159
  const boundaryState = jsx.varProps.boundary;
16421
- const delay = jsx.varProps.delay;
16422
17160
  const fallbackStyle = jsx.varProps.fallbackStyle;
16423
17161
  const showFallback = jsx.varProps.showFallback === true;
16424
17162
  const state = jsx.varProps.state;
16425
- if (showFallback && !isPositiveDelay(delay)) {
16426
- state.value = 'fallback';
16427
- }
16428
- else if (boundaryState && showFallback && isPositiveDelay(delay)) {
16429
- enqueue(() => scheduleOutOfOrderFallbackDelay(ssr, boundaryState, state, delay));
17163
+ if (showFallback) {
17164
+ if (boundaryState) {
17165
+ enqueue(() => scheduleOutOfOrderFallbackDelay(ssr, boundaryState, state));
17166
+ }
17167
+ else {
17168
+ state.value = 'fallback';
17169
+ }
16430
17170
  }
16431
17171
  enqueue(
16432
17172
  /*#__PURE__*/ _jsxSorted('div', {
@@ -16436,13 +17176,13 @@ const SSRFallback = __EXPERIMENTAL__.suspense
16436
17176
  : null;
16437
17177
  const SSRDeferredSlot = __EXPERIMENTAL__.suspense
16438
17178
  ? /*#__PURE__*/ createInternalServerComponent(async (ssr, jsx, options) => {
16439
- const boundaryId = jsx.varProps.boundaryId ?? ssr.nextOutOfOrderId();
17179
+ const boundaryId = jsx.varProps.boundaryId;
16440
17180
  const contentSegment = `${boundaryId}`;
16441
17181
  const boundaryState = jsx.varProps.boundary;
16442
17182
  const contentStyle = jsx.varProps.contentStyle;
16443
17183
  const revealBoundary = jsx.varProps.reveal;
16444
17184
  const content = ssr.segment(contentSegment, createClaimedDeferredSlot(ssr, jsx, options), options);
16445
- writeOutOfOrderPlaceholder(ssr, boundaryId);
17185
+ ssr.write(`<template ${QSuspenseResolved}="${boundaryId}"></template>`);
16446
17186
  ssr.emitOutOfOrderExecutorIfNeeded();
16447
17187
  ssr.queueOutOfOrderSegment(content.then((rendered) => emitRenderedOutOfOrderSegment(ssr, boundaryId, contentSegment, rendered, contentStyle, revealBoundary, boundaryState)));
16448
17188
  })
@@ -16461,29 +17201,27 @@ function createClaimedDeferredSlot(ssr, jsx, options) {
16461
17201
  return /*#__PURE__*/ _jsxSorted(Slot, jsx.varProps, jsx.constProps, slotChildren, jsx.flags, jsx.key);
16462
17202
  }
16463
17203
  async function emitRenderedOutOfOrderSegment(ssr, boundaryId, segmentId, rendered, contentStyle, revealBoundary, boundaryState) {
16464
- markOutOfOrderContentResolved(boundaryState);
17204
+ if (boundaryState) {
17205
+ boundaryState.contentResolved = true;
17206
+ if (boundaryState.delayTimer) {
17207
+ clearTimeout(boundaryState.delayTimer);
17208
+ boundaryState.delayTimer = null;
17209
+ }
17210
+ }
16465
17211
  revealBoundary?.resolve();
16466
17212
  await ssr.$runQueuedRender$(async () => {
16467
17213
  ssr.addRoot(contentStyle);
16468
17214
  const result = await rendered.container.$finalizeOutOfOrderSegment$(segmentId, rendered);
16469
- writeOutOfOrderResolvedTemplate(ssr, boundaryId, result.html, revealBoundary);
17215
+ ssr.write(`<template ${QSuspenseResolved}="${boundaryId}"${revealBoundary?.attrs ?? ''}>`);
17216
+ ssr.write(result.html);
17217
+ ssr.write('</template>');
16470
17218
  ssr.emitOutOfOrderSegmentScripts(result.scripts);
16471
17219
  ssr.emitInlineScript(`qO(${boundaryId})`);
16472
17220
  // qO() is the browser-visible handoff for this segment, so flush it immediately.
16473
17221
  await ssr.streamHandler.flush();
16474
17222
  });
16475
17223
  }
16476
- function markOutOfOrderContentResolved(boundaryState) {
16477
- if (!boundaryState) {
16478
- return;
16479
- }
16480
- boundaryState.contentResolved = true;
16481
- if (boundaryState.delayTimer) {
16482
- clearTimeout(boundaryState.delayTimer);
16483
- boundaryState.delayTimer = null;
16484
- }
16485
- }
16486
- function scheduleOutOfOrderFallbackDelay(ssr, boundaryState, state, delay) {
17224
+ function scheduleOutOfOrderFallbackDelay(ssr, boundaryState, state) {
16487
17225
  boundaryState.delayTimer = setTimeout(() => {
16488
17226
  boundaryState.delayTimer = null;
16489
17227
  void ssr.$runQueuedRender$(async () => {
@@ -16494,24 +17232,11 @@ function scheduleOutOfOrderFallbackDelay(ssr, boundaryState, state, delay) {
16494
17232
  ssr.emitBackpatchDataAndExecutorIfNeeded();
16495
17233
  await ssr.streamHandler.flush();
16496
17234
  });
16497
- }, delay);
17235
+ }, boundaryState.delay);
16498
17236
  }
16499
17237
  function isPositiveDelay(delay) {
16500
17238
  return typeof delay === 'number' && Number.isFinite(delay) && delay > 0;
16501
17239
  }
16502
- function shouldRenderFallback(fallback, revealBoundary) {
16503
- return (fallback != null &&
16504
- fallback !== false &&
16505
- (revealBoundary === null || revealBoundary.showFallback));
16506
- }
16507
- function writeOutOfOrderPlaceholder(ssr, boundaryId) {
16508
- ssr.write(`<template ${QSuspenseResolved}="${boundaryId}"></template>`);
16509
- }
16510
- function writeOutOfOrderResolvedTemplate(ssr, boundaryId, html, revealBoundary) {
16511
- ssr.write(`<template ${QSuspenseResolved}="${boundaryId}"${revealBoundary?.attrs ?? ''}>`);
16512
- ssr.write(html);
16513
- ssr.write('</template>');
16514
- }
16515
17240
 
16516
17241
  // keep this import from core/build so the cjs build works
16517
17242
  /**
@@ -16608,5 +17333,5 @@ if (import.meta.hot) {
16608
17333
  });
16609
17334
  }
16610
17335
 
16611
- export { $, Each, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, Reveal, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, Suspense, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, ELEMENT_SEQ as _ELEMENT_SEQ, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, SubscriptionPatch as _SubscriptionPatch, Task as _Task, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, _createDeserializeContainer, createQRL as _createQRL, createStore as _createStore, delay as _delay, _deserialize, _dumpState, eachCmp as _eaC, eachCmpTask as _eaT, _executeSsrChores, _fnSignal, getAsyncLocalStorage as _getAsyncLocalStorage, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, getSubscriber as _getSubscriber, _getVarProps, _hasStoreEffects, _hmr, _injectAsyncSignalValue, invoke as _invoke, isJSXNode as _isJSXNode, isStore as _isStore, isStringifiable as _isStringifiable, isTask as _isTask, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, mapApp_findIndx as _mapApp_findIndx, mapArray_get as _mapArray_get, mapArray_set as _mapArray_set, newInvokeContext as _newInvokeContext, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, qrlToString as _qrlToString, _qrlWithChunk, _qrlWithChunkDEV, revealCmp as _reC, revealCanReveal as _reR, revealCleanupTask as _reT, _regSymbol, _removeProjection, _res, _resolveContextWithoutSequentialScope, _restProps, retryOnPromise as _retryOnPromise, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, suspenseCmp as _suC, suspenseTask as _suT, scheduleTask as _task, _updateProjectionProps, _useHmr, _val, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getElementName as _vnode_getElementName, vnode_getFirstChild as _vnode_getFirstChild, vnode_getProp as _vnode_getProp, vnode_getVNodeForChildNode as _vnode_getVNodeForChildNode, vnode_insertBefore as _vnode_insertBefore, vnode_isElementVNode as _vnode_isElementVNode, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_newVirtual as _vnode_newVirtual, vnode_remove as _vnode_remove, vnode_setProp as _vnode_setProp, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _wrapProp, _wrapSignal, component$, componentQrl, createAsync$, createAsyncSignal as createAsyncQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, forceStoreEffects, getClientManifest, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsync$, useAsyncQrl, 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 };
17336
+ export { $, Each, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, Reveal, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, Show, SkipRender, Slot, Suspense, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, ELEMENT_SEQ as _ELEMENT_SEQ, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, SubscriptionPatch as _SubscriptionPatch, Task as _Task, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, _createDeserializeContainer, createQRL as _createQRL, createStore as _createStore, delay as _delay, _deserialize, _dumpState, eachCmp as _eaC, eachCmpTask as _eaT, _executeSsrChores, _fnSignal, getAsyncLocalStorage as _getAsyncLocalStorage, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, getSubscriber as _getSubscriber, _getVarProps, _hasStoreEffects, _hmr, _injectAsyncSignalValue, invoke as _invoke, isJSXNode as _isJSXNode, isStore as _isStore, isStringifiable as _isStringifiable, isTask as _isTask, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, mapApp_findIndx as _mapApp_findIndx, mapArray_get as _mapArray_get, mapArray_set as _mapArray_set, _markSignalAsExternallyOwned, newInvokeContext as _newInvokeContext, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, qrlToString as _qrlToString, _qrlWithChunk, _qrlWithChunkDEV, revealCmp as _reC, revealCanReveal as _reR, revealCleanupTask as _reT, _regSymbol, _removeProjection, _res, _resolveContextWithoutSequentialScope, _restProps, retryOnPromise as _retryOnPromise, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, showCmp as _shC, showCmpTask as _shT, suspenseCmp as _suC, suspenseTask as _suT, scheduleTask as _task, _updateProjectionProps, _useHmr, _val, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getElementName as _vnode_getElementName, vnode_getFirstChild as _vnode_getFirstChild, vnode_getProp as _vnode_getProp, vnode_getVNodeForChildNode as _vnode_getVNodeForChildNode, vnode_insertBefore as _vnode_insertBefore, vnode_isElementVNode as _vnode_isElementVNode, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_newVirtual as _vnode_newVirtual, vnode_remove as _vnode_remove, vnode_setProp as _vnode_setProp, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _wrapProp, _wrapSignal, component$, componentQrl, createAsync$, createAsyncSignal as createAsyncQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, forceStoreEffects, getClientManifest, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsync$, useAsyncQrl, 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 };
16612
17337
  //# sourceMappingURL=core.mjs.map