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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * @qwik.dev/core 2.0.0-beta.37-dev+82dc74d
3
+ * @qwik.dev/core 2.0.0-beta.38-dev+0339f17
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) {
@@ -428,6 +431,7 @@ const QBaseAttr = 'q:base';
428
431
  const QLocaleAttr = 'q:locale';
429
432
  const QManifestHashAttr = 'q:manifest-hash';
430
433
  const QInstanceAttr = 'q:instance';
434
+ const QStatePrewarmAttr = 'q:prewarm';
431
435
  const QContainerIsland = 'q:container-island';
432
436
  const QContainerIslandEnd = '/' + QContainerIsland;
433
437
  const QIgnore = 'q:ignore';
@@ -842,8 +846,21 @@ const createMacroTask = (fn) => {
842
846
  let macroTask;
843
847
  if (typeof MessageChannel !== 'undefined') {
844
848
  const channel = new MessageChannel();
849
+ let active = true;
845
850
  channel.port1.onmessage = () => fn();
846
- macroTask = () => channel.port2.postMessage(null);
851
+ macroTask = () => {
852
+ if (active) {
853
+ channel.port2.postMessage(null);
854
+ }
855
+ };
856
+ macroTask.$destroy$ = () => {
857
+ if (active) {
858
+ active = false;
859
+ channel.port1.onmessage = null;
860
+ channel.port1.close();
861
+ channel.port2.close();
862
+ }
863
+ };
847
864
  }
848
865
  else {
849
866
  macroTask = () => setTimeout(fn);
@@ -963,7 +980,7 @@ const rel = isBrowser && doc.createElement('link').relList?.supports?.('modulepr
963
980
  ? 'modulePreload'
964
981
  : 'preload';
965
982
  const isJSRegex = /\.[mc]?js$/;
966
- const yieldInterval = 1000 / 60;
983
+ const yieldInterval = 10;
967
984
 
968
985
  const BundleImportState_None = 0;
969
986
  const BundleImportState_Preload = 2;
@@ -1219,6 +1236,8 @@ const writeStringRootRef = (writer, id) => writer.write(String(id));
1219
1236
  /** @internal */
1220
1237
  const writeStringRootRefPath = (writer, path) => writer.write(stringifyRootRefPath(path));
1221
1238
  /** @internal */
1239
+ const writeStringRootRefDelta = (writer, id, base) => writer.write(String(id - base));
1240
+ /** @internal */
1222
1241
  const createStringStreamWriter = (write) => ({
1223
1242
  write,
1224
1243
  writeRootRef(id) {
@@ -1227,6 +1246,9 @@ const createStringStreamWriter = (write) => ({
1227
1246
  writeRootRefPath(path) {
1228
1247
  return writeStringRootRefPath(this, path);
1229
1248
  },
1249
+ writeRootRefDelta(id, base) {
1250
+ return writeStringRootRefDelta(this, id, base);
1251
+ },
1230
1252
  });
1231
1253
 
1232
1254
  // Pre-allocated common strings to reduce allocation overhead
@@ -1242,7 +1264,7 @@ const COMMA = ',';
1242
1264
  *
1243
1265
  * @public
1244
1266
  */
1245
- const version = "2.0.0-beta.37-dev+82dc74d";
1267
+ const version = "2.0.0-beta.38-dev+0339f17";
1246
1268
 
1247
1269
  const isNode = (value) => {
1248
1270
  return value && typeof value.nodeType === 'number';
@@ -1698,9 +1720,12 @@ function setCursorPosition(container, cursorData, position) {
1698
1720
  }
1699
1721
  }
1700
1722
  function mergeCursors(container, newCursorData, oldCursor) {
1723
+ const oldCursorData = getCursorData(oldCursor);
1724
+ if (!oldCursorData || oldCursorData === newCursorData) {
1725
+ return;
1726
+ }
1701
1727
  // delete from global cursors queue
1702
1728
  removeCursorFromQueue(oldCursor, container);
1703
- const oldCursorData = getCursorData(oldCursor);
1704
1729
  mergeCursorData(newCursorData, oldCursorData);
1705
1730
  }
1706
1731
  function mergeCursorData(newCursorData, oldCursorData) {
@@ -1778,6 +1803,234 @@ function setCursorData(vNode, cursorData) {
1778
1803
  cursorDatas.set(vNode, cursorData);
1779
1804
  }
1780
1805
 
1806
+ /** @internal */
1807
+ const _CONST_PROPS = Symbol('CONST');
1808
+ /** @internal */
1809
+ const _VAR_PROPS = Symbol('VAR');
1810
+ /** @internal */
1811
+ const _OWNER = Symbol('OWNER');
1812
+ /** @internal */
1813
+ const _PROPS_HANDLER = Symbol('PROPS_HANDLER');
1814
+ /** @internal @deprecated v1 compat */
1815
+ const _IMMUTABLE = Symbol('IMMUTABLE');
1816
+ /** @internal */
1817
+ const _UNINITIALIZED = Symbol('UNINITIALIZED');
1818
+
1819
+ /** @internal */
1820
+ const EMPTY_ARRAY = [];
1821
+ /** @internal */
1822
+ const EMPTY_OBJ = {};
1823
+ Object.freeze(EMPTY_ARRAY);
1824
+ Object.freeze(EMPTY_OBJ);
1825
+
1826
+ function createPropsProxy(owner) {
1827
+ // TODO don't make a proxy but populate getters? benchmark
1828
+ return new Proxy({}, new PropsProxyHandler(owner));
1829
+ }
1830
+ class PropsProxyHandler {
1831
+ owner;
1832
+ $effects$ = undefined;
1833
+ $container$ = null;
1834
+ constructor(owner) {
1835
+ this.owner = owner;
1836
+ }
1837
+ get(_, prop) {
1838
+ // escape hatch to get the separated props from a component
1839
+ if (prop === _CONST_PROPS) {
1840
+ return this.owner.constProps;
1841
+ }
1842
+ else if (prop === _VAR_PROPS) {
1843
+ return this.owner.varProps;
1844
+ }
1845
+ else if (prop === _OWNER) {
1846
+ return this.owner;
1847
+ }
1848
+ else if (prop === _PROPS_HANDLER) {
1849
+ return this;
1850
+ }
1851
+ let value;
1852
+ if (prop === 'children') {
1853
+ value = this.owner.children;
1854
+ }
1855
+ else {
1856
+ if (typeof prop === 'string' && typeof this.owner.type === 'string') {
1857
+ const attr = jsxEventToHtmlAttribute(prop);
1858
+ if (attr) {
1859
+ prop = attr;
1860
+ }
1861
+ }
1862
+ value = directGetPropsProxyProp(this.owner, prop);
1863
+ if (prop in this.owner.varProps) {
1864
+ addPropsProxyEffect(this, prop);
1865
+ }
1866
+ }
1867
+ // a proxied value that the optimizer made
1868
+ return value instanceof WrappedSignalImpl ? value.value : value;
1869
+ }
1870
+ set(_, prop, value) {
1871
+ if (prop === _OWNER) {
1872
+ // used for deserialization
1873
+ this.owner = value;
1874
+ }
1875
+ else if (prop === 'children') {
1876
+ this.owner.children = value;
1877
+ }
1878
+ else if (prop === _CONST_PROPS) {
1879
+ this.owner.constProps = value;
1880
+ }
1881
+ else if (prop === _VAR_PROPS) {
1882
+ this.owner.varProps = value;
1883
+ }
1884
+ else {
1885
+ if (typeof prop === 'string' && typeof this.owner.type === 'string') {
1886
+ const attr = jsxEventToHtmlAttribute(prop);
1887
+ if (attr) {
1888
+ prop = attr;
1889
+ }
1890
+ }
1891
+ if (this.owner.constProps && prop in this.owner.constProps) {
1892
+ // delete the prop from the const props first
1893
+ delete this.owner.constProps[prop];
1894
+ }
1895
+ if (this.owner.varProps === EMPTY_OBJ) {
1896
+ this.owner.varProps = {};
1897
+ }
1898
+ else if (!(prop in this.owner.varProps)) {
1899
+ this.owner.toSort = true;
1900
+ }
1901
+ if (this.owner.varProps[prop] !== value) {
1902
+ this.owner.varProps[prop] = value;
1903
+ triggerPropsProxyEffect(this, prop);
1904
+ }
1905
+ }
1906
+ return true;
1907
+ }
1908
+ deleteProperty(_, prop) {
1909
+ let didDelete = delete this.owner.varProps[prop];
1910
+ if (didDelete) {
1911
+ triggerPropsProxyEffect(this, prop);
1912
+ }
1913
+ if (this.owner.constProps) {
1914
+ didDelete = delete this.owner.constProps[prop] || didDelete;
1915
+ }
1916
+ if (this.owner.children != null && prop === 'children') {
1917
+ this.owner.children = null;
1918
+ didDelete = true;
1919
+ }
1920
+ return didDelete;
1921
+ }
1922
+ has(_, prop) {
1923
+ if (prop === 'children') {
1924
+ return this.owner.children != null;
1925
+ }
1926
+ else if (prop === _CONST_PROPS || prop === _VAR_PROPS) {
1927
+ return true;
1928
+ }
1929
+ const inVarProps = prop in this.owner.varProps;
1930
+ if (typeof prop === 'string') {
1931
+ if (inVarProps) {
1932
+ addPropsProxyEffect(this, prop);
1933
+ }
1934
+ if (typeof this.owner.type === 'string') {
1935
+ const attr = jsxEventToHtmlAttribute(prop);
1936
+ if (attr) {
1937
+ prop = attr;
1938
+ }
1939
+ }
1940
+ }
1941
+ return inVarProps || (this.owner.constProps ? prop in this.owner.constProps : false);
1942
+ }
1943
+ getOwnPropertyDescriptor(_, p) {
1944
+ const value = p === 'children'
1945
+ ? this.owner.children
1946
+ : this.owner.constProps && p in this.owner.constProps
1947
+ ? this.owner.constProps[p]
1948
+ : this.owner.varProps[p];
1949
+ return {
1950
+ configurable: true,
1951
+ enumerable: true,
1952
+ value: value,
1953
+ };
1954
+ }
1955
+ ownKeys() {
1956
+ const out = Object.keys(this.owner.varProps);
1957
+ if (this.owner.children != null) {
1958
+ out.push('children');
1959
+ }
1960
+ if (this.owner.constProps) {
1961
+ for (const key in this.owner.constProps) {
1962
+ if (out.indexOf(key) === -1) {
1963
+ out.push(key);
1964
+ }
1965
+ }
1966
+ }
1967
+ return out;
1968
+ }
1969
+ }
1970
+ const addPropsProxyEffect = (propsProxy, prop) => {
1971
+ // Lazily grab the container from the invoke context
1972
+ const ctx = tryGetInvokeContext();
1973
+ if (ctx) {
1974
+ if (propsProxy.$container$ === null) {
1975
+ if (ctx.$container$) {
1976
+ propsProxy.$container$ = ctx.$container$;
1977
+ }
1978
+ }
1979
+ else {
1980
+ isDev &&
1981
+ assertTrue(!ctx.$container$ || ctx.$container$ === propsProxy.$container$, 'Do not use props across containers');
1982
+ }
1983
+ }
1984
+ const effectSubscriber = ctx?.$effectSubscriber$;
1985
+ if (effectSubscriber) {
1986
+ addStoreEffect(propsProxy.owner._proxy, prop, propsProxy, effectSubscriber);
1987
+ }
1988
+ };
1989
+ const triggerPropsProxyEffect = (propsProxy, prop) => {
1990
+ const effects = getEffects$1(propsProxy.$effects$, prop);
1991
+ if (effects) {
1992
+ scheduleEffects(propsProxy.$container$, propsProxy, effects);
1993
+ }
1994
+ return !!effects;
1995
+ };
1996
+ function getEffects$1(effects, prop) {
1997
+ // TODO: Handle STORE_ALL_PROPS
1998
+ return effects?.get(prop);
1999
+ }
2000
+ /**
2001
+ * Instead of using PropsProxyHandler getter (which could create a component-level subscription).
2002
+ * Use this function to get the props directly from a const or var props.
2003
+ *
2004
+ * This does not convert jsx event names.
2005
+ */
2006
+ const directGetPropsProxyProp = (jsx, prop) => {
2007
+ return (jsx.constProps && prop in jsx.constProps ? jsx.constProps[prop] : jsx.varProps[prop]);
2008
+ };
2009
+ const _getProps = (props, prop) => {
2010
+ return _getVarProps(props)?.[prop] || _getConstProps(props)?.[prop] || null;
2011
+ };
2012
+ /** Used by the optimizer for spread props operations @internal */
2013
+ const _getVarProps = (props) => {
2014
+ if (!props) {
2015
+ return null;
2016
+ }
2017
+ return _VAR_PROPS in props
2018
+ ? 'children' in props
2019
+ ? { ...props[_VAR_PROPS], children: props.children }
2020
+ : props[_VAR_PROPS]
2021
+ : props;
2022
+ };
2023
+ /** Used by the optimizer for spread props operations @internal */
2024
+ const _getConstProps = (props) => {
2025
+ if (!props) {
2026
+ return null;
2027
+ }
2028
+ return _CONST_PROPS in props ? props[_CONST_PROPS] : null;
2029
+ };
2030
+ const isPropsProxy = (obj) => {
2031
+ return obj && _VAR_PROPS in obj;
2032
+ };
2033
+
1781
2034
  /**
1782
2035
  * # ================================
1783
2036
  *
@@ -2017,7 +2270,7 @@ class SignalImpl {
2017
2270
  toString() {
2018
2271
  if (isDev) {
2019
2272
  try {
2020
- return (`[${this.constructor.name}${this.$flags$ & 1 /* SignalFlags.INVALID */ ? ' INVALID' : ''} ${this.$untrackedValue$}]` +
2273
+ return (`[${this.constructor.name}${this.$flags$ & 1 /* ComputedSignalFlags.INVALID */ ? ' INVALID' : ''} ${this.$untrackedValue$}]` +
2021
2274
  (Array.from(this.$effects$ || [])
2022
2275
  .map((e) => '\n -> ' + pad(qwikDebugToString(e.consumer), ' '))
2023
2276
  .join('\n') || ''));
@@ -2035,99 +2288,11 @@ class SignalImpl {
2035
2288
  }
2036
2289
  }
2037
2290
 
2038
- const DEBUG$1 = false;
2291
+ const DEBUG = false;
2039
2292
  // 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 {
2293
+ const log = (...args) => console.log('COMPUTED SIGNAL', ...args.map(qwikDebugToString));
2294
+ /** Retains job metadata and also serves as the argument for CTX_ARG compute functions */
2295
+ class Job {
2131
2296
  $signal$;
2132
2297
  /** First holds the compute promise and then the cleanup promise */
2133
2298
  $promise$ = null;
@@ -2149,7 +2314,7 @@ class AsyncJob {
2149
2314
  /** Backward compatible cache method for resource */
2150
2315
  cache() {
2151
2316
  isDev &&
2152
- console.error('useResource cache() method does not do anything. Use `useAsync$` instead of `useResource$`, use the `expires` option for polling behavior.');
2317
+ console.error('useResource cache() method does not do anything. Use `useComputed$` instead of `useResource$`, use the `expires` option for polling behavior.');
2153
2318
  }
2154
2319
  get previous() {
2155
2320
  const val = this.$signal$.$untrackedValue$;
@@ -2164,46 +2329,57 @@ class AsyncJob {
2164
2329
  }
2165
2330
  }
2166
2331
  /**
2167
- * # ================================
2168
- *
2169
- * AsyncSignalImpl
2170
- *
2171
- * # ================================
2332
+ * A signal which is computed from other signals.
2172
2333
  *
2173
- * @internal
2334
+ * Sync compute functions resolve synchronously and every read they perform is auto-tracked. When
2335
+ * the compute function turns out to be async (it returned a promise), the signal lazily switches on
2336
+ * the async engine — jobs, `loading`, `error`, polling — giving it the same API as an AsyncSignal.
2337
+ * Auto-tracking only covers reads before the first await point (the invoke context is lost after
2338
+ * it); later reads must use the ComputeCtx `track()`.
2174
2339
  */
2175
- class AsyncSignalImpl extends ComputedSignalImpl {
2176
- $untrackedLoading$ = false;
2177
- $untrackedError$ = undefined;
2178
- $current$ = null;
2340
+ // TODO when we make core lazy async, lazy load the async engine part of this class
2341
+ class ComputedSignalImpl extends SignalImpl {
2342
+ /**
2343
+ * The compute function is stored here.
2344
+ *
2345
+ * Sync computed functions must be executed synchronously (because of this we need to eagerly
2346
+ * resolve the QRL during the mark dirty phase so that any call to it will be synchronous).
2347
+ */
2348
+ $computeQrl$;
2349
+ $flags$;
2179
2350
  [_EFFECT_BACK_REF] = undefined;
2180
- constructor(container, fn, flags = 1 /* SignalFlags.INVALID */ |
2351
+ constructor(container, fn,
2352
+ // We need a separate flag to know when the computation needs running because
2353
+ // we need the old value to know if effects need running after computation
2354
+ flags = 1 /* ComputedSignalFlags.INVALID */ |
2181
2355
  16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */, options) {
2182
- super(container, fn, flags);
2356
+ // The value is used for comparison when signals trigger, which can only happen
2357
+ // when it was calculated before. Therefore we can pass whatever we like.
2358
+ super(container || tryGetInvokeContext()?.$container$, NEEDS_COMPUTATION);
2359
+ this.$computeQrl$ = fn;
2360
+ this.$flags$ = flags;
2183
2361
  if (!options) {
2184
2362
  return;
2185
2363
  }
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
2364
+ // Eagerly evaluate an initial value without clearing INVALID, so the compute still runs
2188
2365
  const initial = options.initial;
2189
2366
  if (initial !== undefined) {
2190
- const initialValue = typeof initial === 'function' ? initial() : initial;
2191
- this.$untrackedValue$ = initialValue;
2367
+ this.$untrackedValue$ = typeof initial === 'function' ? initial() : initial;
2192
2368
  }
2193
2369
  const concurrency = options.concurrency;
2194
2370
  if (concurrency !== undefined && concurrency >= 0 && concurrency !== 1) {
2195
2371
  this.$concurrency$ = concurrency;
2196
2372
  this.$jobs$ = [];
2197
2373
  }
2198
- const timeout = options.timeout;
2199
- if (timeout) {
2200
- this.$timeoutMs$ = timeout;
2374
+ if (options.timeout) {
2375
+ this.$timeoutMs$ = options.timeout;
2201
2376
  }
2202
2377
  if (options.eagerCleanup) {
2203
2378
  this.$flags$ |= 32 /* AsyncSignalFlags.EAGER_CLEANUP */;
2204
2379
  }
2205
2380
  if (options.clientOnly) {
2206
- this.$flags$ |= 64 /* AsyncSignalFlags.CLIENT_ONLY */;
2381
+ // clientOnly can never resolve during SSR, so it must be async from the start
2382
+ this.$flags$ |= 64 /* AsyncSignalFlags.CLIENT_ONLY */ | 512 /* AsyncSignalFlags.ASYNC_MODE */;
2207
2383
  }
2208
2384
  if (options.allowStale === false) {
2209
2385
  if (isDev && initial !== undefined) {
@@ -2220,28 +2396,30 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2220
2396
  this.$flags$ |= 256 /* AsyncSignalFlags.NO_POLL */;
2221
2397
  }
2222
2398
  }
2223
- get untrackedValue() {
2224
- this.$computeIfNeeded$();
2225
- if (this.$current$?.$promise$) {
2226
- if (this.$untrackedValue$ === NEEDS_COMPUTATION || (qTest ? isServerPlatform() : isServer)) {
2227
- throw this.$current$?.$promise$;
2228
- }
2229
- return this.$untrackedValue$;
2399
+ invalidate(info) {
2400
+ if (arguments.length > 0) {
2401
+ this.$info$ = info;
2402
+ this.$infoVersion$ = this.$infoVersion$ === undefined ? 1 : this.$infoVersion$ + 1;
2230
2403
  }
2231
- if (this.$untrackedError$) {
2232
- throw this.$untrackedError$;
2404
+ if (this.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */) {
2405
+ this.$setInvalid$(true, this.$flags$ & 128 /* AsyncSignalFlags.CLEAR_ON_INVALIDATE */);
2406
+ return;
2233
2407
  }
2234
- // For clientOnly signals without initial value during SSR, throw if trying to read value
2235
- // During SSR, clientOnly signals are skipped, so there's no computed value available
2236
- if ((qTest ? isServerPlatform() : isServer) &&
2237
- this.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */ &&
2238
- this.$untrackedValue$ === NEEDS_COMPUTATION) {
2239
- throw qError(35 /* QError.asyncClientOnlyValueDuringSSR */);
2408
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
2409
+ const ctx = newInvokeContext();
2410
+ ctx.$container$ = this.$container$ || undefined;
2411
+ // @ts-expect-error it's confused about args any[] vs []
2412
+ const running = retryOnPromise(invokeApply.bind(this, ctx, this.$computeIfNeeded$));
2413
+ if (isPromise(running)) {
2414
+ running.catch((err) => {
2415
+ if (this.$container$) {
2416
+ this.$container$.handleError(err, null);
2417
+ }
2418
+ else {
2419
+ console.error('Error during computation', err);
2420
+ }
2421
+ });
2240
2422
  }
2241
- return this.$untrackedValue$;
2242
- }
2243
- set untrackedValue(value) {
2244
- this.$untrackedValue$ = value;
2245
2423
  }
2246
2424
  /**
2247
2425
  * Read the value, subscribing if in a tracking context. Triggers computation if needed.
@@ -2258,32 +2436,65 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2258
2436
  return super.value;
2259
2437
  }
2260
2438
  set value(value) {
2261
- this.$flags$ &= -2 /* SignalFlags.INVALID */;
2262
- this.untrackedLoading = false;
2439
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2263
2440
  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;
2441
+ if (this.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */) {
2442
+ this.untrackedPending = false;
2443
+ this.$info$ = undefined;
2444
+ // Prevent pending computations from overwriting this value
2445
+ if (this.$jobs$) {
2446
+ for (let i = 0; i < this.$jobs$.length; i++) {
2447
+ this.$jobs$[i].$canWrite$ = false;
2448
+ }
2269
2449
  }
2450
+ else if (this.$current$) {
2451
+ this.$current$.$canWrite$ = false;
2452
+ }
2453
+ this.$clearNextPoll$();
2454
+ super.value = value;
2455
+ this.$scheduleNextPoll$();
2456
+ return;
2270
2457
  }
2271
- else if (this.$current$) {
2272
- this.$current$.$canWrite$ = false;
2273
- }
2274
- this.$clearNextPoll$();
2275
2458
  super.value = value;
2276
- this.$scheduleNextPoll$();
2459
+ }
2460
+ get untrackedValue() {
2461
+ this.$computeIfNeeded$();
2462
+ if (this.$current$?.$promise$) {
2463
+ if (this.$untrackedValue$ === NEEDS_COMPUTATION || (qTest ? isServerPlatform() : isServer)) {
2464
+ throw this.$current$?.$promise$;
2465
+ }
2466
+ return this.$untrackedValue$;
2467
+ }
2468
+ if (this.$untrackedError$) {
2469
+ throw this.$untrackedError$;
2470
+ }
2471
+ // For clientOnly signals without initial value during SSR, throw if trying to read value
2472
+ // During SSR, clientOnly signals are skipped, so there's no computed value available
2473
+ if ((qTest ? isServerPlatform() : isServer) &&
2474
+ this.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */ &&
2475
+ this.$untrackedValue$ === NEEDS_COMPUTATION) {
2476
+ throw qError(35 /* QError.asyncClientOnlyValueDuringSSR */);
2477
+ }
2478
+ isDev &&
2479
+ !(this.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */) &&
2480
+ assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
2481
+ return this.$untrackedValue$;
2482
+ }
2483
+ set untrackedValue(value) {
2484
+ this.$untrackedValue$ = value;
2277
2485
  }
2278
2486
  /**
2279
2487
  * Loading is true if the signal is still waiting for the promise to resolve, false if the promise
2280
- * has resolved or rejected.
2488
+ * has resolved or rejected (or the compute function is synchronous).
2281
2489
  *
2282
- * Accessing .loading will trigger computation if needed, since it's often used like
2283
- * `signal.loading ? <Loading /> : signal.value`.
2490
+ * Accessing `.pending` will trigger computation if needed, since it's often used like
2491
+ *
2492
+ * ```ts
2493
+ * signal.pending ? <Loading /> : signal.value
2494
+ * ```
2284
2495
  */
2285
- get loading() {
2286
- const val = this.untrackedLoading;
2496
+ get pending() {
2497
+ const val = this.untrackedPending;
2287
2498
  const ctx = tryGetInvokeContext();
2288
2499
  if (ctx && (this.$container$ ||= ctx.$container$ || null)) {
2289
2500
  isDev &&
@@ -2297,22 +2508,41 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2297
2508
  }
2298
2509
  return val;
2299
2510
  }
2300
- set untrackedLoading(value) {
2301
- if (value !== this.$untrackedLoading$) {
2302
- this.$untrackedLoading$ = value;
2511
+ set untrackedPending(value) {
2512
+ if (value !== !!this.$untrackedPending$) {
2513
+ this.$untrackedPending$ = value;
2303
2514
  scheduleEffects(this.$container$, this, this.$loadingEffects$);
2304
2515
  }
2305
2516
  }
2306
- get untrackedLoading() {
2517
+ get untrackedPending() {
2307
2518
  // 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
2519
  this.$computeIfNeeded$();
2309
2520
  // During SSR there's no such thing as loading state, we must render complete results
2310
2521
  if ((qTest ? isServerPlatform() : isServer) && this.$current$?.$promise$) {
2311
2522
  throw this.$current$?.$promise$;
2312
2523
  }
2313
- return this.$untrackedLoading$;
2524
+ return !!this.$untrackedPending$;
2525
+ }
2526
+ /** @deprecated Use `pending` instead */
2527
+ get loading() {
2528
+ return this.pending;
2529
+ }
2530
+ /** @deprecated Use `untrackedPending` instead */
2531
+ get untrackedLoading() {
2532
+ return this.untrackedPending;
2314
2533
  }
2315
- /** The error that occurred when the signal was resolved. */
2534
+ set untrackedLoading(value) {
2535
+ this.untrackedPending = value;
2536
+ }
2537
+ /**
2538
+ * The error the compute function threw or rejected with.
2539
+ *
2540
+ * Accessing .error will trigger computation if needed, since it's often used like
2541
+ *
2542
+ * ```ts
2543
+ * signal.error ? <Failed /> : signal.value
2544
+ * ```
2545
+ */
2316
2546
  get error() {
2317
2547
  const val = this.untrackedError;
2318
2548
  const ctx = tryGetInvokeContext();
@@ -2335,6 +2565,11 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2335
2565
  }
2336
2566
  }
2337
2567
  get untrackedError() {
2568
+ this.$computeIfNeeded$();
2569
+ // During SSR we must render settled results
2570
+ if ((qTest ? isServerPlatform() : isServer) && this.$current$?.$promise$) {
2571
+ throw this.$current$?.$promise$;
2572
+ }
2338
2573
  return this.$untrackedError$;
2339
2574
  }
2340
2575
  get expires() {
@@ -2377,16 +2612,8 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2377
2612
  }
2378
2613
  this.expires = Math.abs(value);
2379
2614
  }
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;
2385
- }
2386
- this.$setInvalid$(true, this.$flags$ & 128 /* AsyncSignalFlags.CLEAR_ON_INVALIDATE */);
2387
- }
2388
2615
  $setInvalid$(allowRecalc, mustClear) {
2389
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
2616
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
2390
2617
  this.$clearNextPoll$();
2391
2618
  if (mustClear) {
2392
2619
  this.$untrackedValue$ = NEEDS_COMPUTATION;
@@ -2427,19 +2654,97 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2427
2654
  }
2428
2655
  /** Run the computation if needed */
2429
2656
  $computeIfNeeded$() {
2430
- if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
2657
+ if (!(this.$flags$ & 1 /* ComputedSignalFlags.INVALID */)) {
2658
+ return;
2659
+ }
2660
+ if (this.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */) {
2661
+ return this.$computeAsync$();
2662
+ }
2663
+ const computeQrl = this.$computeQrl$;
2664
+ throwIfQRLNotResolved(computeQrl);
2665
+ // The previous compute's cleanups run before the new compute, like the async engine
2666
+ const prevJob = this.$current$;
2667
+ if (prevJob && !prevJob.$cleanupRequested$) {
2668
+ prevJob.$cleanupRequested$ = true;
2669
+ prevJob.$abortController$?.abort();
2670
+ this.$runCleanupCallbacks$(prevJob);
2671
+ }
2672
+ const running = new Job(this, this.$info$, this.$infoVersion$);
2673
+ this.$current$ = running;
2674
+ let result;
2675
+ try {
2676
+ result = this.$invokeComputeFn$(computeQrl.resolved, running);
2677
+ }
2678
+ catch (err) {
2679
+ // A thrown promise is a retry request, not a failure
2680
+ if (isPromise(err)) {
2681
+ throw err;
2682
+ }
2683
+ if (running.$infoVersion$ === this.$infoVersion$) {
2684
+ this.$info$ = undefined;
2685
+ }
2686
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2687
+ this.$setError$(running, err);
2688
+ return;
2689
+ }
2690
+ if (isPromise(result)) {
2691
+ // The compute fn is async: hand the in-flight computation to the async engine
2692
+ this.$flags$ |= 512 /* AsyncSignalFlags.ASYNC_MODE */;
2693
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2694
+ // Consume the in-flight promise first; retries re-invoke the compute fn
2695
+ let first = result;
2696
+ const compute = () => {
2697
+ const r = first ?? this.$invokeComputeFn$(this.$computeQrl$.resolved, running);
2698
+ first = undefined;
2699
+ return r;
2700
+ };
2701
+ running.$promise$ = this.$settleComputation$(running, () => retryOnPromise(compute));
2431
2702
  return;
2432
2703
  }
2704
+ if (running.$infoVersion$ === this.$infoVersion$) {
2705
+ this.$info$ = undefined;
2706
+ }
2707
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2708
+ this.untrackedError = undefined;
2709
+ super.value = result;
2710
+ }
2711
+ /**
2712
+ * Invoke the compute fn with the ComputeCtx argument. CTX_ARG signals (useAsync$, useResource$)
2713
+ * only track via its explicit track(). Computeds auto-track every synchronous read instead; after
2714
+ * the first await they must use the explicit track() too.
2715
+ */
2716
+ $invokeComputeFn$(fn, job) {
2717
+ const computeFn = fn;
2718
+ if (this.$flags$ & 1024 /* AsyncSignalFlags.CTX_ARG */) {
2719
+ // Strip the ambient effect subscriber: these compute fns track only via their track()
2720
+ let iCtx = tryGetInvokeContext();
2721
+ if (iCtx?.$effectSubscriber$) {
2722
+ iCtx = { ...iCtx, $effectSubscriber$: undefined };
2723
+ }
2724
+ return invokeApply(iCtx, computeFn, [job]);
2725
+ }
2726
+ const container = (this.$container$ ||= tryGetInvokeContext()?.$container$ || null);
2727
+ const subscriber = getSubscriber(this, "." /* EffectProperty.VNODE */);
2728
+ if (container) {
2729
+ clearEffectSubscription(container, subscriber);
2730
+ }
2731
+ const ctx = newInvokeContext();
2732
+ ctx.$container$ = container || undefined;
2733
+ ctx.$effectSubscriber$ = subscriber;
2734
+ return invokeApply(ctx, computeFn, [job]);
2735
+ }
2736
+ /** Start an async computation (the async engine; assumes the INVALID flag is set) */
2737
+ $computeAsync$() {
2433
2738
  // Skip computation on SSR for clientOnly signals
2434
2739
  if ((qTest ? isServerPlatform() : isServer) && this.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
2435
2740
  // We must pretend to load, and register as a listener for the captures
2436
- this.$untrackedLoading$ = true;
2741
+ this.$untrackedPending$ = true;
2437
2742
  this.$container$?.serializationCtx.$eagerResume$.add(this);
2438
2743
  return;
2439
2744
  }
2440
2745
  this.$clearNextPoll$();
2441
2746
  // Clear flag here to make sure the cleanups don't start another compute
2442
- this.$flags$ &= -2 /* SignalFlags.INVALID */;
2747
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2443
2748
  const current = this.$current$;
2444
2749
  if (current) {
2445
2750
  this.$requestCleanups$(current);
@@ -2449,12 +2754,12 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2449
2754
  if (this.$jobs$ ? this.$jobs$.length >= limit : current?.$promise$) {
2450
2755
  // We requested cleanups for all the previous jobs, once one finishes it will be removed from the jobs array and trigger computeIfNeeded
2451
2756
  // Restore invalid state
2452
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
2757
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
2453
2758
  return;
2454
2759
  }
2455
2760
  // We put the actual computation in a separate method so we can easily retain the promise
2456
2761
  const infoVersion = this.$infoVersion$;
2457
- const running = new AsyncJob(this, this.$info$, infoVersion);
2762
+ const running = new Job(this, this.$info$, infoVersion);
2458
2763
  this.$current$ = running;
2459
2764
  if (this.$jobs$) {
2460
2765
  this.$jobs$.push(running);
@@ -2462,35 +2767,39 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2462
2767
  running.$promise$ = this.$runComputation$(running);
2463
2768
  }
2464
2769
  async $runComputation$(running) {
2465
- const isCurrent = () => running === this.$current$;
2466
2770
  let fn = this.$computeQrl$.resolved;
2467
2771
  if (!fn) {
2468
2772
  // QRL resolution is async — we have to publish loading=true before awaiting so
2469
2773
  // subscribers know the value isn't ready yet.
2470
- this.untrackedLoading = true;
2774
+ this.untrackedPending = true;
2471
2775
  fn = await this.$computeQrl$.resolve();
2472
2776
  if (running.$abortController$?.signal.aborted) {
2473
2777
  running.$promise$ = null;
2474
2778
  return;
2475
2779
  }
2476
2780
  }
2781
+ if (this.$timeoutMs$) {
2782
+ this.$computationTimeoutId$ = setTimeout(() => {
2783
+ const error = new Error(`timeout ${this.$timeoutMs$}ms`);
2784
+ this.$setError$(running, error);
2785
+ running.$abortController$?.abort(error);
2786
+ }, this.$timeoutMs$);
2787
+ }
2788
+ return this.$settleComputation$(running, () => retryOnPromise(() => this.$invokeComputeFn$(fn, running)));
2789
+ }
2790
+ /** Await the computation and publish its result, error, and loading transitions. */
2791
+ async $settleComputation$(running, compute) {
2792
+ const isCurrent = () => running === this.$current$;
2477
2793
  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
2794
  // Try to stay sync if possible. Only publish loading=true to subscribers when
2486
2795
  // the compute is actually asynchronous — a synchronous resolve (e.g. pre-loaded
2487
2796
  // values injected via _injectAsyncSignalValue) should never transition through a
2488
2797
  // visible loading state, which on SSR would fire the loading-effect subscribers
2489
2798
  // (tasks) while the value is still "loading" from their perspective.
2490
- const valuePromise = retryOnPromise(fn.bind(null, running));
2799
+ const valuePromise = compute();
2491
2800
  let value;
2492
2801
  if (isPromise(valuePromise)) {
2493
- this.untrackedLoading = true;
2802
+ this.untrackedPending = true;
2494
2803
  value = await valuePromise;
2495
2804
  }
2496
2805
  else {
@@ -2515,7 +2824,7 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2515
2824
  // Note that these assignments run setters
2516
2825
  this.untrackedError = undefined;
2517
2826
  /**
2518
- * Use super.value instead of this.value to persist invalid state, so that invalidation
2827
+ * Use SignalImpl.value instead of this.value to persist invalid state, so that invalidation
2519
2828
  * during computation recomputes
2520
2829
  */
2521
2830
  super.value = value;
@@ -2530,370 +2839,130 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2530
2839
  if (running.$infoVersion$ === this.$infoVersion$) {
2531
2840
  this.$info$ = undefined;
2532
2841
  }
2533
- if (this.$flags$ & 1 /* SignalFlags.INVALID */) {
2842
+ if (this.$flags$ & 1 /* ComputedSignalFlags.INVALID */) {
2534
2843
  // we became invalid again while running, so we need to re-run the computation to get the new promise
2535
2844
  this.$computeIfNeeded$();
2536
2845
  }
2537
- else {
2538
- this.untrackedLoading = false;
2539
- this.$scheduleNextPoll$();
2540
- }
2541
- }
2542
- }
2543
- /**
2544
- * Sets the error from the given job. We only accept errors from the current job and we ignore
2545
- * AbortErrors.
2546
- */
2547
- $setError$(job, error) {
2548
- if (job !== this.$current$ || !job.$canWrite$) {
2549
- return;
2550
- }
2551
- job.$canWrite$ = false;
2552
- if (error instanceof Error && error.name === 'AbortError') {
2553
- // AbortError from AbortSignal is a cancellation, not an actual error
2554
- return;
2555
- }
2556
- this.untrackedError = error;
2557
- // Job failures should be rare and require retrying
2558
- this.untrackedValue = NEEDS_COMPUTATION;
2559
- }
2560
- /** Called after SSR/unmount */
2561
- async $destroy$() {
2562
- this.$clearNextPoll$();
2563
- clearTimeout(this.$computationTimeoutId$);
2564
- const current = this.$current$;
2565
- 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);
2846
+ else {
2847
+ this.untrackedPending = false;
2848
+ this.$scheduleNextPoll$();
2768
2849
  }
2769
2850
  }
2770
- return true;
2771
2851
  }
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;
2852
+ /**
2853
+ * Sets the error from the given job. We only accept errors from the current job and we ignore
2854
+ * AbortErrors.
2855
+ */
2856
+ $setError$(job, error) {
2857
+ if (job !== this.$current$ || !job.$canWrite$) {
2858
+ return;
2779
2859
  }
2780
- if (this.owner.children != null && prop === 'children') {
2781
- this.owner.children = null;
2782
- didDelete = true;
2860
+ job.$canWrite$ = false;
2861
+ if (error instanceof Error && error.name === 'AbortError') {
2862
+ // AbortError from AbortSignal is a cancellation, not an actual error
2863
+ return;
2783
2864
  }
2784
- return didDelete;
2865
+ this.untrackedError = error;
2866
+ // Job failures should be rare and require retrying
2867
+ this.untrackedValue = NEEDS_COMPUTATION;
2785
2868
  }
2786
- has(_, prop) {
2787
- if (prop === 'children') {
2788
- return this.owner.children != null;
2869
+ /** Called after SSR/unmount */
2870
+ async $destroy$() {
2871
+ this.$clearNextPoll$();
2872
+ clearTimeout(this.$computationTimeoutId$);
2873
+ const current = this.$current$;
2874
+ if (current) {
2875
+ this.$requestCleanups$(current);
2789
2876
  }
2790
- else if (prop === _CONST_PROPS || prop === _VAR_PROPS) {
2791
- return true;
2877
+ if (this.$jobs$) {
2878
+ await Promise.all(this.$jobs$.map((job) => job.$promise$));
2792
2879
  }
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
- }
2880
+ else {
2881
+ await current?.$promise$;
2804
2882
  }
2805
- return inVarProps || (this.owner.constProps ? prop in this.owner.constProps : false);
2806
2883
  }
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
- };
2884
+ $clearNextPoll$() {
2885
+ if (this.$pollTimeoutId$ !== undefined) {
2886
+ clearTimeout(this.$pollTimeoutId$);
2887
+ this.$pollTimeoutId$ = undefined;
2888
+ }
2818
2889
  }
2819
- ownKeys() {
2820
- const out = Object.keys(this.owner.varProps);
2821
- if (this.owner.children != null) {
2822
- out.push('children');
2890
+ $scheduleNextPoll$() {
2891
+ if ((qTest ? isServerPlatform() : isServer) || !this.$expires$) {
2892
+ return;
2823
2893
  }
2824
- if (this.owner.constProps) {
2825
- for (const key in this.owner.constProps) {
2826
- if (out.indexOf(key) === -1) {
2827
- out.push(key);
2894
+ this.$clearNextPoll$();
2895
+ const allowRecalc = !(this.$flags$ & 256 /* AsyncSignalFlags.NO_POLL */);
2896
+ // Even when clear on invalidate, we don't clear if we're merely re-running due to polling
2897
+ // We expect to get the new value soon, so we can avoid showing a loading state
2898
+ const mustClear = this.$flags$ & 128 /* AsyncSignalFlags.CLEAR_ON_INVALIDATE */ && !allowRecalc;
2899
+ this.$pollTimeoutId$ = setTimeout(() => this.$setInvalid$(allowRecalc, mustClear), this.$expires$);
2900
+ this.$pollTimeoutId$?.unref?.();
2901
+ }
2902
+ $hasSubscribers$() {
2903
+ return !!(this.$effects$?.size || this.$loadingEffects$?.size || this.$errorEffects$?.size);
2904
+ }
2905
+ $requestCleanups$(job, reason) {
2906
+ if (job.$cleanupRequested$) {
2907
+ return;
2908
+ }
2909
+ job.$cleanupRequested$ = true;
2910
+ job.$abortController$?.abort(reason);
2911
+ job.$promise$ = maybeThen(job.$promise$, () => this.$runCleanups$(job));
2912
+ }
2913
+ /** Run a job's cleanup callbacks, returning a promise when any of them are async. */
2914
+ $runCleanupCallbacks$(job) {
2915
+ const cleanups = job.$cleanups$;
2916
+ const onError = (err) => {
2917
+ const handleError = this.$container$?.handleError;
2918
+ if (handleError) {
2919
+ handleError(err, null);
2920
+ }
2921
+ else {
2922
+ console.error('Error in async signal cleanup', err);
2923
+ }
2924
+ };
2925
+ let promiseChain = undefined;
2926
+ if (cleanups) {
2927
+ // Keep this sync-ish so sync functions run immediately.
2928
+ for (let i = 0; i < cleanups.length; i++) {
2929
+ try {
2930
+ const result = cleanups[i]();
2931
+ if (isPromise(result)) {
2932
+ promiseChain = (promiseChain ? promiseChain.then(() => result) : result).catch(onError);
2933
+ }
2934
+ }
2935
+ catch (err) {
2936
+ onError(err);
2828
2937
  }
2829
2938
  }
2939
+ cleanups.length = 0;
2830
2940
  }
2831
- return out;
2941
+ return promiseChain;
2832
2942
  }
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$;
2943
+ /** Clean up and trigger signal compute once complete */
2944
+ $runCleanups$(job) {
2945
+ const onDone = () => {
2946
+ job.$promise$ = null;
2947
+ // Now trigger compute
2948
+ const jobs = this.$jobs$;
2949
+ if (jobs) {
2950
+ const idx = jobs.indexOf(job);
2951
+ if (idx !== -1) {
2952
+ jobs.splice(idx, 1);
2953
+ }
2841
2954
  }
2955
+ this.$computeIfNeeded$();
2956
+ };
2957
+ const promiseChain = this.$runCleanupCallbacks$(job);
2958
+ if (promiseChain) {
2959
+ return promiseChain.then(onDone);
2842
2960
  }
2843
2961
  else {
2844
- isDev &&
2845
- assertTrue(!ctx.$container$ || ctx.$container$ === propsProxy.$container$, 'Do not use props across containers');
2962
+ onDone();
2846
2963
  }
2847
2964
  }
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
2965
  }
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
2966
 
2898
2967
  // <docs markdown="../../readme.md#implicit$FirstArg">
2899
2968
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
@@ -2949,11 +3018,11 @@ const implicit$FirstArg = (fn) => {
2949
3018
  */
2950
3019
  class SerializerSignalImpl extends ComputedSignalImpl {
2951
3020
  constructor(container, argQrl) {
2952
- super(container, argQrl, 1 /* SignalFlags.INVALID */ | 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
3021
+ super(container, argQrl, 1 /* ComputedSignalFlags.INVALID */ | 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
2953
3022
  }
2954
3023
  $didInitialize$ = false;
2955
3024
  $computeIfNeeded$() {
2956
- if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
3025
+ if (!(this.$flags$ & 1 /* ComputedSignalFlags.INVALID */)) {
2957
3026
  return;
2958
3027
  }
2959
3028
  throwIfQRLNotResolved(this.$computeQrl$);
@@ -2966,7 +3035,7 @@ class SerializerSignalImpl extends ComputedSignalImpl {
2966
3035
  : deserialize(currentValue), this, "." /* EffectProperty.VNODE */, this.$container$);
2967
3036
  this.$didInitialize$ = true;
2968
3037
  // Needs to invalidate only after all possible Promise throws happened
2969
- this.$flags$ &= -2 /* SignalFlags.INVALID */;
3038
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2970
3039
  // 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
3040
  const didChange = (this.$didInitialize$ && untrackedValue !== 'undefined') ||
2972
3041
  untrackedValue !== this.$untrackedValue$;
@@ -2977,13 +3046,46 @@ class SerializerSignalImpl extends ComputedSignalImpl {
2977
3046
  }
2978
3047
  }
2979
3048
 
3049
+ /**
3050
+ * # ================================
3051
+ *
3052
+ * AsyncSignalImpl
3053
+ *
3054
+ * # ================================
3055
+ *
3056
+ * The async engine (jobs, loading, error, polling) lives in ComputedSignalImpl; this subclass
3057
+ * configures it from options and switches the compute invocation to CTX_ARG mode: the compute fn
3058
+ * receives the ComputeCtx argument and tracks only via its explicit `track()` (no auto-tracking).
3059
+ *
3060
+ * @internal
3061
+ */
3062
+ class AsyncSignalImpl extends ComputedSignalImpl {
3063
+ constructor(container, fn, flags = 1 /* ComputedSignalFlags.INVALID */ |
3064
+ 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */, options) {
3065
+ super(container, fn, flags | 512 /* AsyncSignalFlags.ASYNC_MODE */ | 1024 /* AsyncSignalFlags.CTX_ARG */, options);
3066
+ }
3067
+ }
3068
+ /**
3069
+ * Inject a pre-loaded value into a signal while preserving subscriptions. Calls `invalidate({__v})`
3070
+ * so the compute function reads the value from `info`, then triggers an immediate synchronous
3071
+ * compute via the private `$computeIfNeeded$()` method (which is mangled in core builds, so callers
3072
+ * from other packages must go through this helper).
3073
+ *
3074
+ * @internal
3075
+ */
3076
+ const _injectAsyncSignalValue = (signal, value) => {
3077
+ const impl = signal;
3078
+ impl.invalidate({ __v: value });
3079
+ impl.$computeIfNeeded$();
3080
+ };
3081
+
2980
3082
  /** @internal */
2981
3083
  const createSignal$1 = (value) => {
2982
3084
  return new SignalImpl(null, value);
2983
3085
  };
2984
3086
  /** @internal */
2985
3087
  const createComputedSignal = (qrl, options) => {
2986
- return new ComputedSignalImpl(options?.container || null, qrl, getComputedSignalFlags(options?.serializationStrategy || 'always'));
3088
+ return new ComputedSignalImpl(options?.container || null, qrl, getComputedSignalFlags(options?.serializationStrategy || 'always'), options);
2987
3089
  };
2988
3090
  /** @internal */
2989
3091
  const createAsyncSignal = (qrl, options) => {
@@ -3007,9 +3109,10 @@ const createSignal = createSignal$1;
3007
3109
  * recalculated.
3008
3110
  *
3009
3111
  * 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()`).
3112
+ * effects. Every synchronous signal or store read is tracked automatically; reads after the first
3113
+ * `await` must use the `track()` provided on the context argument. When the function is async, the
3114
+ * returned signal exposes the async API (`.pending`, `.error`, `.promise()`), and reading an
3115
+ * unresolved `.value` throws the computation promise.
3013
3116
  *
3014
3117
  * @public
3015
3118
  */
@@ -3018,6 +3121,7 @@ const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedSignal);
3018
3121
  * Create a signal holding a `.value` which is calculated from the given async function (QRL). The
3019
3122
  * standalone version of `useAsync$`.
3020
3123
  *
3124
+ * @deprecated Use `createComputed$` instead, it has async support now.
3021
3125
  * @public
3022
3126
  */
3023
3127
  const createAsync$ = /*#__PURE__*/ implicit$FirstArg(createAsyncSignal);
@@ -3064,10 +3168,10 @@ const _wrapProp = (...args) => {
3064
3168
  return obj[prop];
3065
3169
  }
3066
3170
  if (isSignal(obj)) {
3067
- if (!(obj instanceof AsyncSignalImpl)) {
3171
+ if (!(obj instanceof ComputedSignalImpl)) {
3068
3172
  isDev && assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
3069
3173
  }
3070
- if (obj instanceof WrappedSignalImpl && obj.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */) {
3174
+ if (obj instanceof WrappedSignalImpl) {
3071
3175
  return obj;
3072
3176
  }
3073
3177
  return getWrapped(args);
@@ -3121,7 +3225,7 @@ class WrappedSignalImpl extends SignalImpl {
3121
3225
  constructor(container, fn, args, fnStr,
3122
3226
  // We need a separate flag to know when the computation needs running because
3123
3227
  // we need the old value to know if effects need running after computation
3124
- flags = 1 /* SignalFlags.INVALID */ | 4 /* WrappedSignalFlags.UNWRAP */) {
3228
+ flags = 1 /* ComputedSignalFlags.INVALID */) {
3125
3229
  super(container, NEEDS_COMPUTATION);
3126
3230
  this.$args$ = args;
3127
3231
  this.$func$ = fn;
@@ -3129,7 +3233,7 @@ class WrappedSignalImpl extends SignalImpl {
3129
3233
  this.$flags$ = flags;
3130
3234
  }
3131
3235
  invalidate() {
3132
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
3236
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
3133
3237
  // we are trying to run computation without creating a chore, which can be expensive
3134
3238
  // for many signals. If it fails, we schedule a chore to run the computation.
3135
3239
  try {
@@ -3142,8 +3246,8 @@ class WrappedSignalImpl extends SignalImpl {
3142
3246
  }
3143
3247
  }
3144
3248
  // 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 */;
3249
+ if (this.$flags$ & 2 /* ComputedSignalFlags.RUN_EFFECTS */) {
3250
+ this.$flags$ &= -3 /* ComputedSignalFlags.RUN_EFFECTS */;
3147
3251
  scheduleEffects(this.$container$, this, this.$effects$);
3148
3252
  }
3149
3253
  }
@@ -3153,7 +3257,7 @@ class WrappedSignalImpl extends SignalImpl {
3153
3257
  return this.$untrackedValue$;
3154
3258
  }
3155
3259
  $computeIfNeeded$() {
3156
- if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
3260
+ if (!(this.$flags$ & 1 /* ComputedSignalFlags.INVALID */)) {
3157
3261
  return;
3158
3262
  }
3159
3263
  const untrackedValue = trackSignal(() => this.$func$(...this.$args$), this, "." /* EffectProperty.VNODE */, this.$container$);
@@ -3164,10 +3268,10 @@ class WrappedSignalImpl extends SignalImpl {
3164
3268
  // this.$flags$ &= ~SignalFlags.INVALID;
3165
3269
  // reset flag in case we call computedIfNeeded twice and the value was changed only the first time
3166
3270
  // TODO: change to version number?
3167
- this.$flags$ &= -3 /* SignalFlags.RUN_EFFECTS */;
3271
+ this.$flags$ &= -3 /* ComputedSignalFlags.RUN_EFFECTS */;
3168
3272
  const didChange = untrackedValue !== this.$untrackedValue$;
3169
3273
  if (didChange) {
3170
- this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
3274
+ this.$flags$ |= 2 /* ComputedSignalFlags.RUN_EFFECTS */;
3171
3275
  this.$untrackedValue$ = untrackedValue;
3172
3276
  }
3173
3277
  }
@@ -3271,12 +3375,15 @@ const isJSXNode = (n) => {
3271
3375
  * load the QRL captured scope ourselves. Otherwise, we are being called as a QRL and the captures
3272
3376
  * are already set.
3273
3377
  */
3274
- const maybeScopeFromQL = (captureIds, element) => {
3275
- if (typeof captureIds === 'string') {
3378
+ const withScopeFromQL = (captureDeltas, element, callback) => {
3379
+ if (typeof captureDeltas === 'string') {
3276
3380
  const container = getDomContainer(element);
3277
- setCaptures(deserializeCaptures(container, captureIds));
3381
+ return whenContainerDataReady(container, () => {
3382
+ setCaptures(deserializeCaptureDeltas(container, captureDeltas));
3383
+ return callback();
3384
+ });
3278
3385
  }
3279
- return null;
3386
+ return callback();
3280
3387
  };
3281
3388
  /**
3282
3389
  * Handles events for bind:value
@@ -3284,9 +3391,10 @@ const maybeScopeFromQL = (captureIds, element) => {
3284
3391
  * @internal
3285
3392
  */
3286
3393
  function _val(_, element) {
3287
- maybeScopeFromQL(this, element);
3288
- const signal = _captures[0];
3289
- signal.value = element.type === 'number' ? element.valueAsNumber : element.value;
3394
+ return withScopeFromQL(this, element, () => {
3395
+ const signal = _captures[0];
3396
+ signal.value = element.type === 'number' ? element.valueAsNumber : element.value;
3397
+ });
3290
3398
  }
3291
3399
  /**
3292
3400
  * Handles events for bind:checked
@@ -3294,9 +3402,10 @@ function _val(_, element) {
3294
3402
  * @internal
3295
3403
  */
3296
3404
  function _chk(_, element) {
3297
- maybeScopeFromQL(this, element);
3298
- const signal = _captures[0];
3299
- signal.value = element.checked;
3405
+ return withScopeFromQL(this, element, () => {
3406
+ const signal = _captures[0];
3407
+ signal.value = element.checked;
3408
+ });
3300
3409
  }
3301
3410
  /**
3302
3411
  * Resumes selected state (e.g. polling AsyncSignals) by deserializing captures. Used for
@@ -3305,17 +3414,18 @@ function _chk(_, element) {
3305
3414
  * @internal
3306
3415
  */
3307
3416
  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$();
3417
+ return withScopeFromQL(this, element, () => {
3418
+ // Captures are deserialized, now trigger computation on AsyncSignals
3419
+ if (_captures) {
3420
+ for (let i = 0; i < _captures.length; i++) {
3421
+ const capture = _captures[i];
3422
+ if (capture instanceof AsyncSignalImpl && capture.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
3423
+ capture.$computeIfNeeded$();
3424
+ }
3425
+ // note that polling async signals will automatically schedule themselves so no action needed
3315
3426
  }
3316
- // note that polling async signals will automatically schedule themselves so no action needed
3317
3427
  }
3318
- }
3428
+ });
3319
3429
  }
3320
3430
 
3321
3431
  const BIND_VALUE = 'bind:value';
@@ -4095,7 +4205,8 @@ function runEventHandlerQRL(handler, event, element, ctx) {
4095
4205
  return;
4096
4206
  }
4097
4207
  if (!ctx) {
4098
- ctx = newInvokeContextFromDOM(event, element);
4208
+ const container = getDomContainer(element);
4209
+ return whenContainerDataReady(container, () => runEventHandlerQRL(handler, event, element, newInvokeContextFromDOM(event, element, container)));
4099
4210
  }
4100
4211
  const container = ctx.$container$;
4101
4212
  const hostElement = ctx.$hostElement$;
@@ -4136,13 +4247,16 @@ function _run(event, element) {
4136
4247
  // ignore events on disconnected elements, this can happen when the event is triggered while the element is being removed
4137
4248
  return;
4138
4249
  }
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);
4250
+ const container = getDomContainer(element);
4251
+ return whenContainerDataReady(container, () => {
4252
+ const ctx = newInvokeContextFromDOM(event, element, container);
4253
+ if (typeof this === 'string') {
4254
+ setCaptures(deserializeCaptureDeltas(ctx.$container$, this));
4255
+ }
4256
+ const qrlToRun = _captures[0];
4257
+ isDev && assertQrl(qrlToRun);
4258
+ return runEventHandlerQRL(qrlToRun, event, element, ctx);
4259
+ });
4146
4260
  }
4147
4261
 
4148
4262
  function peekNextSibling(vCurrent) {
@@ -4303,9 +4417,7 @@ function diff(diffContext, jsxNode, vStartNode, vCurrent = vnode_getFirstChild(v
4303
4417
  else if (type === Projection) {
4304
4418
  expectProjection(diffContext);
4305
4419
  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);
4420
+ /* isProjection */ true);
4309
4421
  }
4310
4422
  else if (type === SSRComment) {
4311
4423
  expectNoMore(diffContext);
@@ -4418,9 +4530,11 @@ function advance(diffContext) {
4418
4530
  *
4419
4531
  * In the above example all nodes are on same level so we don't `descendVNode` even thought there is
4420
4532
  * an array produced by the `map` function.
4533
+ * @param isProjection - True when descending into a Projection, whose children survive re-renders:
4534
+ * they are diffed against (never re-created) and kept when the new JSX has no children.
4421
4535
  */
4422
- function descend(diffContext, children, descendVNode, shouldExpectNoChildren = true) {
4423
- if (shouldExpectNoChildren &&
4536
+ function descend(diffContext, children, descendVNode, isProjection = false) {
4537
+ if (!isProjection &&
4424
4538
  (children == null || (descendVNode && isArray(children) && children.length === 0))) {
4425
4539
  expectNoChildren(diffContext);
4426
4540
  return;
@@ -4429,7 +4543,10 @@ function descend(diffContext, children, descendVNode, shouldExpectNoChildren = t
4429
4543
  if (descendVNode) {
4430
4544
  isDev &&
4431
4545
  assertDefined(diffContext.$vCurrent$ || diffContext.$vNewNode$, 'Expecting vCurrent to be defined.');
4432
- const creationMode = diffContext.$isCreationMode$ ||
4546
+ const creationMode =
4547
+ // a projection keeps its children across re-renders, so it never inherits
4548
+ // creation mode from a not-yet-rendered host; create only if new or empty
4549
+ (!isProjection && diffContext.$isCreationMode$) ||
4433
4550
  !!diffContext.$vNewNode$ ||
4434
4551
  !vnode_getFirstChild(diffContext.$vCurrent$);
4435
4552
  diffContext.$isCreationMode$ = creationMode;
@@ -4862,18 +4979,20 @@ function diffProps(diffContext, vnode, newAttrs, currentFile) {
4862
4979
  vnode_ensureElementInflated(diffContext.$container$, vnode);
4863
4980
  }
4864
4981
  const oldAttrs = vnode.props;
4982
+ const isHtml = (vnode.flags & 1536 /* VNodeFlags.NAMESPACE_MASK */) === 0 /* VNodeFlags.NS_html */;
4865
4983
  // Actual diffing logic
4866
4984
  // Apply all new attributes
4867
4985
  for (const key in newAttrs) {
4868
4986
  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
- }
4987
+ // HTML parsers lowercase attribute names.
4988
+ const oldKey = oldAttrs && isHtml && !_hasOwnProperty.call(oldAttrs, key) ? key.toLowerCase() : key;
4989
+ if (oldAttrs && _hasOwnProperty.call(oldAttrs, oldKey)) {
4990
+ const oldValue = oldAttrs[oldKey];
4991
+ if (oldKey !== key) {
4992
+ vnode_setProp(vnode, oldKey, null);
4993
+ vnode_setProp(vnode, key, oldValue);
4994
+ }
4995
+ if (!areDiffValuesEqual(newValue, oldValue)) {
4877
4996
  patchProperty(diffContext, vnode, key, newValue, currentFile);
4878
4997
  }
4879
4998
  }
@@ -5343,13 +5462,26 @@ function handleChangedProps(src, dst, propsHandler, container, triggerEffects =
5343
5462
  if (key === 'children' || key === QBackRefs) {
5344
5463
  continue;
5345
5464
  }
5346
- if (!dst || src[key] !== dst[key]) {
5465
+ const newValue = src[key];
5466
+ const oldValue = dst?.[key];
5467
+ if (!dst ||
5468
+ !(areDiffValuesEqual(newValue, oldValue) || areSignalValuesEqual(newValue, oldValue))) {
5469
+ if (key === 'class') {
5470
+ if (areClassValuesEqual(newValue, oldValue)) {
5471
+ continue;
5472
+ }
5473
+ }
5474
+ else if (key === 'style') {
5475
+ if (areStyleValuesEqual(newValue, oldValue)) {
5476
+ continue;
5477
+ }
5478
+ }
5347
5479
  if (triggerEffects) {
5348
5480
  if (dst) {
5349
5481
  // Update the value in dst BEFORE triggering effects
5350
5482
  // so effects see the new value
5351
5483
  // Note: Value is not triggering effects, because we are modyfing direct VAR_PROPS object
5352
- dst[key] = src[key];
5484
+ dst[key] = newValue;
5353
5485
  }
5354
5486
  const didTigger = triggerPropsProxyEffect(propsHandler, key);
5355
5487
  if (!didTigger) {
@@ -5436,10 +5568,16 @@ function cleanup(container, journal, vNode, cursorRoot = null) {
5436
5568
  // don't call cleanupDestroyable yet, do it by the scheduler
5437
5569
  continue;
5438
5570
  }
5439
- else if (obj instanceof SignalImpl || isStore(obj)) {
5440
- clearAllEffects(container, obj);
5571
+ // Stores and plain signals are only producers; their subscriptions are removed
5572
+ // when cleaning the consumers that read them. They don't own reactive backrefs.
5573
+ else if (obj instanceof ComputedSignalImpl || obj instanceof WrappedSignalImpl) {
5574
+ if (!(obj.$flags$ & 4 /* ComputedSignalFlags.PRESERVE_ON_SEQ_CLEANUP */)) {
5575
+ clearAllEffects(container, obj);
5576
+ }
5441
5577
  }
5442
- if (objIsTask || obj instanceof AsyncSignalImpl) {
5578
+ if (objIsTask ||
5579
+ (obj instanceof ComputedSignalImpl &&
5580
+ (obj.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */ || obj.$current$))) {
5443
5581
  cleanupDestroyable(obj);
5444
5582
  }
5445
5583
  }
@@ -5558,6 +5696,37 @@ function markVNodeAsDeleted(vCursor) {
5558
5696
  */
5559
5697
  vCursor.flags |= 32 /* VNodeFlags.Deleted */;
5560
5698
  }
5699
+ function areDiffValuesEqual(newValue, oldValue) {
5700
+ if (newValue === oldValue) {
5701
+ return true;
5702
+ }
5703
+ return (newValue instanceof WrappedSignalImpl &&
5704
+ oldValue instanceof WrappedSignalImpl &&
5705
+ areWrappedSignalsEqual(oldValue, newValue));
5706
+ }
5707
+ function areSignalValuesEqual(newValue, oldValue) {
5708
+ if (newValue === oldValue) {
5709
+ return true;
5710
+ }
5711
+ if (isSignal(newValue) && isSignal(oldValue)) {
5712
+ const unwrappedNew = newValue instanceof WrappedSignalImpl ? newValue.$unwrapIfSignal$() : newValue;
5713
+ const unwrappedOld = oldValue instanceof WrappedSignalImpl ? oldValue.$unwrapIfSignal$() : oldValue;
5714
+ return untrack(() => unwrappedNew.value === unwrappedOld.value);
5715
+ }
5716
+ return false;
5717
+ }
5718
+ function areStyleValuesEqual(newValue, oldValue) {
5719
+ if (newValue === oldValue) {
5720
+ return true;
5721
+ }
5722
+ return stringifyStyle(newValue) === stringifyStyle(oldValue);
5723
+ }
5724
+ function areClassValuesEqual(newValue, oldValue) {
5725
+ if (newValue === oldValue) {
5726
+ return true;
5727
+ }
5728
+ return serializeClass(newValue) === serializeClass(oldValue);
5729
+ }
5561
5730
  function areWrappedSignalsEqual(oldSignal, newSignal) {
5562
5731
  if (oldSignal === newSignal) {
5563
5732
  return true;
@@ -6406,8 +6575,8 @@ function executeCompute(vNode, container) {
6406
6575
  ctx.$container$ = container;
6407
6576
  // needed for computed signals and throwing QRLs
6408
6577
  return maybeThen(retryOnPromise(() => invoke.call(target, ctx, target.$computeIfNeeded$)), () => {
6409
- if (target.$flags$ & 2 /* SignalFlags.RUN_EFFECTS */) {
6410
- target.$flags$ &= -3 /* SignalFlags.RUN_EFFECTS */;
6578
+ if (target.$flags$ & 2 /* ComputedSignalFlags.RUN_EFFECTS */) {
6579
+ target.$flags$ &= -3 /* ComputedSignalFlags.RUN_EFFECTS */;
6411
6580
  return scheduleEffects(container, target, effects);
6412
6581
  }
6413
6582
  });
@@ -6948,6 +7117,10 @@ function splitCursorBoundary(container, vNode) {
6948
7117
  * @returns The vNode itself, now acting as a cursor
6949
7118
  */
6950
7119
  function addCursor(container, root, priority) {
7120
+ if (isCursor(root)) {
7121
+ triggerCursors();
7122
+ return root;
7123
+ }
6951
7124
  const cursorData = {
6952
7125
  afterFlushTasks: null,
6953
7126
  extraPromises: null,
@@ -7000,7 +7173,11 @@ function _executeSsrChores(container, ssrNode) {
7000
7173
  if (ssrNode.dirty & 32 /* ChoreBits.COMPUTE */) {
7001
7174
  executeCompute(ssrNode, container);
7002
7175
  }
7003
- if (isDev && ssrNode.dirty & 1023 /* ChoreBits.DIRTY_MASK */) {
7176
+ if (isDev &&
7177
+ ssrNode.dirty & 1023 /* ChoreBits.DIRTY_MASK */ &&
7178
+ !(ssrNode.flags & 2 /* SsrNodeFlags.WarnedStreamedChore */)) {
7179
+ // Warn once per host; a re-dirtied streamed host would otherwise spam every tick.
7180
+ ssrNode.flags |= 2 /* SsrNodeFlags.WarnedStreamedChore */;
7004
7181
  // We are running on the server.
7005
7182
  // On server we can't schedule task for a different host!
7006
7183
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -8717,8 +8894,17 @@ const processVNodeData$1 = (vData, callback) => {
8717
8894
  }
8718
8895
  return vData.substring(start, nextToConsumeIdx);
8719
8896
  };
8897
+ const consumeEncodedValue = () => {
8898
+ if (getChar(nextToConsumeIdx + 1) !== VNodeDataChar.SEPARATOR) {
8899
+ return consumeValue();
8900
+ }
8901
+ consume();
8902
+ const value = decodeURIComponent(decodeVNodeDataString(consumeValue()));
8903
+ consume();
8904
+ return value;
8905
+ };
8720
8906
  while (peek() !== 0) {
8721
- callback(peek, consumeValue, consume, getChar, nextToConsumeIdx);
8907
+ callback(peek, consumeValue, consume, getChar, nextToConsumeIdx, consumeEncodedValue);
8722
8908
  }
8723
8909
  };
8724
8910
  /** @internal */
@@ -8909,13 +9095,13 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8909
9095
  return !nodeIsElement || (nodeIsElement && shouldSkipElement(node));
8910
9096
  };
8911
9097
  let components = null;
8912
- processVNodeData$1(vData, (peek, consumeValue, consume, getChar, nextToConsumeIdx) => {
9098
+ processVNodeData$1(vData, (peek, consumeValue, consume, getChar, dataIdx, consumeEncodedValue) => {
8913
9099
  if (isNumber(peek())) {
8914
9100
  // Element counts get encoded as numbers.
8915
9101
  while (shouldSkipNode(child)) {
8916
9102
  child = fastNextSibling(child);
8917
9103
  if (!child) {
8918
- throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), nextToConsumeIdx]);
9104
+ throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), dataIdx]);
8919
9105
  }
8920
9106
  }
8921
9107
  combinedText = null;
@@ -8936,7 +9122,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8936
9122
  shouldSkipElement(child)) {
8937
9123
  child = fastNextSibling(child);
8938
9124
  if (!child && value > 0) {
8939
- throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), nextToConsumeIdx]);
9125
+ throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), dataIdx]);
8940
9126
  }
8941
9127
  }
8942
9128
  }
@@ -8961,17 +9147,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8961
9147
  vnode_setProp(vParent, ELEMENT_PROPS, consumeValue());
8962
9148
  }
8963
9149
  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;
9150
+ vParent.key = consumeEncodedValue();
8975
9151
  }
8976
9152
  else if (peek() === VNodeDataChar.SEQ) {
8977
9153
  vnode_setProp(vParent, ELEMENT_SEQ, consumeValue());
@@ -9006,7 +9182,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
9006
9182
  // Custom attribute: |key|value
9007
9183
  const keyValue = consumeValue();
9008
9184
  const key = decodeVNodeDataString(keyValue);
9009
- const valueSeparatorIdx = nextToConsumeIdx + keyValue.length + 1;
9185
+ const valueSeparatorIdx = dataIdx + keyValue.length + 1;
9010
9186
  const isEscapedValue = getChar(valueSeparatorIdx + 1) === VNodeDataChar.SEPARATOR;
9011
9187
  let value;
9012
9188
  if (isEscapedValue) {
@@ -9029,8 +9205,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
9029
9205
  vParent = stack.pop();
9030
9206
  }
9031
9207
  else if (peek() === VNodeDataChar.SLOT) {
9032
- const value = decodeVNodeDataString(consumeValue());
9033
- vnode_setProp(vParent, QSlot, value);
9208
+ vnode_setProp(vParent, QSlot, consumeEncodedValue());
9034
9209
  }
9035
9210
  else {
9036
9211
  // 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 +9316,12 @@ function clearEffectSubscription(container, effect) {
9141
9316
  return;
9142
9317
  }
9143
9318
  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) {
9319
+ if (producer instanceof SignalImpl) {
9149
9320
  clearSignal(container, producer, effect);
9321
+ if (producer instanceof ComputedSignalImpl) {
9322
+ // computeds can carry async engine state (loading/error subscribers)
9323
+ clearAsyncSignalEffects(producer, effect);
9324
+ }
9150
9325
  }
9151
9326
  else if (isPropsProxy(producer)) {
9152
9327
  const propsHandler = producer[_PROPS_HANDLER];
@@ -9171,11 +9346,7 @@ function clearSignal(container, producer, effect) {
9171
9346
  clearAllEffects(container, producer);
9172
9347
  }
9173
9348
  }
9174
- function clearAsyncSignal(producer, effect) {
9175
- const effects = producer.$effects$;
9176
- if (effects && effects.has(effect)) {
9177
- effects.delete(effect);
9178
- }
9349
+ function clearAsyncSignalEffects(producer, effect) {
9179
9350
  const pendingEffects = producer.$loadingEffects$;
9180
9351
  if (pendingEffects && pendingEffects.has(effect)) {
9181
9352
  pendingEffects.delete(effect);
@@ -9211,7 +9382,7 @@ const useTaskQrl = (qrl, opts) => {
9211
9382
  const taskFlags =
9212
9383
  // enabled by default
9213
9384
  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);
9385
+ const task = new Task(4 /* TaskFlags.DIRTY */ | 2 /* TaskFlags.TASK */ | taskFlags, i, iCtx.$hostElement$, qrl, null);
9215
9386
  // In V2 we add the task to the sequential scope. We need to do this
9216
9387
  // in order to be able to retrieve it later when the parent element is
9217
9388
  // deleted and we need to be able to release the task subscriptions.
@@ -9270,17 +9441,15 @@ class Task extends BackRef {
9270
9441
  $index$;
9271
9442
  $el$;
9272
9443
  $qrl$;
9273
- $state$;
9274
9444
  $destroy$;
9275
9445
  $destroyPromise$;
9276
9446
  $taskPromise$ = null;
9277
- constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
9447
+ constructor($flags$, $index$, $el$, $qrl$, $destroy$) {
9278
9448
  super();
9279
9449
  this.$flags$ = $flags$;
9280
9450
  this.$index$ = $index$;
9281
9451
  this.$el$ = $el$;
9282
9452
  this.$qrl$ = $qrl$;
9283
- this.$state$ = $state$;
9284
9453
  this.$destroy$ = $destroy$;
9285
9454
  }
9286
9455
  }
@@ -9296,12 +9465,14 @@ const isTask = (value) => {
9296
9465
  */
9297
9466
  function scheduleTask(_event, element) {
9298
9467
  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 */);
9468
+ return whenContainerDataReady(container, () => {
9469
+ if (typeof this === 'string') {
9470
+ setCaptures(deserializeCaptureDeltas(container, this));
9471
+ }
9472
+ const task = _captures[0];
9473
+ task.$flags$ |= 4 /* TaskFlags.DIRTY */;
9474
+ markVNodeDirty(container, task.$el$, 1 /* ChoreBits.TASKS */);
9475
+ });
9305
9476
  }
9306
9477
 
9307
9478
  const throwIfQRLNotResolved = (qrl) => {
@@ -9413,7 +9584,7 @@ const isSerializerObj = (obj) => {
9413
9584
  return isObject(obj) && typeof obj[SerializerSymbol] === 'function';
9414
9585
  };
9415
9586
  const getComputedSignalFlags = (serializationStrategy) => {
9416
- let flags = 1 /* SignalFlags.INVALID */;
9587
+ let flags = 1 /* ComputedSignalFlags.INVALID */;
9417
9588
  switch (serializationStrategy) {
9418
9589
  // TODO: implement this in the future
9419
9590
  // case 'auto':
@@ -9428,6 +9599,18 @@ const getComputedSignalFlags = (serializationStrategy) => {
9428
9599
  }
9429
9600
  return flags;
9430
9601
  };
9602
+ /**
9603
+ * Mark this signal as owned outside of the component that read it.
9604
+ *
9605
+ * Externally owned signals are preserved when found in a component's sequential scope during
9606
+ * component cleanup.
9607
+ *
9608
+ * @internal
9609
+ */
9610
+ const _markSignalAsExternallyOwned = (signal) => {
9611
+ signal.$flags$ |=
9612
+ 4 /* ComputedSignalFlags.PRESERVE_ON_SEQ_CLEANUP */;
9613
+ };
9431
9614
 
9432
9615
  const stringifyPath = [];
9433
9616
  function qwikDebugToString(value) {
@@ -9787,6 +9970,7 @@ function getEffects(target, prop, storeEffects) {
9787
9970
 
9788
9971
  /** Used to represent an undefined value that must be serialized */
9789
9972
  const explicitUndefined = Symbol('undefined');
9973
+ const EMPTY_OBJECT_PAYLOAD = 0;
9790
9974
  // Used for allocate, make sure they are in sync with Constants
9791
9975
  const _constants = [
9792
9976
  undefined,
@@ -9807,6 +9991,10 @@ const _constants = [
9807
9991
  Number.MAX_SAFE_INTEGER,
9808
9992
  Number.MAX_SAFE_INTEGER - 1,
9809
9993
  Number.MIN_SAFE_INTEGER,
9994
+ ':',
9995
+ '.',
9996
+ 'id',
9997
+ 'ref',
9810
9998
  ];
9811
9999
  // Used for dumpState, make sure they are in sync with Constants
9812
10000
  const _constantNames = [
@@ -9828,6 +10016,10 @@ const _constantNames = [
9828
10016
  'MAX_SAFE_INTEGER',
9829
10017
  'MAX_SAFE_INTEGER-1',
9830
10018
  'MIN_SAFE_INTEGER',
10019
+ "':'",
10020
+ "'.'",
10021
+ "'id'",
10022
+ "'ref'",
9831
10023
  ];
9832
10024
  // Used for dumpState, make sure they are in sync with TypeIds
9833
10025
  const _typeIdNames = [
@@ -9873,6 +10065,9 @@ const _typeIdNames = [
9873
10065
  'SubscriptionData',
9874
10066
  'EffectSubscription',
9875
10067
  'SubscriptionPatch',
10068
+ 'SubscriptionDataConstTrue',
10069
+ 'SubscriptionDataConstFalse',
10070
+ 'EffectSubscriptionNoData',
9876
10071
  ];
9877
10072
 
9878
10073
  function qrlToString(serializationContext, qrl, raw) {
@@ -9919,17 +10114,25 @@ function qrlToString(serializationContext, qrl, raw) {
9919
10114
  symbol = String(serializationContext.$addSyncFn$(null, 0, fn));
9920
10115
  }
9921
10116
  const captures = qrl.getCaptured();
9922
- let captureIds = null;
10117
+ let captureDeltas = null;
9923
10118
  if (captures && captures.length > 0) {
9924
10119
  // We refer by id so every capture needs to be a root
9925
- captureIds = captures.map((ref) => `${serializationContext.$addRoot$(ref)}`).join(' ');
10120
+ let previous = 0;
10121
+ let output = '';
10122
+ for (let i = 0; i < captures.length; i++) {
10123
+ const captureId = serializationContext.$addRoot$(captures[i]);
10124
+ const delta = captureId - previous;
10125
+ previous = captureId;
10126
+ output += (i === 0 ? '' : ' ') + delta;
10127
+ }
10128
+ captureDeltas = output;
9926
10129
  }
9927
10130
  if (raw) {
9928
- return [chunk, symbol, captureIds];
10131
+ return [chunk, symbol, captureDeltas];
9929
10132
  }
9930
10133
  let qrlStringInline = `${chunk}#${symbol}`;
9931
- if (captureIds) {
9932
- qrlStringInline += `#${captureIds}`;
10134
+ if (captureDeltas) {
10135
+ qrlStringInline += `#${captureDeltas}`;
9933
10136
  }
9934
10137
  return qrlStringInline;
9935
10138
  }
@@ -9941,12 +10144,23 @@ function qrlToChunks(serializationContext, qrl) {
9941
10144
  return prefix;
9942
10145
  }
9943
10146
  const chunks = [prefix, '#'];
9944
- const captureIds = captures.split(' ');
9945
- for (let i = 0; i < captureIds.length; i++) {
9946
- if (i > 0) {
9947
- chunks.push(' ');
10147
+ let previousCaptureId = 0;
10148
+ let start = 0;
10149
+ for (let i = 0; i <= captures.length; i++) {
10150
+ if (i === captures.length || captures[i] === ' ') {
10151
+ if (i > start) {
10152
+ const captureId = previousCaptureId + Number(captures.slice(start, i));
10153
+ if (start > 0) {
10154
+ chunks.push(' ');
10155
+ chunks.push({ id: captureId, base: previousCaptureId });
10156
+ }
10157
+ else {
10158
+ chunks.push(captureId);
10159
+ }
10160
+ previousCaptureId = captureId;
10161
+ }
10162
+ start = i + 1;
9948
10163
  }
9949
- chunks.push(Number(captureIds[i]));
9950
10164
  }
9951
10165
  return chunks;
9952
10166
  }
@@ -9962,10 +10176,14 @@ function createQRLWithBackChannel(chunk, symbol, captures, container) {
9962
10176
  }
9963
10177
  return createQRL(chunk, symbol, null, qrlImporter, captures, container);
9964
10178
  }
9965
- /** Parses "chunk#hash#...rootRef" */
10179
+ /** Parses "chunk#hash#...captureDelta" */
9966
10180
  function parseQRL(qrl, container) {
9967
- const [chunk, symbol, captures] = qrl.split('#');
9968
- return createQRLWithBackChannel(chunk, symbol, captures || null, container);
10181
+ const firstHash = qrl.indexOf('#');
10182
+ const secondHash = qrl.indexOf('#', firstHash + 1);
10183
+ const chunk = qrl.slice(0, firstHash);
10184
+ const symbol = secondHash === -1 ? qrl.slice(firstHash + 1) : qrl.slice(firstHash + 1, secondHash);
10185
+ const captures = secondHash !== -1 && secondHash + 1 < qrl.length ? qrl.slice(secondHash + 1) : null;
10186
+ return createQRLWithBackChannel(chunk, symbol, captures, container);
9969
10187
  }
9970
10188
  const QRL_RUNTIME_CHUNK = 'mock-chunk';
9971
10189
 
@@ -10119,6 +10337,36 @@ class Serializer {
10119
10337
  }
10120
10338
  return Number(key);
10121
10339
  }
10340
+ rebaseQrlCaptureDeltas$(baseRootId, captureDeltas) {
10341
+ let previousCaptureId = 0;
10342
+ let previousOutputBase = baseRootId;
10343
+ const deltaList = captureDeltas.split(' ');
10344
+ for (let i = 0; i < deltaList.length; i++) {
10345
+ const captureId = previousCaptureId + Number(deltaList[i]);
10346
+ deltaList[i] = String(captureId - previousOutputBase);
10347
+ previousCaptureId = captureId;
10348
+ previousOutputBase = captureId;
10349
+ }
10350
+ return deltaList.join(' ');
10351
+ }
10352
+ outputShortStringConstant$(value) {
10353
+ switch (value) {
10354
+ case ':':
10355
+ this.output(3 /* TypeIds.Constant */, 18 /* Constants.Colon */);
10356
+ return true;
10357
+ case '.':
10358
+ this.output(3 /* TypeIds.Constant */, 19 /* Constants.Dot */);
10359
+ return true;
10360
+ case 'id':
10361
+ this.output(3 /* TypeIds.Constant */, 20 /* Constants.Id */);
10362
+ return true;
10363
+ case 'ref':
10364
+ this.output(3 /* TypeIds.Constant */, 21 /* Constants.Ref */);
10365
+ return true;
10366
+ default:
10367
+ return false;
10368
+ }
10369
+ }
10122
10370
  outputString(value) {
10123
10371
  const s = this.stringNeedsJsonEscape$(value) ? JSON.stringify(value) : QUOTE + value + QUOTE;
10124
10372
  let angleBracketIdx = -1;
@@ -10136,6 +10384,9 @@ class Serializer {
10136
10384
  writeRootRefPath(path) {
10137
10385
  this.$writer$.writeRootRefPath(path);
10138
10386
  }
10387
+ writeRootRefDelta(id, base) {
10388
+ this.$writer$.writeRootRefDelta(id, base);
10389
+ }
10139
10390
  outputStringChunks(chunks) {
10140
10391
  this.$writer$.write(QUOTE);
10141
10392
  for (let i = 0; i < chunks.length; i++) {
@@ -10146,6 +10397,9 @@ class Serializer {
10146
10397
  else if (typeof chunk === 'number') {
10147
10398
  this.writeRootRef(chunk);
10148
10399
  }
10400
+ else if ('base' in chunk) {
10401
+ this.writeRootRefDelta(chunk.id, chunk.base);
10402
+ }
10149
10403
  else {
10150
10404
  this.writeRootRefPath(chunk.path);
10151
10405
  }
@@ -10275,6 +10529,9 @@ class Serializer {
10275
10529
  if (value.length === 0) {
10276
10530
  this.output(3 /* TypeIds.Constant */, 4 /* Constants.EmptyString */);
10277
10531
  }
10532
+ else if (this.outputShortStringConstant$(value)) {
10533
+ break;
10534
+ }
10278
10535
  else {
10279
10536
  // If the string is short, we output directly
10280
10537
  // Very short strings add overhead to tracking
@@ -10314,27 +10571,38 @@ class Serializer {
10314
10571
  }
10315
10572
  else if (isQrl(value)) {
10316
10573
  if (this.getSeenRefOrOutput(value, index)) {
10317
- const [chunk, symbol, captures] = qrlToString(this.$serializationContext$, value, true);
10574
+ const [chunk, symbol, captureDeltasFromZero] = qrlToString(this.$serializationContext$, value, true);
10318
10575
  let data;
10319
10576
  if (chunk !== '') {
10320
10577
  // 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(' ');
10578
+ const chunkRootId = this.$serializationContext$.$addRoot$(chunk);
10579
+ const symbolRootId = this.$serializationContext$.$addRoot$(symbol);
10580
+ const captureDeltas = captureDeltasFromZero
10581
+ ? this.rebaseQrlCaptureDeltas$(symbolRootId, captureDeltasFromZero)
10582
+ : null;
10583
+ data = [chunkRootId, '#', { id: symbolRootId, base: chunkRootId }];
10584
+ if (captureDeltasFromZero) {
10585
+ let previousRootId = 0;
10586
+ let previousOutputBase = symbolRootId;
10587
+ let start = 0;
10328
10588
  data.push('#');
10329
- for (let i = 0; i < captureIds.length; i++) {
10330
- if (i > 0) {
10331
- data.push(' ');
10589
+ for (let i = 0; i <= captureDeltasFromZero.length; i++) {
10590
+ if (i === captureDeltasFromZero.length || captureDeltasFromZero[i] === ' ') {
10591
+ if (i > start) {
10592
+ const captureId = previousRootId + Number(captureDeltasFromZero.slice(start, i));
10593
+ if (start > 0) {
10594
+ data.push(' ');
10595
+ }
10596
+ data.push({ id: captureId, base: previousOutputBase });
10597
+ previousRootId = captureId;
10598
+ previousOutputBase = captureId;
10599
+ }
10600
+ start = i + 1;
10332
10601
  }
10333
- data.push(Number(captureIds[i]));
10334
10602
  }
10335
10603
  }
10336
10604
  // Since we map QRLs to strings, we need to keep track of this secondary mapping
10337
- const qrlKey = data.join('');
10605
+ const qrlKey = `${chunkRootId}#${symbolRootId - chunkRootId}${captureDeltas ? '#' + captureDeltas : ''}`;
10338
10606
  const existing = this.$qrlMap$.get(qrlKey);
10339
10607
  if (existing) {
10340
10608
  // We encountered the same QRL again, make it a root
@@ -10402,15 +10670,21 @@ class Serializer {
10402
10670
  ]);
10403
10671
  }
10404
10672
  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
- ]);
10673
+ const { $scopedStyleIdPrefix$, $isConst$ } = value.data;
10674
+ if ($scopedStyleIdPrefix$ === null) {
10675
+ this.output($isConst$ ? 42 /* TypeIds.SubscriptionDataConstTrue */ : 43 /* TypeIds.SubscriptionDataConstFalse */, 0);
10676
+ }
10677
+ else {
10678
+ this.output(39 /* TypeIds.SubscriptionData */, [$scopedStyleIdPrefix$, $isConst$]);
10679
+ }
10410
10680
  }
10411
10681
  else if (value instanceof EffectSubscription) {
10412
- // TODO no data if [null, true]
10413
- this.output(40 /* TypeIds.EffectSubscription */, [value.consumer, value.property, value.data]);
10682
+ if (value.data === null) {
10683
+ this.output(44 /* TypeIds.EffectSubscriptionNoData */, [value.consumer, value.property]);
10684
+ }
10685
+ else {
10686
+ this.output(40 /* TypeIds.EffectSubscription */, [value.consumer, value.property, value.data]);
10687
+ }
10414
10688
  }
10415
10689
  else if (value instanceof SubscriptionPatch) {
10416
10690
  this.output(41 /* TypeIds.SubscriptionPatch */, [value.rootId, value.subscriptions]);
@@ -10464,7 +10738,7 @@ class Serializer {
10464
10738
  }
10465
10739
  }
10466
10740
  }
10467
- this.output(5 /* TypeIds.Object */, out.length ? out : 0);
10741
+ this.output(5 /* TypeIds.Object */, out.length ? out : EMPTY_OBJECT_PAYLOAD);
10468
10742
  }
10469
10743
  }
10470
10744
  else if (this.$serializationContext$.$isDomRef$(value)) {
@@ -10497,16 +10771,18 @@ class Serializer {
10497
10771
  let v = value.$untrackedValue$;
10498
10772
  const shouldAlwaysSerialize = value.$flags$ & 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
10499
10773
  const shouldNeverSerialize = value.$flags$ & 8 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
10500
- const isInvalid = value.$flags$ & 1 /* SignalFlags.INVALID */;
10774
+ const isInvalid = value.$flags$ & 1 /* ComputedSignalFlags.INVALID */;
10501
10775
  const isSkippable = fastSkipSerialize(value.$untrackedValue$);
10502
- const isAsync = value instanceof AsyncSignalImpl;
10776
+ // async-mode computeds carry the same engine state and must round-trip like AsyncSignals
10777
+ const isAsync = !!(value.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */);
10778
+ const isErrored = isAsync && !!value.$untrackedError$;
10503
10779
  const expires = isAsync && value.$expires$ !== 0 ? value.$expires$ : undefined;
10504
10780
  const concurrency = isAsync && value.$concurrency$ !== 1 ? value.$concurrency$ : undefined;
10505
10781
  const timeout = isAsync && value.$timeoutMs$ !== 0 ? value.$timeoutMs$ : undefined;
10506
10782
  // Send the flags but remove the serialization bits and default to 0 when undefined
10507
10783
  const asyncFlags = (isAsync && value.$flags$ & -25 /* SerializationSignalFlags.SERIALIZATION_ALL_STRATEGIES */) ||
10508
10784
  undefined;
10509
- if (isInvalid || isSkippable) {
10785
+ if (isInvalid || isSkippable || isErrored) {
10510
10786
  v = NEEDS_COMPUTATION;
10511
10787
  }
10512
10788
  else if (shouldAlwaysSerialize) {
@@ -10554,30 +10830,6 @@ class Serializer {
10554
10830
  else if (value instanceof Date) {
10555
10831
  this.output(7 /* TypeIds.Date */, Number.isNaN(value.valueOf()) ? '' : value.valueOf());
10556
10832
  }
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
10833
  else if (value instanceof RegExp) {
10582
10834
  this.output(8 /* TypeIds.Regex */, value.toString());
10583
10835
  }
@@ -10650,7 +10902,7 @@ class Serializer {
10650
10902
  this.output(37 /* TypeIds.JSXNode */, out);
10651
10903
  }
10652
10904
  else if (value instanceof Task) {
10653
- const out = [value.$qrl$, value.$flags$, value.$index$, value.$el$, value.$state$];
10905
+ const out = [value.$qrl$, value.$flags$, value.$index$, value.$el$];
10654
10906
  while (out[out.length - 1] === undefined) {
10655
10907
  out.pop();
10656
10908
  }
@@ -10704,6 +10956,30 @@ class Serializer {
10704
10956
  this.output(2 /* TypeIds.ForwardRef */, this.getForwardRefId(forwardRefId));
10705
10957
  }
10706
10958
  }
10959
+ else if (this.$hasTemporal$ && value instanceof Temporal.Duration) {
10960
+ this.output(15 /* TypeIds.TemporalDuration */, value.toJSON());
10961
+ }
10962
+ else if (this.$hasTemporal$ && value instanceof Temporal.Instant) {
10963
+ this.output(16 /* TypeIds.TemporalInstant */, value.toJSON());
10964
+ }
10965
+ else if (this.$hasTemporal$ && value instanceof Temporal.PlainDate) {
10966
+ this.output(17 /* TypeIds.TemporalPlainDate */, value.toJSON());
10967
+ }
10968
+ else if (this.$hasTemporal$ && value instanceof Temporal.PlainDateTime) {
10969
+ this.output(18 /* TypeIds.TemporalPlainDateTime */, value.toJSON());
10970
+ }
10971
+ else if (this.$hasTemporal$ && value instanceof Temporal.PlainMonthDay) {
10972
+ this.output(19 /* TypeIds.TemporalPlainMonthDay */, value.toJSON());
10973
+ }
10974
+ else if (this.$hasTemporal$ && value instanceof Temporal.PlainTime) {
10975
+ this.output(20 /* TypeIds.TemporalPlainTime */, value.toJSON());
10976
+ }
10977
+ else if (this.$hasTemporal$ && value instanceof Temporal.PlainYearMonth) {
10978
+ this.output(21 /* TypeIds.TemporalPlainYearMonth */, value.toJSON());
10979
+ }
10980
+ else if (this.$hasTemporal$ && value instanceof Temporal.ZonedDateTime) {
10981
+ this.output(22 /* TypeIds.TemporalZonedDateTime */, value.toJSON());
10982
+ }
10707
10983
  else if (vnode_isVNode(value)) {
10708
10984
  this.output(3 /* TypeIds.Constant */, 0 /* Constants.Undefined */);
10709
10985
  }
@@ -10870,7 +11146,9 @@ const discoverValuesForVNodeData = (vnodeData, callback) => {
10870
11146
  if (attrValue == null ||
10871
11147
  typeof attrValue === 'string' ||
10872
11148
  (typeof attrValue === 'number' && key === ELEMENT_ID) ||
10873
- (key === ELEMENT_PROPS && isObjectEmpty(attrValue))) {
11149
+ (key === ELEMENT_PROPS &&
11150
+ (isObjectEmpty(attrValue) ||
11151
+ shouldSkipComponentProps(value, attrValue)))) {
10874
11152
  continue;
10875
11153
  }
10876
11154
  callback(attrValue);
@@ -10879,6 +11157,21 @@ const discoverValuesForVNodeData = (vnodeData, callback) => {
10879
11157
  }
10880
11158
  };
10881
11159
  const isSsrAttrs = (value) => typeof value === 'object' && value !== null && !isObjectEmpty(value);
11160
+ const shouldSkipComponentProps = (attrs, value, isDevMode = isDev) => {
11161
+ if (isDevMode || !Object.prototype.hasOwnProperty.call(attrs, OnRenderProp)) {
11162
+ return false;
11163
+ }
11164
+ const owner = value[_OWNER];
11165
+ return (isObjectEmpty(owner.varProps) &&
11166
+ !hasPropsEffects(value) &&
11167
+ !hasComponentBackRef(attrs[QBackRefs]));
11168
+ };
11169
+ const hasPropsEffects = (propsProxy) => {
11170
+ return !!propsProxy[_PROPS_HANDLER].$effects$?.size;
11171
+ };
11172
+ const hasComponentBackRef = (backRefs) => {
11173
+ return backRefs instanceof Map && backRefs.has(":" /* EffectProperty.COMPONENT */);
11174
+ };
10882
11175
  /**
10883
11176
  * When serializing the object we need check if it is URL, RegExp, Map, Set, etc. This is time
10884
11177
  * consuming. So if we could know that this is a basic object literal we could skip the check, and
@@ -11377,6 +11670,12 @@ const constantToName = (code) => {
11377
11670
  * @internal
11378
11671
  */
11379
11672
  function preprocessState(data, container, segmentId, startIndex = 0) {
11673
+ const iterator = preprocessStateIterator(data, container, startIndex);
11674
+ while (!iterator.next().done) {
11675
+ // Run synchronously for non-browser and non-container deserialization paths.
11676
+ }
11677
+ }
11678
+ function* preprocessStateIterator(data, container, startIndex = 0) {
11380
11679
  const isRootDeepRef = (type, value) => {
11381
11680
  return type === 1 /* TypeIds.RootRef */ && typeof value === 'string' && value.indexOf(' ') !== -1;
11382
11681
  };
@@ -11419,11 +11718,39 @@ function preprocessState(data, container, segmentId, startIndex = 0) {
11419
11718
  else if (isForwardRefsMap(data[i])) {
11420
11719
  container.$forwardRefs$ = data[i + 1];
11421
11720
  }
11721
+ yield;
11422
11722
  }
11423
11723
  }
11424
11724
 
11425
11725
  const resolvers = new WeakMap();
11426
- const pendingStoreTargets = new Map();
11726
+ const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
11727
+ const BASE64_REGEXP = /^[A-Za-z0-9+/]*$/;
11728
+ // Validate canonical unpadded base64 before its length controls allocation.
11729
+ const isCanonicalBase64 = (value) => {
11730
+ if (typeof value !== 'string' || !BASE64_REGEXP.test(value)) {
11731
+ return false;
11732
+ }
11733
+ const rest = value.length % 4;
11734
+ if (rest === 0) {
11735
+ return true;
11736
+ }
11737
+ if (rest === 1) {
11738
+ return false;
11739
+ }
11740
+ const lastValue = BASE64_ALPHABET.indexOf(value[value.length - 1]);
11741
+ return (lastValue & (rest === 2 ? 15 : 3)) === 0;
11742
+ };
11743
+ const beginDeserialization = (container) => {
11744
+ const ownsPendingStoreTargets = container.$pendingStoreTargets$ === undefined;
11745
+ container.$pendingStoreTargets$ ??= new Map();
11746
+ return ownsPendingStoreTargets;
11747
+ };
11748
+ const endDeserialization = (container, ownsPendingStoreTargets) => {
11749
+ if (ownsPendingStoreTargets) {
11750
+ container.$pendingStoreTargets$.clear();
11751
+ container.$pendingStoreTargets$ = undefined;
11752
+ }
11753
+ };
11427
11754
  const allocate = (container, typeId, value) => {
11428
11755
  switch (typeId) {
11429
11756
  case 0 /* TypeIds.Plain */:
@@ -11449,10 +11776,17 @@ const allocate = (container, typeId, value) => {
11449
11776
  case 9 /* TypeIds.QRL */: {
11450
11777
  let qrl;
11451
11778
  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);
11779
+ const serializedQrl = value;
11780
+ const firstHash = serializedQrl.indexOf('#');
11781
+ const secondHash = serializedQrl.indexOf('#', firstHash + 1);
11782
+ const chunkRootId = Number(serializedQrl.slice(0, firstHash));
11783
+ const symbolDelta = secondHash === -1
11784
+ ? Number(serializedQrl.slice(firstHash + 1))
11785
+ : Number(serializedQrl.slice(firstHash + 1, secondHash));
11786
+ const symbolRootId = chunkRootId + symbolDelta;
11787
+ const chunk = container.$getObjectById$(chunkRootId);
11788
+ const symbol = container.$getObjectById$(symbolRootId);
11789
+ qrl = createQRLWithBackChannel(chunk, symbol, secondHash !== -1 ? serializedQrl : null, container);
11456
11790
  }
11457
11791
  else {
11458
11792
  // Sync qrl
@@ -11461,7 +11795,7 @@ const allocate = (container, typeId, value) => {
11461
11795
  return qrl;
11462
11796
  }
11463
11797
  case 28 /* TypeIds.Task */:
11464
- return new Task(-1, -1, null, null, null, null);
11798
+ return new Task(-1, -1, null, null, null);
11465
11799
  case 6 /* TypeIds.URL */:
11466
11800
  return new URL(value);
11467
11801
  case 7 /* TypeIds.Date */:
@@ -11509,7 +11843,7 @@ const allocate = (container, typeId, value) => {
11509
11843
  const storeValue = allocate(container, t, v);
11510
11844
  const store = getOrCreateStore(storeValue, 0 /* StoreFlags.NONE */, container);
11511
11845
  if (needsInflation(t)) {
11512
- pendingStoreTargets.set(storeValue, { t, v });
11846
+ container.$pendingStoreTargets$.set(storeValue, { t, v });
11513
11847
  }
11514
11848
  // We must store the reference so it doesn't get deserialized again in inflate()
11515
11849
  data[0] = 0 /* TypeIds.Plain */;
@@ -11539,12 +11873,16 @@ const allocate = (container, typeId, value) => {
11539
11873
  // Don't leave unhandled promise rejections
11540
11874
  promise.catch(() => { });
11541
11875
  return promise;
11542
- case 27 /* TypeIds.Uint8Array */:
11876
+ case 27 /* TypeIds.Uint8Array */: {
11877
+ if (!isCanonicalBase64(value)) {
11878
+ throw qError(38 /* QError.invalidUint8ArrayPayload */);
11879
+ }
11543
11880
  const encodedLength = value.length;
11544
- const blocks = encodedLength >>> 2;
11545
- const rest = encodedLength & 3;
11881
+ const blocks = Math.floor(encodedLength / 4);
11882
+ const rest = encodedLength % 4;
11546
11883
  const decodedLength = blocks * 3 + (rest ? rest - 1 : 0);
11547
11884
  return new Uint8Array(decodedLength);
11885
+ }
11548
11886
  case 38 /* TypeIds.PropsProxy */:
11549
11887
  return createPropsProxy(null);
11550
11888
  case 10 /* TypeIds.VNode */:
@@ -11585,7 +11923,12 @@ const allocate = (container, typeId, value) => {
11585
11923
  }
11586
11924
  case 39 /* TypeIds.SubscriptionData */:
11587
11925
  return new SubscriptionData({});
11926
+ case 42 /* TypeIds.SubscriptionDataConstTrue */:
11927
+ return new SubscriptionData({ $scopedStyleIdPrefix$: null, $isConst$: true });
11928
+ case 43 /* TypeIds.SubscriptionDataConstFalse */:
11929
+ return new SubscriptionData({ $scopedStyleIdPrefix$: null, $isConst$: false });
11588
11930
  case 40 /* TypeIds.EffectSubscription */:
11931
+ case 44 /* TypeIds.EffectSubscriptionNoData */:
11589
11932
  return new EffectSubscription(null, null, null, null);
11590
11933
  case 41 /* TypeIds.SubscriptionPatch */:
11591
11934
  return new SubscriptionPatch();
@@ -12021,6 +12364,7 @@ function processJSXNode(ssr, enqueue, value, options) {
12021
12364
  if (isPromise(jsxOutput)) {
12022
12365
  // Defer reading QScopedStyle until after the promise resolves
12023
12366
  enqueue(async () => {
12367
+ await ssr.streamHandler.flush();
12024
12368
  const resolvedOutput = await jsxOutput;
12025
12369
  const compStyleComponentId = addComponentStylePrefix(host.getProp(QScopedStyle));
12026
12370
  enqueue(resolvedOutput);
@@ -12052,7 +12396,7 @@ function processJSXNode(ssr, enqueue, value, options) {
12052
12396
  function maybeAddPollingAsyncSignalToEagerResume(serializationCtx, signal) {
12053
12397
  // Unwrap if it's a WrappedSignalImpl
12054
12398
  const unwrappedSignal = signal instanceof WrappedSignalImpl ? signal.$unwrapIfSignal$() : signal;
12055
- if (unwrappedSignal instanceof AsyncSignalImpl) {
12399
+ if (unwrappedSignal instanceof ComputedSignalImpl) {
12056
12400
  const expires = unwrappedSignal.$expires$;
12057
12401
  // Don't check for $effects$ here - effects are added later during tracking.
12058
12402
  // The AsyncSignal's polling mechanism will check for effects before scheduling.
@@ -12235,7 +12579,7 @@ function setEvent(serializationCtx, key, rawValue, hasMovedCaptures) {
12235
12579
  *
12236
12580
  * For internal qrls (starting with `_`) we assume that they do the right thing.
12237
12581
  */
12238
- if (!qrl.$symbol$.startsWith('_') && (qrl.$captures$?.length || hasMovedCaptures)) {
12582
+ if (!qrl.$symbol$.startsWith('_') && (Array.isArray(qrl.$captures$) || hasMovedCaptures)) {
12239
12583
  qrl = createQRL(null, '_run', _run, null, [qrl]);
12240
12584
  }
12241
12585
  return qrlToChunks(serializationCtx, qrl);
@@ -12449,22 +12793,24 @@ const useOnEventsSequentialScope = () => {
12449
12793
  const _hmr = function (event, element) {
12450
12794
  // Deserialize captures from `this` when called via qwikloader/attribute dispatch
12451
12795
  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;
12796
+ return whenContainerDataReady(container, () => {
12797
+ if (typeof this === 'string') {
12798
+ setCaptures(deserializeCaptureDeltas(container, this));
12799
+ }
12800
+ const devPath = _captures?.[1];
12801
+ const hmrPath = devPath ?? element.getAttribute('data-qwik-inspector');
12802
+ if (!hmrPath || !event.detail.files.some((file) => hmrPath.startsWith(file))) {
12803
+ return;
12804
+ }
12805
+ const host = _captures?.[0];
12806
+ if (!host || host.flags & 32 /* VNodeFlags.Deleted */) {
12807
+ return;
12808
+ }
12809
+ markVNodeDirty(container, host, 4 /* ChoreBits.COMPONENT */);
12810
+ // Mark HMR as handled
12811
+ const doc = element.ownerDocument;
12812
+ doc.__hmrDone = doc.__hmrT;
12813
+ });
12468
12814
  };
12469
12815
  let hmrQrl;
12470
12816
  /**
@@ -12569,22 +12915,93 @@ const isSafeObjectKV = (key, value) => {
12569
12915
  (typeof value !== 'function' || !dangerousObjectKeys.has(key)));
12570
12916
  };
12571
12917
  const inflate = (container, target, typeId, data) => {
12918
+ const ownsPendingStoreTargets = beginDeserialization(container);
12919
+ try {
12920
+ const iterator = inflateIterator(container, target, typeId, data);
12921
+ while (!iterator.next().done) {
12922
+ // Run synchronously for lazy deserialization paths.
12923
+ }
12924
+ }
12925
+ finally {
12926
+ endDeserialization(container, ownsPendingStoreTargets);
12927
+ }
12928
+ };
12929
+ function* eagerDeserializeArrayIterator(container, data, output = Array(data.length / 2)) {
12930
+ for (let i = 0; i < data.length; i += 2) {
12931
+ output[i / 2] = yield* deserializeDataIterator(container, data[i], data[i + 1]);
12932
+ yield;
12933
+ }
12934
+ return output;
12935
+ }
12936
+ function* eagerDeserializeStateIterator(container, data, output = Array(data.length / 2)) {
12937
+ const ownsPendingStoreTargets = beginDeserialization(container);
12938
+ const length = data.length / 2;
12939
+ const allocated = new Uint8Array(length);
12940
+ const previousGetObjectById = container.$getObjectById$;
12941
+ const allocateRoot = (index) => {
12942
+ if (!allocated[index]) {
12943
+ allocated[index] = 1;
12944
+ const typeIndex = index * 2;
12945
+ const typeId = data[typeIndex];
12946
+ const value = data[typeIndex + 1];
12947
+ output[index] = typeId === 0 /* TypeIds.Plain */ ? value : allocate(container, typeId, value);
12948
+ }
12949
+ return output[index];
12950
+ };
12951
+ container.$getObjectById$ = (id) => allocateRoot(typeof id === 'string' ? parseInt(id, 10) : id);
12952
+ try {
12953
+ for (let i = 0; i < length; i++) {
12954
+ allocateRoot(i);
12955
+ yield;
12956
+ }
12957
+ for (let i = 0; i < length; i++) {
12958
+ const typeIndex = i * 2;
12959
+ const typeId = data[typeIndex];
12960
+ const value = data[typeIndex + 1];
12961
+ const propValue = output[i];
12962
+ data[typeIndex] = 0 /* TypeIds.Plain */;
12963
+ data[typeIndex + 1] = propValue;
12964
+ if (needsInflation(typeId)) {
12965
+ yield* inflateIterator(container, propValue, typeId, value);
12966
+ }
12967
+ yield;
12968
+ }
12969
+ }
12970
+ finally {
12971
+ container.$getObjectById$ = previousGetObjectById;
12972
+ endDeserialization(container, ownsPendingStoreTargets);
12973
+ }
12974
+ return output;
12975
+ }
12976
+ function* deserializeDataIterator(container, typeId, value) {
12977
+ if (typeId === 0 /* TypeIds.Plain */) {
12978
+ return value;
12979
+ }
12980
+ const propValue = allocate(container, typeId, value);
12981
+ if (needsInflation(typeId)) {
12982
+ yield* inflateIterator(container, propValue, typeId, value);
12983
+ }
12984
+ return propValue;
12985
+ }
12986
+ function* inflateIterator(container, target, typeId, data) {
12572
12987
  if (typeId === 0 /* TypeIds.Plain */) {
12573
12988
  // Already processed
12574
12989
  return;
12575
12990
  }
12991
+ if (typeId === 5 /* TypeIds.Object */ && data !== EMPTY_OBJECT_PAYLOAD && !Array.isArray(data)) {
12992
+ throw new Error('Invalid Object payload');
12993
+ }
12576
12994
  // Restore the complex data, special case for Array
12577
12995
  if (typeId !== 4 /* TypeIds.Array */ && Array.isArray(data)) {
12578
- data = _eagerDeserializeArray(container, data);
12996
+ data = yield* eagerDeserializeArrayIterator(container, data);
12579
12997
  }
12580
12998
  switch (typeId) {
12581
12999
  case 4 /* TypeIds.Array */:
12582
13000
  // Arrays are special, we need to fill the array in place
12583
- _eagerDeserializeArray(container, data, target);
13001
+ yield* eagerDeserializeArrayIterator(container, data, target);
12584
13002
  break;
12585
13003
  case 5 /* TypeIds.Object */:
12586
- if (data === 0) {
12587
- // Special case, was an empty object
13004
+ if (data === EMPTY_OBJECT_PAYLOAD) {
12588
13005
  break;
12589
13006
  }
12590
13007
  for (let i = 0; i < data.length; i += 2) {
@@ -12596,37 +13013,65 @@ const inflate = (container, target, typeId, data) => {
12596
13013
  target[key] = value;
12597
13014
  }
12598
13015
  break;
12599
- case 28 /* TypeIds.Task */:
13016
+ case 25 /* TypeIds.Set */: {
13017
+ const set = target;
13018
+ const d = data;
13019
+ for (let i = 0; i < d.length; i++) {
13020
+ set.add(d[i]);
13021
+ yield;
13022
+ }
13023
+ break;
13024
+ }
13025
+ case 26 /* TypeIds.Map */: {
13026
+ const map = target;
13027
+ const d = data;
13028
+ for (let i = 0; i < d.length; i++) {
13029
+ map.set(d[i++], d[i]);
13030
+ yield;
13031
+ }
13032
+ break;
13033
+ }
13034
+ case 24 /* TypeIds.Promise */: {
13035
+ const promise = target;
13036
+ const [resolved, result] = data;
13037
+ const [resolve, reject] = resolvers.get(promise);
13038
+ if (resolved) {
13039
+ // Native Promises cannot fulfill with another Promise.
13040
+ if (resolvers.has(result)) {
13041
+ throw qError(37 /* QError.invalidPromiseDependency */);
13042
+ }
13043
+ resolve(result);
13044
+ }
13045
+ else {
13046
+ reject(result);
13047
+ }
13048
+ break;
13049
+ }
13050
+ case 23 /* TypeIds.Error */: {
13051
+ const d = data;
13052
+ target.message = d[0];
13053
+ for (let i = 1; i < d.length; i += 2) {
13054
+ const key = d[i];
13055
+ const value = d[i + 1];
13056
+ if (isSafeObjectKV(key, value)) {
13057
+ target[key] = value;
13058
+ }
13059
+ yield;
13060
+ }
13061
+ break;
13062
+ }
13063
+ case 28 /* TypeIds.Task */: {
12600
13064
  const task = target;
12601
13065
  const v = data;
12602
13066
  task.$qrl$ = v[0];
12603
13067
  task.$flags$ = v[1];
12604
13068
  task.$index$ = v[2];
12605
13069
  task.$el$ = v[3];
12606
- task.$state$ = v[4];
12607
13070
  break;
13071
+ }
12608
13072
  case 29 /* TypeIds.Component */:
12609
13073
  target[SERIALIZABLE_STATE][0] = data[0];
12610
13074
  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
13075
  case 30 /* TypeIds.Signal */: {
12631
13076
  const signal = target;
12632
13077
  const d = data;
@@ -12642,7 +13087,7 @@ const inflate = (container, target, typeId, data) => {
12642
13087
  signal.$args$ = d[1];
12643
13088
  signal.$untrackedValue$ = NEEDS_COMPUTATION;
12644
13089
  signal.$flags$ = d[2];
12645
- signal.$flags$ |= 1 /* SignalFlags.INVALID */;
13090
+ signal.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
12646
13091
  signal.$hostElement$ = d[3];
12647
13092
  signal.$effects$ = new Set(d.slice(4));
12648
13093
  inflateWrappedSignalValue(signal);
@@ -12668,7 +13113,7 @@ const inflate = (container, target, typeId, data) => {
12668
13113
  asyncSignal.$flags$ = d[5] ?? 0;
12669
13114
  if (asyncSignal.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
12670
13115
  // If it's client only, it was serialized because it pretended to be loading
12671
- asyncSignal.$untrackedLoading$ = true;
13116
+ asyncSignal.$untrackedPending$ = true;
12672
13117
  }
12673
13118
  const hasValue = d.length > 6;
12674
13119
  if (hasValue) {
@@ -12676,7 +13121,7 @@ const inflate = (container, target, typeId, data) => {
12676
13121
  }
12677
13122
  // can happen when never serialize etc
12678
13123
  if (asyncSignal.$untrackedValue$ === NEEDS_COMPUTATION) {
12679
- asyncSignal.$flags$ |= 1 /* SignalFlags.INVALID */;
13124
+ asyncSignal.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
12680
13125
  }
12681
13126
  // Handle old format (negative = no poll) and new format (always positive, flag in d[5])
12682
13127
  const rawExpires = (d[7] ?? 0);
@@ -12718,82 +13163,42 @@ const inflate = (container, target, typeId, data) => {
12718
13163
  }
12719
13164
  if (typeId !== 34 /* TypeIds.SerializerSignal */ && computed.$untrackedValue$ !== NEEDS_COMPUTATION) {
12720
13165
  // If we have a value after SSR, it will always be mean the signal was not invalid
12721
- // The serialized signal is always left invalid so it can recreate the custom object
12722
- computed.$flags$ &= -2 /* SignalFlags.INVALID */;
12723
- }
12724
- restoreEffectBackRefForEffects(computed.$effects$, computed);
12725
- break;
12726
- }
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
- case 36 /* TypeIds.FormData */: {
12736
- const formData = target;
12737
- const d = data;
12738
- for (let i = 0; i < d.length; i++) {
12739
- formData.append(d[i++], d[i]);
12740
- }
12741
- break;
12742
- }
12743
- case 37 /* TypeIds.JSXNode */: {
12744
- const jsx = target;
12745
- const [type, key, varProps, constProps, children, toSort] = data;
12746
- jsx.type = type;
12747
- jsx.key = key;
12748
- jsx.varProps = varProps;
12749
- jsx.constProps = constProps || null;
12750
- jsx.children = children;
12751
- jsx.toSort = !!toSort;
12752
- break;
12753
- }
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]);
13166
+ // The serialized signal is always left invalid so it can recreate the custom object
13167
+ computed.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
12759
13168
  }
13169
+ restoreEffectBackRefForEffects(computed.$effects$, computed);
12760
13170
  break;
12761
13171
  }
12762
- case 26 /* TypeIds.Map */: {
12763
- const map = target;
13172
+ case 36 /* TypeIds.FormData */: {
13173
+ const formData = target;
12764
13174
  const d = data;
12765
13175
  for (let i = 0; i < d.length; i++) {
12766
- map.set(d[i++], d[i]);
13176
+ formData.append(d[i++], d[i]);
13177
+ yield;
12767
13178
  }
12768
13179
  break;
12769
13180
  }
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
- }
13181
+ case 37 /* TypeIds.JSXNode */: {
13182
+ const jsx = target;
13183
+ const [type, key, varProps, constProps, children, toSort] = data;
13184
+ jsx.type = type;
13185
+ jsx.key = key;
13186
+ jsx.varProps = varProps;
13187
+ jsx.constProps = constProps || null;
13188
+ jsx.children = children;
13189
+ jsx.toSort = !!toSort;
12780
13190
  break;
12781
13191
  }
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 */:
13192
+ case 38 /* TypeIds.PropsProxy */: {
12792
13193
  const propsProxy = target;
12793
13194
  const d = data;
12794
13195
  let owner = d[0];
12795
13196
  if (owner === _UNINITIALIZED) {
12796
- owner = new JSXNodeImpl(Fragment, d[1], d[2], null, 0, null);
13197
+ owner = new JSXNodeImpl(Fragment, null, null, null, 0, null);
13198
+ // Important: var/const props can be eager-deserialization placeholders. (empty objects)
13199
+ // Do not let JSXNodeImpl constructor normalize {} to EMPTY_OBJ/null.
13200
+ owner.varProps = d[1];
13201
+ owner.constProps = d[2];
12797
13202
  owner._proxy = propsProxy;
12798
13203
  }
12799
13204
  propsProxy[_OWNER] = owner;
@@ -12801,12 +13206,16 @@ const inflate = (container, target, typeId, data) => {
12801
13206
  propsHandler.$effects$ = d[3];
12802
13207
  restoreEffectBackRefForEffectsMap(propsHandler.$effects$, propsProxy);
12803
13208
  break;
13209
+ }
12804
13210
  case 39 /* TypeIds.SubscriptionData */: {
12805
13211
  const effectData = target;
12806
13212
  effectData.data.$scopedStyleIdPrefix$ = data[0];
12807
13213
  effectData.data.$isConst$ = data[1];
12808
13214
  break;
12809
13215
  }
13216
+ case 42 /* TypeIds.SubscriptionDataConstTrue */:
13217
+ case 43 /* TypeIds.SubscriptionDataConstFalse */:
13218
+ break;
12810
13219
  case 40 /* TypeIds.EffectSubscription */: {
12811
13220
  const effectSub = target;
12812
13221
  const d = data;
@@ -12823,29 +13232,43 @@ const inflate = (container, target, typeId, data) => {
12823
13232
  patch.subscriptions = d[1];
12824
13233
  break;
12825
13234
  }
13235
+ case 44 /* TypeIds.EffectSubscriptionNoData */: {
13236
+ const effectSub = target;
13237
+ const d = data;
13238
+ effectSub.consumer = d[0];
13239
+ effectSub.property = d[1];
13240
+ effectSub.data = null;
13241
+ restoreEffectBackRefForConsumer(effectSub);
13242
+ break;
13243
+ }
13244
+ case 27 /* TypeIds.Uint8Array */: {
13245
+ const bytes = target;
13246
+ const buf = atob(data);
13247
+ for (let j = 0; j < buf.length; j++) {
13248
+ bytes[j] = buf.charCodeAt(j);
13249
+ if ((j & 31) === 31) {
13250
+ yield;
13251
+ }
13252
+ }
13253
+ break;
13254
+ }
13255
+ case 35 /* TypeIds.Store */: {
13256
+ const store = unwrapStore(target);
13257
+ const storeTarget = container.$pendingStoreTargets$.get(store);
13258
+ if (storeTarget) {
13259
+ container.$pendingStoreTargets$.delete(store);
13260
+ yield* inflateIterator(container, store, storeTarget.t, storeTarget.v);
13261
+ }
13262
+ const [, flags, effects] = data;
13263
+ const storeHandler = getStoreHandler(target);
13264
+ storeHandler.$flags$ = flags;
13265
+ storeHandler.$effects$ = effects;
13266
+ restoreEffectBackRefForEffectsMap(storeHandler.$effects$, store);
13267
+ break;
13268
+ }
12826
13269
  default:
12827
13270
  throw qError(16 /* QError.serializeErrorNotImplemented */, [typeId]);
12828
13271
  }
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
13272
  }
12850
13273
  function inflateWrappedSignalValue(signal) {
12851
13274
  if (signal.$hostElement$ !== null && vnode_isVNode(signal.$hostElement$)) {
@@ -12956,15 +13379,21 @@ class DeserializationHandler {
12956
13379
  return value;
12957
13380
  }
12958
13381
  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);
13382
+ const ownsPendingStoreTargets = beginDeserialization(container);
13383
+ try {
13384
+ const propValue = allocate(container, typeId, value);
13385
+ Reflect.set(target, property, propValue);
13386
+ this.$data$[idx] = 0 /* TypeIds.Plain */;
13387
+ this.$data$[idx + 1] = propValue;
13388
+ /** We stored the reference, so now we can inflate, allowing cycles */
13389
+ if (needsInflation(typeId)) {
13390
+ inflate(container, propValue, typeId, value);
13391
+ }
13392
+ return propValue;
13393
+ }
13394
+ finally {
13395
+ endDeserialization(container, ownsPendingStoreTargets);
12966
13396
  }
12967
- return propValue;
12968
13397
  }
12969
13398
  has(target, property) {
12970
13399
  if (property === SERIALIZER_PROXY_UNWRAP) {
@@ -12988,6 +13417,54 @@ class DeserializationHandler {
12988
13417
  }
12989
13418
  }
12990
13419
 
13420
+ const YIELD_INTERVAL = 10;
13421
+ const scheduleYieldingIterator = (state) => {
13422
+ if (!state.$scheduled$) {
13423
+ state.$scheduled$ = true;
13424
+ state.$schedule$();
13425
+ }
13426
+ };
13427
+ const runYieldingIterator = (state, onDone, onError) => {
13428
+ state.$scheduled$ = false;
13429
+ const deadline = performance.now() + YIELD_INTERVAL;
13430
+ let count = 0;
13431
+ try {
13432
+ while (true) {
13433
+ const result = state.$iterator$.next();
13434
+ if (result.done) {
13435
+ onDone(result.value);
13436
+ return;
13437
+ }
13438
+ // Sampling the clock every 32 steps keeps `performance.now()` out of the hottest path.
13439
+ if ((++count & 31) === 0 && performance.now() >= deadline) {
13440
+ scheduleYieldingIterator(state);
13441
+ return;
13442
+ }
13443
+ }
13444
+ }
13445
+ catch (error) {
13446
+ onError(error);
13447
+ }
13448
+ };
13449
+ const createYieldingIteratorState = (iterator, onDone, onError) => {
13450
+ const state = {
13451
+ $iterator$: iterator,
13452
+ $schedule$: undefined,
13453
+ $scheduled$: false,
13454
+ };
13455
+ const schedule = createMacroTask(() => {
13456
+ runYieldingIterator(state, (value) => {
13457
+ schedule.$destroy$?.();
13458
+ onDone(value);
13459
+ }, (error) => {
13460
+ schedule.$destroy$?.();
13461
+ onError(error);
13462
+ });
13463
+ });
13464
+ state.$schedule$ = schedule;
13465
+ return state;
13466
+ };
13467
+
12991
13468
  /**
12992
13469
  * Serialize data to string using SerializationContext.
12993
13470
  *
@@ -13000,12 +13477,12 @@ async function _serialize(data) {
13000
13477
  return serializationContext.$writer$.toString();
13001
13478
  }
13002
13479
  /**
13003
- * Deserialize data from string to an array of objects.
13480
+ * Deserialize data from string.
13004
13481
  *
13005
13482
  * @param rawStateData - Data to deserialize
13006
13483
  * @internal
13007
13484
  */
13008
- function _deserialize(rawStateData) {
13485
+ async function _deserialize(rawStateData) {
13009
13486
  if (rawStateData == null) {
13010
13487
  throw new Error('No state data to deserialize');
13011
13488
  }
@@ -13013,9 +13490,18 @@ function _deserialize(rawStateData) {
13013
13490
  if (!Array.isArray(stateData) || stateData.length < 2 || typeof stateData[0] !== 'number') {
13014
13491
  throw new Error('Invalid state data');
13015
13492
  }
13016
- const container = _createDeserializeContainer(stateData);
13017
- return deserializeData(container, stateData[0], stateData[1]);
13493
+ const state = Array(stateData.length / 2);
13494
+ const container = createBaseDeserializeContainer(stateData, () => state);
13495
+ container.$state$ = state;
13496
+ await runDeserializeIterator(eagerDeserializeStateIterator(container, stateData, state));
13497
+ return state[0];
13018
13498
  }
13499
+ const runDeserializeIterator = (iterator) => {
13500
+ return new Promise((resolve, reject) => {
13501
+ const state = createYieldingIteratorState(iterator, resolve, reject);
13502
+ scheduleYieldingIterator(state);
13503
+ });
13504
+ };
13019
13505
  function getObjectById(id, stateData) {
13020
13506
  if (typeof id === 'string') {
13021
13507
  id = parseInt(id, 10);
@@ -13029,8 +13515,14 @@ function getObjectById(id, stateData) {
13029
13515
  function _createDeserializeContainer(stateData) {
13030
13516
  // eslint-disable-next-line prefer-const
13031
13517
  let state;
13518
+ const container = createBaseDeserializeContainer(stateData, () => state);
13519
+ state = wrapDeserializerProxy(container, stateData);
13520
+ container.$state$ = state;
13521
+ return container;
13522
+ }
13523
+ function createBaseDeserializeContainer(stateData, getState) {
13032
13524
  const container = {
13033
- $getObjectById$: (id) => getObjectById(id, state),
13525
+ $getObjectById$: (id) => getObjectById(id, getState()),
13034
13526
  $getForwardRef$: (id) => container.$forwardRefs$?.[Number(id)],
13035
13527
  getSyncFn: (_) => {
13036
13528
  const fn = () => { };
@@ -13041,8 +13533,6 @@ function _createDeserializeContainer(stateData) {
13041
13533
  $forwardRefs$: null,
13042
13534
  };
13043
13535
  preprocessState(stateData, container);
13044
- state = wrapDeserializerProxy(container, stateData);
13045
- container.$state$ = state;
13046
13536
  return container;
13047
13537
  }
13048
13538
 
@@ -13594,13 +14084,12 @@ const mergeSubscriptionSet = (container, producer, backRef, rootEffects, patchEf
13594
14084
  };
13595
14085
 
13596
14086
  const processedStatePatchScripts = new WeakMap();
13597
- /** @internal */
13598
- const processSegmentStateScripts = (container, segmentId) => {
14087
+ function* processSegmentStateScriptsIterator(container, segmentId) {
13599
14088
  if (!__EXPERIMENTAL__.suspense) {
13600
14089
  return;
13601
14090
  }
13602
14091
  const stateContainer = container;
13603
- const qwikStates = stateContainer.element.querySelectorAll(`${stateScriptSelector(stateContainer.$instanceHash$)}${QStatePatchAttrSelector}`);
14092
+ const qwikStates = stateContainer.element.querySelectorAll(`script[type="qwik/state"][q\\:instance="${stateContainer.$instanceHash$}"]${QStatePatchAttrSelector}`);
13604
14093
  const processedScripts = getProcessedStatePatchScripts(container);
13605
14094
  for (let i = 0; i < qwikStates.length; i++) {
13606
14095
  const stateScript = qwikStates[i];
@@ -13611,9 +14100,10 @@ const processSegmentStateScripts = (container, segmentId) => {
13611
14100
  continue;
13612
14101
  }
13613
14102
  processedScripts.add(stateScript);
13614
- processStatePatch(container, stateContainer, stateScript.textContent);
14103
+ yield* processStatePatch(container, stateContainer, stateScript.textContent);
14104
+ yield;
13615
14105
  }
13616
- };
14106
+ }
13617
14107
  const getProcessedStatePatchScripts = (container) => {
13618
14108
  let processedScripts = processedStatePatchScripts.get(container);
13619
14109
  if (!processedScripts) {
@@ -13622,20 +14112,18 @@ const getProcessedStatePatchScripts = (container) => {
13622
14112
  }
13623
14113
  return processedScripts;
13624
14114
  };
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));
14115
+ function* processStatePatch(container, stateContainer, textContent) {
14116
+ if (!textContent) {
14117
+ return;
13636
14118
  }
13637
- };
13638
- const appendStatePatchRoots = (container, stateContainer, rootStart, rawStateData) => {
14119
+ const [rootStart, rawStateData, forwardRefs, subscriptionPatchRootId] = JSON.parse(textContent);
14120
+ yield* appendStatePatchRoots(container, stateContainer, rootStart, rawStateData);
14121
+ mergeForwardRefs(stateContainer, forwardRefs || undefined);
14122
+ applySubscriptionPatches(container, subscriptionPatchRootId === undefined
14123
+ ? undefined
14124
+ : stateContainer.$getObjectById$(subscriptionPatchRootId));
14125
+ }
14126
+ function* appendStatePatchRoots(container, stateContainer, rootStart, rawStateData) {
13639
14127
  const currentRootCount = stateContainer.$rawStateData$.length / 2;
13640
14128
  if (rootStart !== currentRootCount) {
13641
14129
  if (qDev) {
@@ -13645,12 +14133,13 @@ const appendStatePatchRoots = (container, stateContainer, rootStart, rawStateDat
13645
14133
  }
13646
14134
  for (let i = 0; i < rawStateData.length; i++) {
13647
14135
  stateContainer.$rawStateData$[rootStart * 2 + i] = rawStateData[i];
14136
+ yield;
13648
14137
  }
13649
- preprocessState(stateContainer.$rawStateData$, container, undefined, rootStart * 2);
14138
+ yield* preprocessStateIterator(stateContainer.$rawStateData$, container, rootStart * 2);
13650
14139
  stateContainer.$stateData$ = wrapDeserializerProxy(container, stateContainer.$rawStateData$);
13651
14140
  stateContainer.$stateData$.length = stateContainer.$rawStateData$.length / 2;
13652
14141
  stateContainer.$rootForwardRefs$ = stateContainer.$forwardRefs$;
13653
- };
14142
+ }
13654
14143
  const mergeForwardRefs = (stateContainer, forwardRefs) => {
13655
14144
  if (!forwardRefs) {
13656
14145
  return;
@@ -13665,24 +14154,172 @@ const mergeForwardRefs = (stateContainer, forwardRefs) => {
13665
14154
  stateContainer.$forwardRefs$ = rootForwardRefs;
13666
14155
  };
13667
14156
 
13668
- // NOTE: we want to move this function to qwikloader, and therefore this function should not have any external dependencies
14157
+ const Q_SUSPENSE_RESOLVED = 'q:r';
14158
+ const Q_PATCH = 'q:patch';
14159
+ /**
14160
+ * Process the VNodeData script tags and store the VNodeData in the VNodeDataMap.
14161
+ *
14162
+ * The end result of this function is that each DOM element has the associated `VNodeData` attached
14163
+ * to it, to be used later `VNode` materialization. The "attachment" is done through the
14164
+ * `VNodeDataMap`.
14165
+ *
14166
+ * Run this function on startup to process the `<script type="qwik/vnode">` tags. The data in the
14167
+ * tags needs to be parsed and attached to the DOM elements. (We do this through `VNodeDataMap`)
14168
+ * `VNodeDataMap` is then used to lazily materialize the VNodes.
14169
+ *
14170
+ * Only one invocation of this function is needed per document/browser session.
14171
+ *
14172
+ * Below is an example of the kinds of constructs which need to be handled when dealing with
14173
+ * VNodeData deserialization.
14174
+ *
14175
+ * ```
14176
+ * <html q:container="paused">
14177
+ * <body>
14178
+ * <div q:container="paused">
14179
+ * <script type="qwik/vnode">...</script>
14180
+ * </div>
14181
+ * <div q:container="html">...</div>
14182
+ * before
14183
+ * <!--q:container=ABC-->
14184
+ * ...
14185
+ * <!--/q:container-->
14186
+ * after
14187
+ * <!--q:ignore=FOO-->
14188
+ * ...
14189
+ * <!--q:container-island=BAR-->
14190
+ * <div>some interactive island</div>
14191
+ * <!--/q:container-island-->
14192
+ * ...
14193
+ * <!--/q:ignore-->
14194
+ * <textarea q:container="text">...</textarea>
14195
+ * <script type="qwik/vnode">...</script>
14196
+ * </body>
14197
+ * </html>
14198
+ * ```
14199
+ *
14200
+ * Each `qwik/vnode` script assumes that the elements are numbered in depth first order. For this
14201
+ * reason, whenever the `processVNodeData` comes across a `q:container` it must ignore its
14202
+ * children.
14203
+ *
14204
+ * IMPLEMENTATION:
14205
+ *
14206
+ * - Stack to keep track of the current `q:container` being processed.
14207
+ * - Attach all `qwik/vnode` scripts (not the data contain within them) to the `q:container` element.
14208
+ * - Walk the tree and process each `q:container` element.
14209
+ */
14210
+ function processVNodeData(document, containerElement) {
14211
+ const qDocument = document;
14212
+ // Per-container gate: a walk already populated this container's refs → done.
14213
+ if (containerElement?.qVNodeRefs) {
14214
+ return;
14215
+ }
14216
+ // A document-wide walk is in flight; it will pick up this newly-added container too.
14217
+ if (qDocument.qVNodeDataStarted && !qDocument.qVNodeDataProcessed) {
14218
+ return;
14219
+ }
14220
+ qDocument.qVNodeDataStarted = true;
14221
+ qDocument.qVNodeData ||= new WeakMap();
14222
+ enqueueProcessVNodeDataJob(qDocument, processRootVNodeData(document));
14223
+ }
13669
14224
  function processOutOfOrderSegmentVNodeData(document, segmentId, contentNode) {
13670
14225
  if (!__EXPERIMENTAL__.suspense || !contentNode) {
13671
14226
  return;
13672
14227
  }
13673
14228
  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]);
14229
+ if (!qContainerElement) {
14230
+ return;
14231
+ }
14232
+ const script = qContainerElement.querySelector(`script[type="qwik/vnode"][q\\:r="${segmentId}"]:not([q\\:patch])`);
14233
+ const patches = qContainerElement.querySelectorAll(`script[type="qwik/vnode"][q\\:r="${segmentId}"][q\\:patch]`);
14234
+ enqueueProcessVNodeDataJob(document, processOutOfOrderSegmentVNodeDataIterator(document, segmentId, qContainerElement, contentNode, script?.textContent || undefined, patches));
14235
+ }
14236
+ const onVNodeDataReady = (document, callback) => {
14237
+ const qDocument = document;
14238
+ if (qDocument.qVNodeDataReady) {
14239
+ callback();
14240
+ }
14241
+ else {
14242
+ (qDocument.qVNodeDataCallbacks ||= []).push(callback);
14243
+ }
14244
+ };
14245
+ function enqueueProcessVNodeDataJob(qDocument, iterator) {
14246
+ const state = (qDocument.qVNodeDataState ||= {
14247
+ $queue$: [],
14248
+ $active$: null,
14249
+ });
14250
+ qDocument.qProcessVNodeDataPatch ||= (script) => enqueueProcessVNodeDataJob(qDocument, processVNodeDataPatch(qDocument, script));
14251
+ qDocument.qVNodeDataReady = false;
14252
+ state.$queue$.push(iterator);
14253
+ scheduleProcessVNodeData(qDocument, state);
14254
+ }
14255
+ function scheduleProcessVNodeData(qDocument, state) {
14256
+ if (state.$active$) {
14257
+ return;
14258
+ }
14259
+ const iterator = state.$queue$.shift();
14260
+ if (!iterator) {
14261
+ markVNodeDataReady(qDocument);
14262
+ return;
14263
+ }
14264
+ state.$active$ = createYieldingIteratorState(runProcessVNodeData(qDocument, state, iterator), () => {
14265
+ if (qDocument.qVNodeDataState === state) {
14266
+ state.$active$ = null;
14267
+ scheduleProcessVNodeData(qDocument, state);
14268
+ }
14269
+ }, (error) => {
14270
+ state.$active$ = null;
14271
+ logError(error);
14272
+ markVNodeDataReady(qDocument);
14273
+ });
14274
+ scheduleYieldingIterator(state.$active$);
14275
+ }
14276
+ function* runProcessVNodeData(qDocument, state, iterator) {
14277
+ while (qDocument.qVNodeDataState === state) {
14278
+ if (iterator.next().done) {
14279
+ return;
14280
+ }
14281
+ yield;
14282
+ }
14283
+ }
14284
+ function markVNodeDataReady(document) {
14285
+ if (document.qVNodeDataReady) {
14286
+ return;
14287
+ }
14288
+ document.qVNodeDataReady = true;
14289
+ document.qVNodeDataState = undefined;
14290
+ const callbacks = document.qVNodeDataCallbacks;
14291
+ document.qVNodeDataCallbacks = undefined;
14292
+ if (callbacks) {
14293
+ for (let i = 0; i < callbacks.length; i++) {
14294
+ callbacks[i]();
14295
+ }
14296
+ }
14297
+ }
14298
+ function* processRootVNodeData(document) {
14299
+ yield* processVNodeDataImpl(document);
14300
+ const qDocument = document;
14301
+ qDocument.qVNodeDataProcessed = true;
14302
+ // Re-open the gate so a container added after this drain re-runs a walk.
14303
+ qDocument.qVNodeDataStarted = false;
14304
+ }
14305
+ function* processOutOfOrderSegmentVNodeDataIterator(document, segmentId, segmentContainer, segmentContent, segmentVNodeData, patches) {
14306
+ yield* processVNodeDataImpl(document, segmentId, segmentContainer, segmentContent, segmentVNodeData);
14307
+ for (let i = 0; i < patches.length; i++) {
14308
+ yield* processVNodeDataPatch(document, patches[i]);
13679
14309
  }
13680
14310
  }
13681
- function processVNodeData(document) {
13682
- processVNodeDataImpl(document);
13683
- document.qVNodeDataProcessed = true;
14311
+ function* processVNodeDataPatch(document, script) {
14312
+ const qContainerElement = script?.closest('[q\\:container]');
14313
+ const patchSegment = script?.getAttribute(Q_SUSPENSE_RESOLVED);
14314
+ const contentNode = qContainerElement &&
14315
+ (patchSegment
14316
+ ? qContainerElement.querySelector(`[q\\:rp="${patchSegment}"]`)
14317
+ : qContainerElement);
14318
+ if (qContainerElement && contentNode) {
14319
+ yield* processVNodeDataImpl(document, patchSegment || undefined, qContainerElement, contentNode, script.textContent);
14320
+ }
13684
14321
  }
13685
- function processVNodeDataImpl(document, segmentId, segmentContainer, segmentContent, segmentVNodeData) {
14322
+ function* processVNodeDataImpl(document, segmentId, segmentContainer, segmentContent, segmentVNodeData) {
13686
14323
  const Q_CONTAINER = 'q:container';
13687
14324
  const Q_CONTAINER_END = '/' + Q_CONTAINER;
13688
14325
  const Q_PROPS_SEPARATOR = ':';
@@ -13691,9 +14328,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13691
14328
  const Q_IGNORE_END = '/' + Q_IGNORE;
13692
14329
  const Q_CONTAINER_ISLAND = 'q:container-island';
13693
14330
  const Q_CONTAINER_ISLAND_END = '/' + Q_CONTAINER_ISLAND;
13694
- const Q_SUSPENSE_RESOLVED = 'q:r';
13695
14331
  const Q_SUSPENSE_RESULT_PARENT = 'q:rp';
13696
- const Q_PATCH = 'q:patch';
13697
14332
  const qDocument = document;
13698
14333
  const vNodeDataMap = (qDocument.qVNodeData ||= new WeakMap());
13699
14334
  const prototype = document.body;
@@ -13711,7 +14346,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13711
14346
  const hasAttribute = prototype.hasAttribute;
13712
14347
  const getNodeType = getter(prototype, 'nodeType');
13713
14348
  // Process all of the `qwik/vnode` script tags by attaching them to the corresponding containers.
13714
- const attachVnodeDataAndRefs = (element) => {
14349
+ const attachVnodeDataAndRefs = function* (element) {
13715
14350
  const scripts = element.querySelectorAll('script[type="qwik/vnode"]');
13716
14351
  for (let i = 0; i < scripts.length; i++) {
13717
14352
  const script = scripts[i];
@@ -13719,6 +14354,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13719
14354
  qContainer.qVNodeRefs ||= new Map();
13720
14355
  if (script.hasAttribute(Q_PATCH)) {
13721
14356
  qDocument.qProcessVNodeDataPatch?.(script);
14357
+ yield;
13722
14358
  continue;
13723
14359
  }
13724
14360
  const scriptContent = script.textContent;
@@ -13729,14 +14365,21 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13729
14365
  else {
13730
14366
  qContainer.qVnodeData = scriptContent;
13731
14367
  }
14368
+ yield;
13732
14369
  }
13733
14370
  const shadowRoots = element.querySelectorAll('[q\\:shadowroot]');
13734
14371
  for (let i = 0; i < shadowRoots.length; i++) {
13735
14372
  const parent = shadowRoots[i];
13736
14373
  const shadowRoot = parent.shadowRoot;
13737
- shadowRoot && attachVnodeDataAndRefs(shadowRoot);
14374
+ if (shadowRoot) {
14375
+ yield* attachVnodeDataAndRefs(shadowRoot);
14376
+ }
14377
+ yield;
13738
14378
  }
13739
14379
  };
14380
+ ///////////////////////////////
14381
+ // Functions to consume the tree.
14382
+ ///////////////////////////////
13740
14383
  /**
13741
14384
  * Looks up which type of node this is in a monomorphic way which should be faster.
13742
14385
  *
@@ -13792,7 +14435,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13792
14435
  * @param exitNode The node which represents the last node and we should exit.
13793
14436
  * @param qVNodeRefs Place to store the VNodeRefs
13794
14437
  */
13795
- const walkContainer = (walker, containerNode, node, exitNode, vData, qVNodeRefs, qContainerElement, segmentId) => {
14438
+ const walkContainer = function* (walker, containerNode, node, exitNode, vData, qVNodeRefs, qContainerElement, segmentId) {
13796
14439
  const isSegment = __EXPERIMENTAL__.suspense && segmentId !== undefined;
13797
14440
  const vData_length = vData.length;
13798
14441
  /// Stores the current element index as the TreeWalker traverses the DOM.
@@ -13818,6 +14461,9 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13818
14461
  }
13819
14462
  return elementsToSkip;
13820
14463
  };
14464
+ if (!node) {
14465
+ return;
14466
+ }
13821
14467
  do {
13822
14468
  if (node === exitNode) {
13823
14469
  return;
@@ -13831,7 +14477,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13831
14477
  while (cursor && !(nextNode = nextSibling(cursor))) {
13832
14478
  cursor = cursor.parentNode;
13833
14479
  }
13834
- walkContainer(walker, container, node, nextNode, container.qVnodeData || '', container.qVNodeRefs, container);
14480
+ yield* walkContainer(walker, container, node, nextNode, container.qVnodeData || '', container.qVNodeRefs, container);
13835
14481
  }
13836
14482
  else if (nodeType === 16 /* NodeType.COMMENT_IGNORE_START */) {
13837
14483
  let islandNode = node;
@@ -13866,7 +14512,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13866
14512
  throw new Error(`<!--${node?.nodeValue}--> not closed!`);
13867
14513
  }
13868
14514
  } while (getFastNodeType(nextNode) !== 8 /* NodeType.COMMENT_SKIP_END */);
13869
- walkContainer(walker, node, node, nextNode, '', null, qContainerElement);
14515
+ yield* walkContainer(walker, node, node, nextNode, '', null, qContainerElement);
13870
14516
  }
13871
14517
  else if (nodeType === 6 /* NodeType.ELEMENT_SHADOW_ROOT_WRAPPER */) {
13872
14518
  // If we are in a shadow root, we need to get the shadow root element.
@@ -13874,7 +14520,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13874
14520
  const shadowRootContainer = node;
13875
14521
  const shadowRoot = shadowRootContainer?.shadowRoot;
13876
14522
  if (shadowRoot) {
13877
- walkContainer(
14523
+ yield* walkContainer(
13878
14524
  // we need to create a new walker for the shadow root
13879
14525
  document.createTreeWalker(shadowRoot, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */), null, shadowRoot, null, '', null, null);
13880
14526
  }
@@ -13937,51 +14583,42 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13937
14583
  }
13938
14584
  elementIdx++;
13939
14585
  if (__EXPERIMENTAL__.suspense && contentBoundaryId !== null) {
13940
- processSuspenseContentSegment(qContainerElement, node, contentBoundaryId);
14586
+ yield* processSuspenseContentSegment(qContainerElement, node, contentBoundaryId);
13941
14587
  nextNode = nextSibling(node);
13942
14588
  if (nextNode) {
13943
14589
  walker.currentNode = nextNode;
13944
14590
  }
13945
14591
  }
13946
14592
  }
14593
+ yield;
13947
14594
  } while ((node = nextNode || walker.nextNode()));
13948
14595
  };
13949
- const processVNodeDataScope = (qContainerElement, contentNode, vData, scopeSegmentId) => {
14596
+ const processVNodeDataScope = function* (qContainerElement, contentNode, vData, scopeSegmentId) {
13950
14597
  qContainerElement.qVNodeRefs ||= new Map();
13951
14598
  const scopeWalker = document.createTreeWalker(document, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */);
13952
14599
  scopeWalker.currentNode = contentNode;
13953
- walkContainer(scopeWalker, contentNode, contentNode, nextSibling(contentNode), vData, qContainerElement.qVNodeRefs, qContainerElement, scopeSegmentId);
14600
+ yield* walkContainer(scopeWalker, contentNode, contentNode, nextSibling(contentNode), vData, qContainerElement.qVNodeRefs, qContainerElement, scopeSegmentId);
13954
14601
  };
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);
14602
+ const processSuspenseContentSegment = function* (qContainerElement, contentNode, boundaryId, vData) {
14603
+ vData ||= qContainerElement?.qSegmentVnodeData?.get(boundaryId);
14604
+ if (qContainerElement && vData) {
14605
+ yield* processVNodeDataScope(qContainerElement, contentNode, vData, boundaryId);
13972
14606
  }
13973
14607
  };
13974
- if (__EXPERIMENTAL__.suspense && segmentId !== undefined) {
13975
- if (segmentContainer && segmentContent && segmentVNodeData) {
13976
- segmentContainer.qVNodeRefs ||= new Map();
13977
- processSuspenseContentSegment(segmentContainer, segmentContent, segmentId, segmentVNodeData);
14608
+ if (segmentContainer && segmentContent && segmentVNodeData !== undefined) {
14609
+ segmentContainer.qVNodeRefs ||= new Map();
14610
+ if (__EXPERIMENTAL__.suspense && segmentId !== undefined) {
14611
+ yield* processSuspenseContentSegment(segmentContainer, segmentContent, segmentId, segmentVNodeData);
14612
+ }
14613
+ else {
14614
+ yield* processVNodeDataScope(segmentContainer, segmentContent, segmentVNodeData);
13978
14615
  }
13979
14616
  return;
13980
14617
  }
13981
- attachVnodeDataAndRefs(document);
14618
+ yield* attachVnodeDataAndRefs(document);
13982
14619
  // Walk the tree and process each `q:container` element.
13983
14620
  const walker = document.createTreeWalker(document, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */);
13984
- walkContainer(walker, null, walker.firstChild(), null, '', null, null);
14621
+ yield* walkContainer(walker, null, walker.firstChild(), null, '', null, null);
13985
14622
  }
13986
14623
  const isSeparator = (ch) =>
13987
14624
  /* `!` */ VNodeDataSeparator.ADVANCE_1 <= ch && ch <= VNodeDataSeparator.ADVANCE_8192; /* `.` */
@@ -14016,6 +14653,87 @@ const findVDataSectionEnd = (vData, start, end) => {
14016
14653
  return start;
14017
14654
  };
14018
14655
 
14656
+ function isContainerReady(container) {
14657
+ return container.$containerDataProcessState$ === 3 /* ContainerDataProcessState.ProcessingStateDone */;
14658
+ }
14659
+ function processContainerStateData(container, iterator) {
14660
+ const state = (container.$containerStateDataState$ ||= {
14661
+ $queue$: [],
14662
+ $active$: null,
14663
+ });
14664
+ container.$containerDataProcessState$ = 2 /* ContainerDataProcessState.ProcessingState */;
14665
+ state.$queue$.push(iterator);
14666
+ scheduleProcessContainerStateData(container, state);
14667
+ }
14668
+ const onContainerDataReady = (container, callback) => {
14669
+ const domContainer = container;
14670
+ const checkReady = () => {
14671
+ if (!isContainerReady(domContainer)) {
14672
+ (domContainer.$containerStateReadyCallbacks$ ||= []).push(checkReady);
14673
+ return;
14674
+ }
14675
+ onVNodeDataReady(domContainer.document, () => {
14676
+ if (isContainerReady(domContainer)) {
14677
+ callback();
14678
+ }
14679
+ else {
14680
+ checkReady();
14681
+ }
14682
+ });
14683
+ };
14684
+ checkReady();
14685
+ };
14686
+ const whenContainerDataReady = (container, callback) => {
14687
+ const domContainer = container;
14688
+ if (isContainerReady(domContainer) && domContainer.document.qVNodeDataReady) {
14689
+ return callback();
14690
+ }
14691
+ return new Promise((resolve, reject) => {
14692
+ onContainerDataReady(domContainer, () => {
14693
+ try {
14694
+ resolve(callback());
14695
+ }
14696
+ catch (error) {
14697
+ reject(error);
14698
+ }
14699
+ });
14700
+ });
14701
+ };
14702
+ function scheduleProcessContainerStateData(container, state) {
14703
+ if (state.$active$) {
14704
+ return;
14705
+ }
14706
+ const iterator = state.$queue$.shift();
14707
+ if (!iterator) {
14708
+ markContainerDataReady(container);
14709
+ return;
14710
+ }
14711
+ state.$active$ = createYieldingIteratorState(iterator, () => {
14712
+ state.$active$ = null;
14713
+ scheduleProcessContainerStateData(container, state);
14714
+ }, (error) => {
14715
+ state.$active$ = null;
14716
+ logError(error);
14717
+ markContainerDataReady(container);
14718
+ });
14719
+ scheduleYieldingIterator(state.$active$);
14720
+ }
14721
+ function markContainerDataReady(container) {
14722
+ const state = container.$containerDataProcessState$;
14723
+ if (state !== 2 /* ContainerDataProcessState.ProcessingState */) {
14724
+ return;
14725
+ }
14726
+ container.$containerDataProcessState$ = 3 /* ContainerDataProcessState.ProcessingStateDone */;
14727
+ container.$containerStateDataState$ = undefined;
14728
+ const callbacks = container.$containerStateReadyCallbacks$;
14729
+ container.$containerStateReadyCallbacks$ = undefined;
14730
+ if (callbacks) {
14731
+ for (let i = 0; i < callbacks.length; i++) {
14732
+ callbacks[i]();
14733
+ }
14734
+ }
14735
+ }
14736
+
14019
14737
  /** @file Public APIs for the SSR */
14020
14738
  /** @public */
14021
14739
  function getDomContainer(element) {
@@ -14038,8 +14756,12 @@ function getOutOfOrderStreamingScript(boundaryId, content) {
14038
14756
  const qContainerElement = content?.closest(QContainerSelector);
14039
14757
  const qContainer = qContainerElement?.qContainer;
14040
14758
  if (qContainer) {
14041
- processOutOfOrderSegmentVNodeData(qContainer.element.ownerDocument, segmentId, content);
14042
- processSegmentStateScripts(qContainer, segmentId);
14759
+ const document = qContainer.element.ownerDocument;
14760
+ processOutOfOrderSegmentVNodeData(document, segmentId, content);
14761
+ onVNodeDataReady(document, () => {
14762
+ qContainer.element.qContainer === qContainer &&
14763
+ processContainerStateData(qContainer, processSegmentStateScriptsIterator(qContainer, segmentId));
14764
+ });
14043
14765
  }
14044
14766
  }
14045
14767
  /** @internal */
@@ -14054,6 +14776,9 @@ class DomContainer extends _SharedContainer {
14054
14776
  $instanceHash$;
14055
14777
  $forwardRefs$ = null;
14056
14778
  vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
14779
+ $containerDataProcessState$ = 0 /* ContainerDataProcessState.NotStarted */;
14780
+ $containerStateReadyCallbacks$ = undefined;
14781
+ $containerStateDataState$ = undefined;
14057
14782
  $rawStateData$;
14058
14783
  $stateData$;
14059
14784
  $rootForwardRefs$ = null;
@@ -14064,7 +14789,8 @@ class DomContainer extends _SharedContainer {
14064
14789
  if (!this.qContainer) {
14065
14790
  throw qError(25 /* QError.elementWithoutContainer */);
14066
14791
  }
14067
- this.document = element.ownerDocument;
14792
+ const document = element.ownerDocument;
14793
+ this.document = document;
14068
14794
  this.element = element;
14069
14795
  this.$buildBase$ = element.getAttribute(QBaseAttr);
14070
14796
  this.$instanceHash$ = element.getAttribute(QInstanceAttr);
@@ -14072,23 +14798,46 @@ class DomContainer extends _SharedContainer {
14072
14798
  this.rootVNode = vnode_newUnMaterializedElement(this.element);
14073
14799
  this.$rawStateData$ = [];
14074
14800
  this.$stateData$ = [];
14075
- const document = this.element.ownerDocument;
14076
- if (!document.qVNodeDataProcessed) {
14077
- processVNodeData(document);
14078
- }
14079
14801
  if (__EXPERIMENTAL__.suspense && document.querySelector('template[q\\:r]')) {
14080
14802
  document.qProcessOOOS ||= getOutOfOrderStreamingScript;
14081
14803
  }
14082
14804
  this.$qFuncs$ = getQFuncs(document, this.$instanceHash$) || EMPTY_ARRAY;
14083
14805
  this.$setServerData$();
14084
- element.setAttribute(QContainerAttr, "resumed" /* QContainerValue.RESUMED */);
14085
14806
  element.qContainer = this;
14086
14807
  element.qDestroy = () => this.$destroy$();
14087
- this.$processRootStateScript$();
14808
+ this.$containerDataProcessState$ = 1 /* ContainerDataProcessState.ProcessingVNode */;
14809
+ processVNodeData(document, element);
14810
+ onVNodeDataReady(document, () => {
14811
+ if (this.$containerDataProcessState$ === 1 /* ContainerDataProcessState.ProcessingVNode */) {
14812
+ processContainerStateData(this, this.$processContainerData$());
14813
+ }
14814
+ });
14815
+ }
14816
+ *$processContainerData$() {
14817
+ const element = this.element;
14818
+ if (element.qContainer !== this) {
14819
+ return;
14820
+ }
14821
+ const rootState = element.querySelector(`script[type="qwik/state"][q\\:instance="${this.$instanceHash$}"]:not(${QStatePatchAttrSelector})`) ||
14822
+ element.querySelector(`script[type="qwik/state"]:not([q\\:instance]):not(${QStatePatchAttrSelector})`);
14823
+ if (rootState) {
14824
+ this.$rawStateData$ = JSON.parse(rootState.textContent);
14825
+ yield* preprocessStateIterator(this.$rawStateData$, this);
14826
+ this.$rootForwardRefs$ = this.$forwardRefs$;
14827
+ const rootCount = this.$rawStateData$.length / 2;
14828
+ const statePrewarm = element.getAttribute(QStatePrewarmAttr);
14829
+ if (statePrewarm !== null && rootCount > 0 && rootCount >= Number(statePrewarm)) {
14830
+ this.$stateData$ = yield* eagerDeserializeStateIterator(this, this.$rawStateData$);
14831
+ }
14832
+ else {
14833
+ this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
14834
+ }
14835
+ }
14088
14836
  if (__EXPERIMENTAL__.suspense) {
14089
- processSegmentStateScripts(this);
14837
+ yield* processSegmentStateScriptsIterator(this);
14090
14838
  }
14091
14839
  this.$hoistStyles$();
14840
+ element.setAttribute(QContainerAttr, "resumed" /* QContainerValue.RESUMED */);
14092
14841
  if (!qTest && element.isConnected) {
14093
14842
  element.dispatchEvent(new CustomEvent('qresume', { bubbles: true }));
14094
14843
  }
@@ -14108,9 +14857,15 @@ class DomContainer extends _SharedContainer {
14108
14857
  }
14109
14858
  el.removeAttribute(QContainerAttr);
14110
14859
  const document = el.ownerDocument;
14860
+ this.$containerStateDataState$ = undefined;
14861
+ this.$containerDataProcessState$ = 0 /* ContainerDataProcessState.NotStarted */;
14111
14862
  const hasContainers = document.querySelector(QContainerSelector) !== null;
14112
14863
  if (!hasContainers) {
14113
14864
  document.qVNodeData = undefined;
14865
+ document.qVNodeDataStarted = undefined;
14866
+ document.qVNodeDataReady = undefined;
14867
+ document.qVNodeDataState = undefined;
14868
+ document.qVNodeDataCallbacks = undefined;
14114
14869
  document.qVNodeDataProcessed = undefined;
14115
14870
  document.qProcessVNodeDataPatch = undefined;
14116
14871
  }
@@ -14120,18 +14875,6 @@ class DomContainer extends _SharedContainer {
14120
14875
  }
14121
14876
  }
14122
14877
  }
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
14878
  /**
14136
14879
  * The first time we render we need to hoist the styles. (Meaning we need to move all styles from
14137
14880
  * component inline to <head>)
@@ -14420,8 +15163,7 @@ function invokeApply(context, fn, args) {
14420
15163
  _context = previousContext;
14421
15164
  }
14422
15165
  }
14423
- const newInvokeContextFromDOM = (event, element) => {
14424
- const domContainer = getDomContainer(element);
15166
+ const newInvokeContextFromDOM = (event, element, domContainer = getDomContainer(element)) => {
14425
15167
  const hostElement = vnode_locate(domContainer.rootVNode, element);
14426
15168
  const locale = domContainer.$locale$;
14427
15169
  locale && setLocale(locale);
@@ -14563,7 +15305,7 @@ const initLazyRefDev = (lazy) => {
14563
15305
  };
14564
15306
  /** Validate captured scope and register WeakRef tracking on a QRLClass instance. */
14565
15307
  const initQrlClassDev = (lazy, captures, qrl) => {
14566
- if (captures && typeof captures === 'object') {
15308
+ if (Array.isArray(captures)) {
14567
15309
  for (let i = 0; i < captures.length; i++) {
14568
15310
  const item = captures[i];
14569
15311
  verifySerializable(item, 'Captured variable in the closure can not be serialized');
@@ -14765,7 +15507,9 @@ class QRLClass {
14765
15507
  }
14766
15508
  // If it is plain value with deserialized or missing captures, resolve it immediately
14767
15509
  // 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$)) {
15510
+ if ($lazy$.$ref$ != null &&
15511
+ (!this.$captures$ || typeof this.$captures$ !== 'string') &&
15512
+ !isPromise($lazy$.$ref$)) {
14769
15513
  this.resolved = bindCaptures(this, $lazy$.$ref$);
14770
15514
  }
14771
15515
  }
@@ -14920,31 +15664,50 @@ let _captures = null;
14920
15664
  const setCaptures = (captures) => {
14921
15665
  _captures = captures;
14922
15666
  };
14923
- const deserializeCaptures = (container, captures) => {
15667
+ const deserializeCaptureDeltas = (container, deltaString, startIndex = 0, previousRootId) => {
14924
15668
  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));
15669
+ let previous = previousRootId;
15670
+ let start = startIndex;
15671
+ for (let i = startIndex; i <= deltaString.length; i++) {
15672
+ if (i === deltaString.length || deltaString.charCodeAt(i) === 32) {
15673
+ if (i > start) {
15674
+ const delta = Number(deltaString.slice(start, i));
15675
+ previous = previous === undefined ? delta : previous + delta;
15676
+ refs.push(container.$getObjectById$(previous));
15677
+ }
15678
+ start = i + 1;
15679
+ }
14929
15680
  }
14930
15681
  return refs;
14931
15682
  };
15683
+ const deserializeQrlCaptureDeltas = (container, qrlString) => {
15684
+ const firstHash = qrlString.indexOf('#');
15685
+ if (firstHash === -1) {
15686
+ return deserializeCaptureDeltas(container, qrlString);
15687
+ }
15688
+ const secondHash = qrlString.indexOf('#', firstHash + 1);
15689
+ const previousRootId = Number(qrlString.slice(0, firstHash)) + Number(qrlString.slice(firstHash + 1, secondHash));
15690
+ return deserializeCaptureDeltas(container, qrlString, secondHash + 1, previousRootId);
15691
+ };
14932
15692
  /** Puts the qrl captures into `_captures`, and returns a Promise that should be awaited if possible */
14933
15693
  const ensureQrlCaptures = (qrl) => {
14934
15694
  // We read the captures once, synchronously, so no need to keep previous
14935
- _captures = qrl.$captures$;
15695
+ const serializedCaptures = qrl.$captures$;
14936
15696
  const container = qrl.$container$;
14937
- if (typeof _captures === 'string') {
15697
+ if (typeof serializedCaptures === 'string') {
14938
15698
  if (!container) {
14939
15699
  throw qError(13 /* QError.qrlMissingContainer */);
14940
15700
  }
14941
15701
  const prevLoading = loading;
14942
- _captures = qrl.$captures$ = deserializeCaptures(container, _captures);
15702
+ _captures = qrl.$captures$ = deserializeQrlCaptureDeltas(container, serializedCaptures);
14943
15703
  if (loading !== prevLoading) {
14944
15704
  // return the loading promise so callers can await it
14945
15705
  return loading;
14946
15706
  }
14947
15707
  }
15708
+ else {
15709
+ _captures = serializedCaptures || null;
15710
+ }
14948
15711
  };
14949
15712
  // Wrap functions to provide their lexical scope
14950
15713
  const bindCaptures = (qrl, ref) => {
@@ -15911,7 +16674,13 @@ const useComputedQrl = (qrl, options) => {
15911
16674
  * recalculated, and if the result changed, all tasks which are tracking the signal will be re-run
15912
16675
  * and all components that read the signal will be re-rendered.
15913
16676
  *
15914
- * The function must be synchronous and must not have any side effects.
16677
+ * Every synchronous signal or store read is tracked automatically. Reads after an `await` are not:
16678
+ * the tracking context is lost, so track them explicitly with the `track()` provided on the context
16679
+ * argument. When the function is async, the returned signal exposes the async API: reading an
16680
+ * unresolved `.value` throws the computation promise, and `.pending` and `.error` expose the
16681
+ * computation state.
16682
+ *
16683
+ * The function must not have any side effects.
15915
16684
  *
15916
16685
  * @public
15917
16686
  */
@@ -16009,7 +16778,7 @@ const useVisibleTaskQrl = (qrl, opts) => {
16009
16778
  // In SSR we defer execution until triggered in DOM
16010
16779
  flags = 1 /* TaskFlags.VISIBLE_TASK */;
16011
16780
  }
16012
- const task = new Task(flags, i, iCtx.$hostElement$, qrl, undefined, null);
16781
+ const task = new Task(flags, i, iCtx.$hostElement$, qrl, null);
16013
16782
  set(task);
16014
16783
  useRegisterTaskEvents(task, eagerness);
16015
16784
  };
@@ -16047,11 +16816,11 @@ const getTaskHandlerQrl = (task) => {
16047
16816
  * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
16048
16817
  * re-throw it (or a new Error), the resource status will never be `rejected`.
16049
16818
  *
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.
16819
+ * @deprecated Use `useComputed$` instead, which is more powerful and flexible. `useResource$` is
16820
+ * still available for backward compatibility but it is recommended to migrate to `useComputed$`
16821
+ * for new code and when updating existing code.
16053
16822
  * @public
16054
- * @see useAsync$
16823
+ * @see useComputed$
16055
16824
  * @see Resource
16056
16825
  * @see ResourceReturn
16057
16826
  */
@@ -16156,7 +16925,10 @@ const creator = (qrl, options) => {
16156
16925
  qrl.resolve();
16157
16926
  return createAsyncSignal(qrl, options);
16158
16927
  };
16159
- /** @internal */
16928
+ /**
16929
+ * @deprecated Use `useComputed$` instead, it has async support now.
16930
+ * @internal
16931
+ */
16160
16932
  const useAsyncQrl = (qrl, options) => {
16161
16933
  return useConstant((creator), qrl, options);
16162
16934
  };
@@ -16179,6 +16951,8 @@ const useAsyncQrl = (qrl, options) => {
16179
16951
  * will subscribe to it and return the last resolved value until the new value is ready. As soon as
16180
16952
  * the new value is ready, the subscribers will be updated.
16181
16953
  *
16954
+ * @deprecated Use `useComputed$` instead, it has async support now. It auto-tracks synchronous
16955
+ * reads; after the first `await`, use the provided `track()` like before.
16182
16956
  * @public
16183
16957
  */
16184
16958
  const useAsync$ = implicit$FirstArg(useAsyncQrl);
@@ -16215,6 +16989,46 @@ const eachCmp = (props) => {
16215
16989
  const Each = /*#__PURE__*/ componentQrl(
16216
16990
  /*#__PURE__*/ inlinedQrl(eachCmp, '_eaC'));
16217
16991
 
16992
+ const invokeShowFn = (fn, context, arg) => {
16993
+ return isQrl(fn) ? fn.getFn(context)(arg) : fn(arg);
16994
+ };
16995
+ /** @internal */
16996
+ const showCmpTask = ({ track }) => {
16997
+ const props = _captures[0];
16998
+ const context = tryGetInvokeContext();
16999
+ const host = context.$hostElement$;
17000
+ const container = context.$container$;
17001
+ const isSsr = qTest ? isServerPlatform() : isServer;
17002
+ return maybeThen(track(() => invokeShowFn(props.when$, tryGetInvokeContext(), undefined)), (condition) => {
17003
+ const branch = condition ? props.then$ : props.else$;
17004
+ return maybeThen(branch ? invokeShowFn(branch, context, condition) : null, (output) => {
17005
+ const jsx = branch ? [output] : [];
17006
+ if (isSsr) {
17007
+ const ssr = container;
17008
+ return ssr.renderJSX(jsx, {
17009
+ currentStyleScoped: null,
17010
+ parentComponentFrame: ssr.getComponentFrame(0),
17011
+ });
17012
+ }
17013
+ else {
17014
+ setNodeDiffPayload(host, jsx);
17015
+ markVNodeDirty(container, host, 2 /* ChoreBits.NODE_DIFF */);
17016
+ }
17017
+ });
17018
+ });
17019
+ };
17020
+ /** @internal */
17021
+ const showCmp = (props) => {
17022
+ if (!__EXPERIMENTAL__.show) {
17023
+ throw new Error('Show is experimental and must be enabled with `experimental: ["show"]` in the `qwikVite` plugin.');
17024
+ }
17025
+ useTaskQrl(/*#__PURE__*/ inlinedQrl(showCmpTask, '_shT', [props]));
17026
+ return SkipRender;
17027
+ };
17028
+ /** @public @experimental */
17029
+ const Show = /*#__PURE__*/ componentQrl(
17030
+ /*#__PURE__*/ inlinedQrl(showCmp, '_shC'));
17031
+
16218
17032
  const RevealContext = /*#__PURE__*/ createContextId('qk-reveal');
16219
17033
  const createRevealContext = (props) => {
16220
17034
  return {
@@ -16608,5 +17422,5 @@ if (import.meta.hot) {
16608
17422
  });
16609
17423
  }
16610
17424
 
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 };
17425
+ 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
17426
  //# sourceMappingURL=core.mjs.map