@qwik.dev/core 2.0.0-beta.36 → 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.36-dev+3268fab
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';
@@ -465,6 +469,7 @@ const QDefaultSlot = '';
465
469
  const ELEMENT_ID = 'q:id';
466
470
  const ELEMENT_KEY = 'q:key';
467
471
  const ELEMENT_PROPS = 'q:props';
472
+ /** @internal */
468
473
  const ELEMENT_SEQ = 'q:seq';
469
474
  const ELEMENT_SEQ_IDX = 'q:seqIdx';
470
475
  const ITERATION_ITEM_SINGLE = 'q:p'; // Single iteration parameter (not an array)
@@ -504,6 +509,12 @@ const maybeThen = (valueOrPromise, thenFn) => {
504
509
  ? valueOrPromise.then(thenFn)
505
510
  : thenFn(valueOrPromise);
506
511
  };
512
+ /** @internal */
513
+ const delay = (timeout) => {
514
+ return new Promise((resolve) => {
515
+ setTimeout(resolve, timeout);
516
+ });
517
+ };
507
518
  const checkError = (e) => {
508
519
  if (isServer && e instanceof ReferenceError && e.message.includes('window')) {
509
520
  e.message = 'It seems like you forgot to add "if (isBrowser) {...}" here:' + e.message;
@@ -515,6 +526,8 @@ const justThrow = (e) => {
515
526
  /**
516
527
  * Retries a function that throws a promise. If you pass `onError`, you're responsible for handling
517
528
  * errors.
529
+ *
530
+ * @internal
518
531
  */
519
532
  function retryOnPromise(fn, onError = justThrow) {
520
533
  let ok = false;
@@ -833,8 +846,21 @@ const createMacroTask = (fn) => {
833
846
  let macroTask;
834
847
  if (typeof MessageChannel !== 'undefined') {
835
848
  const channel = new MessageChannel();
849
+ let active = true;
836
850
  channel.port1.onmessage = () => fn();
837
- 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
+ };
838
864
  }
839
865
  else {
840
866
  macroTask = () => setTimeout(fn);
@@ -954,7 +980,7 @@ const rel = isBrowser && doc.createElement('link').relList?.supports?.('modulepr
954
980
  ? 'modulePreload'
955
981
  : 'preload';
956
982
  const isJSRegex = /\.[mc]?js$/;
957
- const yieldInterval = 1000 / 60;
983
+ const yieldInterval = 10;
958
984
 
959
985
  const BundleImportState_None = 0;
960
986
  const BundleImportState_Preload = 2;
@@ -1210,6 +1236,8 @@ const writeStringRootRef = (writer, id) => writer.write(String(id));
1210
1236
  /** @internal */
1211
1237
  const writeStringRootRefPath = (writer, path) => writer.write(stringifyRootRefPath(path));
1212
1238
  /** @internal */
1239
+ const writeStringRootRefDelta = (writer, id, base) => writer.write(String(id - base));
1240
+ /** @internal */
1213
1241
  const createStringStreamWriter = (write) => ({
1214
1242
  write,
1215
1243
  writeRootRef(id) {
@@ -1218,6 +1246,9 @@ const createStringStreamWriter = (write) => ({
1218
1246
  writeRootRefPath(path) {
1219
1247
  return writeStringRootRefPath(this, path);
1220
1248
  },
1249
+ writeRootRefDelta(id, base) {
1250
+ return writeStringRootRefDelta(this, id, base);
1251
+ },
1221
1252
  });
1222
1253
 
1223
1254
  // Pre-allocated common strings to reduce allocation overhead
@@ -1233,7 +1264,7 @@ const COMMA = ',';
1233
1264
  *
1234
1265
  * @public
1235
1266
  */
1236
- const version = "2.0.0-beta.36-dev+3268fab";
1267
+ const version = "2.0.0-beta.38-dev+0339f17";
1237
1268
 
1238
1269
  const isNode = (value) => {
1239
1270
  return value && typeof value.nodeType === 'number';
@@ -1689,9 +1720,12 @@ function setCursorPosition(container, cursorData, position) {
1689
1720
  }
1690
1721
  }
1691
1722
  function mergeCursors(container, newCursorData, oldCursor) {
1723
+ const oldCursorData = getCursorData(oldCursor);
1724
+ if (!oldCursorData || oldCursorData === newCursorData) {
1725
+ return;
1726
+ }
1692
1727
  // delete from global cursors queue
1693
1728
  removeCursorFromQueue(oldCursor, container);
1694
- const oldCursorData = getCursorData(oldCursor);
1695
1729
  mergeCursorData(newCursorData, oldCursorData);
1696
1730
  }
1697
1731
  function mergeCursorData(newCursorData, oldCursorData) {
@@ -1769,6 +1803,234 @@ function setCursorData(vNode, cursorData) {
1769
1803
  cursorDatas.set(vNode, cursorData);
1770
1804
  }
1771
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
+
1772
2034
  /**
1773
2035
  * # ================================
1774
2036
  *
@@ -1840,6 +2102,7 @@ const STORE_TARGET = Symbol('store.target');
1840
2102
  const STORE_HANDLER = Symbol('store.handler');
1841
2103
  const STORE_ALL_PROPS = Symbol('store.all');
1842
2104
 
2105
+ /** @internal */
1843
2106
  function getSubscriber(effect, prop, data) {
1844
2107
  if (!effect[_EFFECT_BACK_REF]) {
1845
2108
  if (isServer && isSsrNode(effect)) {
@@ -2007,7 +2270,7 @@ class SignalImpl {
2007
2270
  toString() {
2008
2271
  if (isDev) {
2009
2272
  try {
2010
- 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$}]` +
2011
2274
  (Array.from(this.$effects$ || [])
2012
2275
  .map((e) => '\n -> ' + pad(qwikDebugToString(e.consumer), ' '))
2013
2276
  .join('\n') || ''));
@@ -2025,99 +2288,11 @@ class SignalImpl {
2025
2288
  }
2026
2289
  }
2027
2290
 
2028
- const DEBUG$1 = false;
2291
+ const DEBUG = false;
2029
2292
  // eslint-disable-next-line no-console
2030
- const log$1 = (...args) => console.log('COMPUTED SIGNAL', ...args.map(qwikDebugToString));
2031
- /**
2032
- * A signal which is computed from other signals.
2033
- *
2034
- * The value is available synchronously, but the computation is done lazily.
2035
- */
2036
- class ComputedSignalImpl extends SignalImpl {
2037
- /**
2038
- * The compute function is stored here.
2039
- *
2040
- * The computed functions must be executed synchronously (because of this we need to eagerly
2041
- * resolve the QRL during the mark dirty phase so that any call to it will be synchronous). )
2042
- */
2043
- $computeQrl$;
2044
- $flags$;
2045
- [_EFFECT_BACK_REF] = undefined;
2046
- constructor(container, fn,
2047
- // We need a separate flag to know when the computation needs running because
2048
- // we need the old value to know if effects need running after computation
2049
- flags = 1 /* SignalFlags.INVALID */ |
2050
- 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */) {
2051
- // The value is used for comparison when signals trigger, which can only happen
2052
- // when it was calculated before. Therefore we can pass whatever we like.
2053
- super(container || tryGetInvokeContext()?.$container$, NEEDS_COMPUTATION);
2054
- this.$computeQrl$ = fn;
2055
- this.$flags$ = flags;
2056
- }
2057
- invalidate() {
2058
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
2059
- const ctx = newInvokeContext();
2060
- ctx.$container$ = this.$container$ || undefined;
2061
- // @ts-expect-error it's confused about args any[] vs []
2062
- const running = retryOnPromise(invokeApply.bind(this, ctx, this.$computeIfNeeded$));
2063
- if (running) {
2064
- running.catch((err) => {
2065
- if (this.$container$) {
2066
- this.$container$.handleError(err, null);
2067
- }
2068
- else {
2069
- console.error('Error during computation', err);
2070
- }
2071
- });
2072
- }
2073
- }
2074
- get untrackedValue() {
2075
- this.$computeIfNeeded$();
2076
- isDev && assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
2077
- return this.$untrackedValue$;
2078
- }
2079
- set untrackedValue(value) {
2080
- this.$untrackedValue$ = value;
2081
- }
2082
- $computeIfNeeded$() {
2083
- if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
2084
- return;
2085
- }
2086
- const computeQrl = this.$computeQrl$;
2087
- throwIfQRLNotResolved(computeQrl);
2088
- const ctx = tryGetInvokeContext();
2089
- const previousEffectSubscription = ctx?.$effectSubscriber$;
2090
- if (ctx) {
2091
- const effectSubscriber = getSubscriber(this, "." /* EffectProperty.VNODE */);
2092
- clearEffectSubscription(this.$container$, effectSubscriber);
2093
- ctx.$effectSubscriber$ = effectSubscriber;
2094
- }
2095
- try {
2096
- const untrackedValue = computeQrl.getFn(ctx)();
2097
- if (isPromise(untrackedValue)) {
2098
- throw qError(29 /* QError.computedNotSync */, [
2099
- computeQrl.dev ? computeQrl.dev.file : '',
2100
- computeQrl.$hash$,
2101
- ]);
2102
- }
2103
- DEBUG$1 && log$1('Signal.$compute$', untrackedValue);
2104
- this.$flags$ &= ~1 /* SignalFlags.INVALID */;
2105
- super.value = untrackedValue;
2106
- }
2107
- finally {
2108
- if (ctx) {
2109
- ctx.$effectSubscriber$ = previousEffectSubscription;
2110
- }
2111
- }
2112
- }
2113
- }
2114
-
2115
- const DEBUG = false;
2116
- const log = (...args) =>
2117
- // eslint-disable-next-line no-console
2118
- console.log('ASYNC COMPUTED SIGNAL', ...args.map(qwikDebugToString));
2119
- /** Retains job metadata and also serves as the argument for the compute function */
2120
- 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 {
2121
2296
  $signal$;
2122
2297
  /** First holds the compute promise and then the cleanup promise */
2123
2298
  $promise$ = null;
@@ -2139,7 +2314,7 @@ class AsyncJob {
2139
2314
  /** Backward compatible cache method for resource */
2140
2315
  cache() {
2141
2316
  isDev &&
2142
- 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.');
2143
2318
  }
2144
2319
  get previous() {
2145
2320
  const val = this.$signal$.$untrackedValue$;
@@ -2154,46 +2329,57 @@ class AsyncJob {
2154
2329
  }
2155
2330
  }
2156
2331
  /**
2157
- * # ================================
2158
- *
2159
- * AsyncSignalImpl
2160
- *
2161
- * # ================================
2332
+ * A signal which is computed from other signals.
2162
2333
  *
2163
- * @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()`.
2164
2339
  */
2165
- class AsyncSignalImpl extends ComputedSignalImpl {
2166
- $untrackedLoading$ = false;
2167
- $untrackedError$ = undefined;
2168
- $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$;
2169
2350
  [_EFFECT_BACK_REF] = undefined;
2170
- 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 */ |
2171
2355
  16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */, options) {
2172
- 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;
2173
2361
  if (!options) {
2174
2362
  return;
2175
2363
  }
2176
- // Handle initial value - eagerly evaluate if function, set $untrackedValue$ and $promiseValue$
2177
- // 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
2178
2365
  const initial = options.initial;
2179
2366
  if (initial !== undefined) {
2180
- const initialValue = typeof initial === 'function' ? initial() : initial;
2181
- this.$untrackedValue$ = initialValue;
2367
+ this.$untrackedValue$ = typeof initial === 'function' ? initial() : initial;
2182
2368
  }
2183
2369
  const concurrency = options.concurrency;
2184
2370
  if (concurrency !== undefined && concurrency >= 0 && concurrency !== 1) {
2185
2371
  this.$concurrency$ = concurrency;
2186
2372
  this.$jobs$ = [];
2187
2373
  }
2188
- const timeout = options.timeout;
2189
- if (timeout) {
2190
- this.$timeoutMs$ = timeout;
2374
+ if (options.timeout) {
2375
+ this.$timeoutMs$ = options.timeout;
2191
2376
  }
2192
2377
  if (options.eagerCleanup) {
2193
2378
  this.$flags$ |= 32 /* AsyncSignalFlags.EAGER_CLEANUP */;
2194
2379
  }
2195
2380
  if (options.clientOnly) {
2196
- 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 */;
2197
2383
  }
2198
2384
  if (options.allowStale === false) {
2199
2385
  if (isDev && initial !== undefined) {
@@ -2210,28 +2396,30 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2210
2396
  this.$flags$ |= 256 /* AsyncSignalFlags.NO_POLL */;
2211
2397
  }
2212
2398
  }
2213
- get untrackedValue() {
2214
- this.$computeIfNeeded$();
2215
- if (this.$current$?.$promise$) {
2216
- if (this.$untrackedValue$ === NEEDS_COMPUTATION || (qTest ? isServerPlatform() : isServer)) {
2217
- throw this.$current$?.$promise$;
2218
- }
2219
- 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;
2220
2403
  }
2221
- if (this.$untrackedError$) {
2222
- throw this.$untrackedError$;
2404
+ if (this.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */) {
2405
+ this.$setInvalid$(true, this.$flags$ & 128 /* AsyncSignalFlags.CLEAR_ON_INVALIDATE */);
2406
+ return;
2223
2407
  }
2224
- // For clientOnly signals without initial value during SSR, throw if trying to read value
2225
- // During SSR, clientOnly signals are skipped, so there's no computed value available
2226
- if ((qTest ? isServerPlatform() : isServer) &&
2227
- this.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */ &&
2228
- this.$untrackedValue$ === NEEDS_COMPUTATION) {
2229
- 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
+ });
2230
2422
  }
2231
- return this.$untrackedValue$;
2232
- }
2233
- set untrackedValue(value) {
2234
- this.$untrackedValue$ = value;
2235
2423
  }
2236
2424
  /**
2237
2425
  * Read the value, subscribing if in a tracking context. Triggers computation if needed.
@@ -2248,32 +2436,65 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2248
2436
  return super.value;
2249
2437
  }
2250
2438
  set value(value) {
2251
- this.$flags$ &= -2 /* SignalFlags.INVALID */;
2252
- this.untrackedLoading = false;
2439
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2253
2440
  this.untrackedError = undefined;
2254
- this.$info$ = undefined;
2255
- // Prevent pending computations from overwriting this value
2256
- if (this.$jobs$) {
2257
- for (let i = 0; i < this.$jobs$.length; i++) {
2258
- 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
+ }
2259
2449
  }
2450
+ else if (this.$current$) {
2451
+ this.$current$.$canWrite$ = false;
2452
+ }
2453
+ this.$clearNextPoll$();
2454
+ super.value = value;
2455
+ this.$scheduleNextPoll$();
2456
+ return;
2260
2457
  }
2261
- else if (this.$current$) {
2262
- this.$current$.$canWrite$ = false;
2263
- }
2264
- this.$clearNextPoll$();
2265
2458
  super.value = value;
2266
- 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;
2267
2485
  }
2268
2486
  /**
2269
2487
  * Loading is true if the signal is still waiting for the promise to resolve, false if the promise
2270
- * has resolved or rejected.
2488
+ * has resolved or rejected (or the compute function is synchronous).
2271
2489
  *
2272
- * Accessing .loading will trigger computation if needed, since it's often used like
2273
- * `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
+ * ```
2274
2495
  */
2275
- get loading() {
2276
- const val = this.untrackedLoading;
2496
+ get pending() {
2497
+ const val = this.untrackedPending;
2277
2498
  const ctx = tryGetInvokeContext();
2278
2499
  if (ctx && (this.$container$ ||= ctx.$container$ || null)) {
2279
2500
  isDev &&
@@ -2287,22 +2508,41 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2287
2508
  }
2288
2509
  return val;
2289
2510
  }
2290
- set untrackedLoading(value) {
2291
- if (value !== this.$untrackedLoading$) {
2292
- this.$untrackedLoading$ = value;
2511
+ set untrackedPending(value) {
2512
+ if (value !== !!this.$untrackedPending$) {
2513
+ this.$untrackedPending$ = value;
2293
2514
  scheduleEffects(this.$container$, this, this.$loadingEffects$);
2294
2515
  }
2295
2516
  }
2296
- get untrackedLoading() {
2517
+ get untrackedPending() {
2297
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.
2298
2519
  this.$computeIfNeeded$();
2299
2520
  // During SSR there's no such thing as loading state, we must render complete results
2300
2521
  if ((qTest ? isServerPlatform() : isServer) && this.$current$?.$promise$) {
2301
2522
  throw this.$current$?.$promise$;
2302
2523
  }
2303
- 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;
2304
2533
  }
2305
- /** 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
+ */
2306
2546
  get error() {
2307
2547
  const val = this.untrackedError;
2308
2548
  const ctx = tryGetInvokeContext();
@@ -2325,6 +2565,11 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2325
2565
  }
2326
2566
  }
2327
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
+ }
2328
2573
  return this.$untrackedError$;
2329
2574
  }
2330
2575
  get expires() {
@@ -2367,16 +2612,8 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2367
2612
  }
2368
2613
  this.expires = Math.abs(value);
2369
2614
  }
2370
- /** Invalidates the signal, causing it to re-compute its value. */
2371
- async invalidate(info) {
2372
- if (arguments.length > 0) {
2373
- this.$info$ = info;
2374
- this.$infoVersion$ = this.$infoVersion$ === undefined ? 1 : this.$infoVersion$ + 1;
2375
- }
2376
- this.$setInvalid$(true, this.$flags$ & 128 /* AsyncSignalFlags.CLEAR_ON_INVALIDATE */);
2377
- }
2378
2615
  $setInvalid$(allowRecalc, mustClear) {
2379
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
2616
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
2380
2617
  this.$clearNextPoll$();
2381
2618
  if (mustClear) {
2382
2619
  this.$untrackedValue$ = NEEDS_COMPUTATION;
@@ -2417,19 +2654,97 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2417
2654
  }
2418
2655
  /** Run the computation if needed */
2419
2656
  $computeIfNeeded$() {
2420
- 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));
2421
2702
  return;
2422
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$() {
2423
2738
  // Skip computation on SSR for clientOnly signals
2424
2739
  if ((qTest ? isServerPlatform() : isServer) && this.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
2425
2740
  // We must pretend to load, and register as a listener for the captures
2426
- this.$untrackedLoading$ = true;
2741
+ this.$untrackedPending$ = true;
2427
2742
  this.$container$?.serializationCtx.$eagerResume$.add(this);
2428
2743
  return;
2429
2744
  }
2430
2745
  this.$clearNextPoll$();
2431
2746
  // Clear flag here to make sure the cleanups don't start another compute
2432
- this.$flags$ &= -2 /* SignalFlags.INVALID */;
2747
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2433
2748
  const current = this.$current$;
2434
2749
  if (current) {
2435
2750
  this.$requestCleanups$(current);
@@ -2439,12 +2754,12 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2439
2754
  if (this.$jobs$ ? this.$jobs$.length >= limit : current?.$promise$) {
2440
2755
  // We requested cleanups for all the previous jobs, once one finishes it will be removed from the jobs array and trigger computeIfNeeded
2441
2756
  // Restore invalid state
2442
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
2757
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
2443
2758
  return;
2444
2759
  }
2445
2760
  // We put the actual computation in a separate method so we can easily retain the promise
2446
2761
  const infoVersion = this.$infoVersion$;
2447
- const running = new AsyncJob(this, this.$info$, infoVersion);
2762
+ const running = new Job(this, this.$info$, infoVersion);
2448
2763
  this.$current$ = running;
2449
2764
  if (this.$jobs$) {
2450
2765
  this.$jobs$.push(running);
@@ -2452,35 +2767,39 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2452
2767
  running.$promise$ = this.$runComputation$(running);
2453
2768
  }
2454
2769
  async $runComputation$(running) {
2455
- const isCurrent = () => running === this.$current$;
2456
2770
  let fn = this.$computeQrl$.resolved;
2457
2771
  if (!fn) {
2458
2772
  // QRL resolution is async — we have to publish loading=true before awaiting so
2459
2773
  // subscribers know the value isn't ready yet.
2460
- this.untrackedLoading = true;
2774
+ this.untrackedPending = true;
2461
2775
  fn = await this.$computeQrl$.resolve();
2462
2776
  if (running.$abortController$?.signal.aborted) {
2463
2777
  running.$promise$ = null;
2464
2778
  return;
2465
2779
  }
2466
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$;
2467
2793
  try {
2468
- if (this.$timeoutMs$) {
2469
- this.$computationTimeoutId$ = setTimeout(() => {
2470
- const error = new Error(`timeout ${this.$timeoutMs$}ms`);
2471
- this.$setError$(running, error);
2472
- running.$abortController$?.abort(error);
2473
- }, this.$timeoutMs$);
2474
- }
2475
2794
  // Try to stay sync if possible. Only publish loading=true to subscribers when
2476
2795
  // the compute is actually asynchronous — a synchronous resolve (e.g. pre-loaded
2477
2796
  // values injected via _injectAsyncSignalValue) should never transition through a
2478
2797
  // visible loading state, which on SSR would fire the loading-effect subscribers
2479
2798
  // (tasks) while the value is still "loading" from their perspective.
2480
- const valuePromise = retryOnPromise(fn.bind(null, running));
2799
+ const valuePromise = compute();
2481
2800
  let value;
2482
2801
  if (isPromise(valuePromise)) {
2483
- this.untrackedLoading = true;
2802
+ this.untrackedPending = true;
2484
2803
  value = await valuePromise;
2485
2804
  }
2486
2805
  else {
@@ -2505,7 +2824,7 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2505
2824
  // Note that these assignments run setters
2506
2825
  this.untrackedError = undefined;
2507
2826
  /**
2508
- * 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
2509
2828
  * during computation recomputes
2510
2829
  */
2511
2830
  super.value = value;
@@ -2520,12 +2839,12 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2520
2839
  if (running.$infoVersion$ === this.$infoVersion$) {
2521
2840
  this.$info$ = undefined;
2522
2841
  }
2523
- if (this.$flags$ & 1 /* SignalFlags.INVALID */) {
2842
+ if (this.$flags$ & 1 /* ComputedSignalFlags.INVALID */) {
2524
2843
  // we became invalid again while running, so we need to re-run the computation to get the new promise
2525
2844
  this.$computeIfNeeded$();
2526
2845
  }
2527
2846
  else {
2528
- this.untrackedLoading = false;
2847
+ this.untrackedPending = false;
2529
2848
  this.$scheduleNextPoll$();
2530
2849
  }
2531
2850
  }
@@ -2552,325 +2871,98 @@ class AsyncSignalImpl extends ComputedSignalImpl {
2552
2871
  this.$clearNextPoll$();
2553
2872
  clearTimeout(this.$computationTimeoutId$);
2554
2873
  const current = this.$current$;
2555
- if (current) {
2556
- this.$requestCleanups$(current);
2557
- }
2558
- if (this.$jobs$) {
2559
- await Promise.all(this.$jobs$.map((job) => job.$promise$));
2560
- }
2561
- else {
2562
- await current?.$promise$;
2563
- }
2564
- }
2565
- $clearNextPoll$() {
2566
- if (this.$pollTimeoutId$ !== undefined) {
2567
- clearTimeout(this.$pollTimeoutId$);
2568
- this.$pollTimeoutId$ = undefined;
2569
- }
2570
- }
2571
- $scheduleNextPoll$() {
2572
- if ((qTest ? isServerPlatform() : isServer) || !this.$expires$) {
2573
- return;
2574
- }
2575
- this.$clearNextPoll$();
2576
- const allowRecalc = !(this.$flags$ & 256 /* AsyncSignalFlags.NO_POLL */);
2577
- // Even when clear on invalidate, we don't clear if we're merely re-running due to polling
2578
- // We expect to get the new value soon, so we can avoid showing a loading state
2579
- const mustClear = this.$flags$ & 128 /* AsyncSignalFlags.CLEAR_ON_INVALIDATE */ && !allowRecalc;
2580
- this.$pollTimeoutId$ = setTimeout(() => this.$setInvalid$(allowRecalc, mustClear), this.$expires$);
2581
- this.$pollTimeoutId$?.unref?.();
2582
- }
2583
- $hasSubscribers$() {
2584
- return !!(this.$effects$?.size || this.$loadingEffects$?.size || this.$errorEffects$?.size);
2585
- }
2586
- $requestCleanups$(job, reason) {
2587
- if (job.$cleanupRequested$) {
2588
- return;
2589
- }
2590
- job.$cleanupRequested$ = true;
2591
- job.$abortController$?.abort(reason);
2592
- job.$promise$ = maybeThen(job.$promise$, () => this.$runCleanups$(job));
2593
- }
2594
- /** Clean up and trigger signal compute once complete */
2595
- $runCleanups$(job) {
2596
- const cleanups = job.$cleanups$;
2597
- const onError = (err) => {
2598
- const handleError = this.$container$?.handleError;
2599
- if (handleError) {
2600
- handleError(err, null);
2601
- }
2602
- else {
2603
- console.error('Error in async signal cleanup', err);
2604
- }
2605
- };
2606
- const onDone = () => {
2607
- job.$promise$ = null;
2608
- if (cleanups) {
2609
- cleanups.length = 0;
2610
- }
2611
- // Now trigger compute
2612
- const jobs = this.$jobs$;
2613
- if (jobs) {
2614
- const idx = jobs.indexOf(job);
2615
- if (idx !== -1) {
2616
- jobs.splice(idx, 1);
2617
- }
2618
- }
2619
- this.$computeIfNeeded$();
2620
- };
2621
- let promiseChain = undefined;
2622
- if (cleanups) {
2623
- // Keep this sync-ish so sync functions run immediately.
2624
- for (let i = 0; i < cleanups.length; i++) {
2625
- try {
2626
- const result = cleanups[i]();
2627
- if (isPromise(result)) {
2628
- promiseChain = (promiseChain ? promiseChain.then(() => result) : result).catch(onError);
2629
- }
2630
- }
2631
- catch (err) {
2632
- onError(err);
2633
- }
2634
- }
2635
- }
2636
- if (promiseChain) {
2637
- return promiseChain.then(onDone);
2638
- }
2639
- else {
2640
- onDone();
2641
- }
2642
- }
2643
- }
2644
-
2645
- /** @internal */
2646
- const _CONST_PROPS = Symbol('CONST');
2647
- /** @internal */
2648
- const _VAR_PROPS = Symbol('VAR');
2649
- /** @internal */
2650
- const _OWNER = Symbol('OWNER');
2651
- /** @internal */
2652
- const _PROPS_HANDLER = Symbol('PROPS_HANDLER');
2653
- /** @internal @deprecated v1 compat */
2654
- const _IMMUTABLE = Symbol('IMMUTABLE');
2655
- /** @internal */
2656
- const _UNINITIALIZED = Symbol('UNINITIALIZED');
2657
-
2658
- /** @internal */
2659
- const EMPTY_ARRAY = [];
2660
- /** @internal */
2661
- const EMPTY_OBJ = {};
2662
- Object.freeze(EMPTY_ARRAY);
2663
- Object.freeze(EMPTY_OBJ);
2664
-
2665
- function createPropsProxy(owner) {
2666
- // TODO don't make a proxy but populate getters? benchmark
2667
- return new Proxy({}, new PropsProxyHandler(owner));
2668
- }
2669
- class PropsProxyHandler {
2670
- owner;
2671
- $effects$ = undefined;
2672
- $container$ = null;
2673
- constructor(owner) {
2674
- this.owner = owner;
2675
- }
2676
- get(_, prop) {
2677
- // escape hatch to get the separated props from a component
2678
- if (prop === _CONST_PROPS) {
2679
- return this.owner.constProps;
2680
- }
2681
- else if (prop === _VAR_PROPS) {
2682
- return this.owner.varProps;
2683
- }
2684
- else if (prop === _OWNER) {
2685
- return this.owner;
2686
- }
2687
- else if (prop === _PROPS_HANDLER) {
2688
- return this;
2689
- }
2690
- let value;
2691
- if (prop === 'children') {
2692
- value = this.owner.children;
2693
- }
2694
- else {
2695
- if (typeof prop === 'string' && typeof this.owner.type === 'string') {
2696
- const attr = jsxEventToHtmlAttribute(prop);
2697
- if (attr) {
2698
- prop = attr;
2699
- }
2700
- }
2701
- value = directGetPropsProxyProp(this.owner, prop);
2702
- if (prop in this.owner.varProps) {
2703
- addPropsProxyEffect(this, prop);
2704
- }
2705
- }
2706
- // a proxied value that the optimizer made
2707
- return value instanceof WrappedSignalImpl && value.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */
2708
- ? value.value
2709
- : value;
2710
- }
2711
- set(_, prop, value) {
2712
- if (prop === _OWNER) {
2713
- // used for deserialization
2714
- this.owner = value;
2715
- }
2716
- else if (prop === 'children') {
2717
- this.owner.children = value;
2718
- }
2719
- else if (prop === _CONST_PROPS) {
2720
- this.owner.constProps = value;
2721
- }
2722
- else if (prop === _VAR_PROPS) {
2723
- this.owner.varProps = value;
2724
- }
2725
- else {
2726
- if (typeof prop === 'string' && typeof this.owner.type === 'string') {
2727
- const attr = jsxEventToHtmlAttribute(prop);
2728
- if (attr) {
2729
- prop = attr;
2730
- }
2731
- }
2732
- if (this.owner.constProps && prop in this.owner.constProps) {
2733
- // delete the prop from the const props first
2734
- delete this.owner.constProps[prop];
2735
- }
2736
- if (this.owner.varProps === EMPTY_OBJ) {
2737
- this.owner.varProps = {};
2738
- }
2739
- else if (!(prop in this.owner.varProps)) {
2740
- this.owner.toSort = true;
2741
- }
2742
- if (this.owner.varProps[prop] !== value) {
2743
- this.owner.varProps[prop] = value;
2744
- triggerPropsProxyEffect(this, prop);
2745
- }
2874
+ if (current) {
2875
+ this.$requestCleanups$(current);
2746
2876
  }
2747
- return true;
2748
- }
2749
- deleteProperty(_, prop) {
2750
- let didDelete = delete this.owner.varProps[prop];
2751
- if (didDelete) {
2752
- triggerPropsProxyEffect(this, prop);
2877
+ if (this.$jobs$) {
2878
+ await Promise.all(this.$jobs$.map((job) => job.$promise$));
2753
2879
  }
2754
- if (this.owner.constProps) {
2755
- didDelete = delete this.owner.constProps[prop] || didDelete;
2880
+ else {
2881
+ await current?.$promise$;
2756
2882
  }
2757
- if (this.owner.children != null && prop === 'children') {
2758
- this.owner.children = null;
2759
- didDelete = true;
2883
+ }
2884
+ $clearNextPoll$() {
2885
+ if (this.$pollTimeoutId$ !== undefined) {
2886
+ clearTimeout(this.$pollTimeoutId$);
2887
+ this.$pollTimeoutId$ = undefined;
2760
2888
  }
2761
- return didDelete;
2762
2889
  }
2763
- has(_, prop) {
2764
- if (prop === 'children') {
2765
- return this.owner.children != null;
2890
+ $scheduleNextPoll$() {
2891
+ if ((qTest ? isServerPlatform() : isServer) || !this.$expires$) {
2892
+ return;
2766
2893
  }
2767
- else if (prop === _CONST_PROPS || prop === _VAR_PROPS) {
2768
- return true;
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;
2769
2908
  }
2770
- const inVarProps = prop in this.owner.varProps;
2771
- if (typeof prop === 'string') {
2772
- if (inVarProps) {
2773
- addPropsProxyEffect(this, prop);
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);
2774
2920
  }
2775
- if (typeof this.owner.type === 'string') {
2776
- const attr = jsxEventToHtmlAttribute(prop);
2777
- if (attr) {
2778
- prop = attr;
2779
- }
2921
+ else {
2922
+ console.error('Error in async signal cleanup', err);
2780
2923
  }
2781
- }
2782
- return inVarProps || (this.owner.constProps ? prop in this.owner.constProps : false);
2783
- }
2784
- getOwnPropertyDescriptor(_, p) {
2785
- const value = p === 'children'
2786
- ? this.owner.children
2787
- : this.owner.constProps && p in this.owner.constProps
2788
- ? this.owner.constProps[p]
2789
- : this.owner.varProps[p];
2790
- return {
2791
- configurable: true,
2792
- enumerable: true,
2793
- value: value,
2794
2924
  };
2795
- }
2796
- ownKeys() {
2797
- const out = Object.keys(this.owner.varProps);
2798
- if (this.owner.children != null) {
2799
- out.push('children');
2800
- }
2801
- if (this.owner.constProps) {
2802
- for (const key in this.owner.constProps) {
2803
- if (out.indexOf(key) === -1) {
2804
- out.push(key);
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);
2805
2937
  }
2806
2938
  }
2939
+ cleanups.length = 0;
2807
2940
  }
2808
- return out;
2941
+ return promiseChain;
2809
2942
  }
2810
- }
2811
- const addPropsProxyEffect = (propsProxy, prop) => {
2812
- // Lazily grab the container from the invoke context
2813
- const ctx = tryGetInvokeContext();
2814
- if (ctx) {
2815
- if (propsProxy.$container$ === null) {
2816
- if (ctx.$container$) {
2817
- 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
+ }
2818
2954
  }
2955
+ this.$computeIfNeeded$();
2956
+ };
2957
+ const promiseChain = this.$runCleanupCallbacks$(job);
2958
+ if (promiseChain) {
2959
+ return promiseChain.then(onDone);
2819
2960
  }
2820
2961
  else {
2821
- isDev &&
2822
- assertTrue(!ctx.$container$ || ctx.$container$ === propsProxy.$container$, 'Do not use props across containers');
2962
+ onDone();
2823
2963
  }
2824
2964
  }
2825
- const effectSubscriber = ctx?.$effectSubscriber$;
2826
- if (effectSubscriber) {
2827
- addStoreEffect(propsProxy.owner._proxy, prop, propsProxy, effectSubscriber);
2828
- }
2829
- };
2830
- const triggerPropsProxyEffect = (propsProxy, prop) => {
2831
- const effects = getEffects$1(propsProxy.$effects$, prop);
2832
- if (effects) {
2833
- scheduleEffects(propsProxy.$container$, propsProxy, effects);
2834
- }
2835
- return !!effects;
2836
- };
2837
- function getEffects$1(effects, prop) {
2838
- // TODO: Handle STORE_ALL_PROPS
2839
- return effects?.get(prop);
2840
2965
  }
2841
- /**
2842
- * Instead of using PropsProxyHandler getter (which could create a component-level subscription).
2843
- * Use this function to get the props directly from a const or var props.
2844
- *
2845
- * This does not convert jsx event names.
2846
- */
2847
- const directGetPropsProxyProp = (jsx, prop) => {
2848
- return (jsx.constProps && prop in jsx.constProps ? jsx.constProps[prop] : jsx.varProps[prop]);
2849
- };
2850
- const _getProps = (props, prop) => {
2851
- return _getVarProps(props)?.[prop] || _getConstProps(props)?.[prop] || null;
2852
- };
2853
- /** Used by the optimizer for spread props operations @internal */
2854
- const _getVarProps = (props) => {
2855
- if (!props) {
2856
- return null;
2857
- }
2858
- return _VAR_PROPS in props
2859
- ? 'children' in props
2860
- ? { ...props[_VAR_PROPS], children: props.children }
2861
- : props[_VAR_PROPS]
2862
- : props;
2863
- };
2864
- /** Used by the optimizer for spread props operations @internal */
2865
- const _getConstProps = (props) => {
2866
- if (!props) {
2867
- return null;
2868
- }
2869
- return _CONST_PROPS in props ? props[_CONST_PROPS] : null;
2870
- };
2871
- const isPropsProxy = (obj) => {
2872
- return obj && _VAR_PROPS in obj;
2873
- };
2874
2966
 
2875
2967
  // <docs markdown="../../readme.md#implicit$FirstArg">
2876
2968
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
@@ -2926,11 +3018,11 @@ const implicit$FirstArg = (fn) => {
2926
3018
  */
2927
3019
  class SerializerSignalImpl extends ComputedSignalImpl {
2928
3020
  constructor(container, argQrl) {
2929
- super(container, argQrl, 1 /* SignalFlags.INVALID */ | 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
3021
+ super(container, argQrl, 1 /* ComputedSignalFlags.INVALID */ | 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
2930
3022
  }
2931
3023
  $didInitialize$ = false;
2932
3024
  $computeIfNeeded$() {
2933
- if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
3025
+ if (!(this.$flags$ & 1 /* ComputedSignalFlags.INVALID */)) {
2934
3026
  return;
2935
3027
  }
2936
3028
  throwIfQRLNotResolved(this.$computeQrl$);
@@ -2943,7 +3035,7 @@ class SerializerSignalImpl extends ComputedSignalImpl {
2943
3035
  : deserialize(currentValue), this, "." /* EffectProperty.VNODE */, this.$container$);
2944
3036
  this.$didInitialize$ = true;
2945
3037
  // Needs to invalidate only after all possible Promise throws happened
2946
- this.$flags$ &= -2 /* SignalFlags.INVALID */;
3038
+ this.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
2947
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.
2948
3040
  const didChange = (this.$didInitialize$ && untrackedValue !== 'undefined') ||
2949
3041
  untrackedValue !== this.$untrackedValue$;
@@ -2954,13 +3046,46 @@ class SerializerSignalImpl extends ComputedSignalImpl {
2954
3046
  }
2955
3047
  }
2956
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
+
2957
3082
  /** @internal */
2958
3083
  const createSignal$1 = (value) => {
2959
3084
  return new SignalImpl(null, value);
2960
3085
  };
2961
3086
  /** @internal */
2962
3087
  const createComputedSignal = (qrl, options) => {
2963
- return new ComputedSignalImpl(options?.container || null, qrl, getComputedSignalFlags(options?.serializationStrategy || 'always'));
3088
+ return new ComputedSignalImpl(options?.container || null, qrl, getComputedSignalFlags(options?.serializationStrategy || 'always'), options);
2964
3089
  };
2965
3090
  /** @internal */
2966
3091
  const createAsyncSignal = (qrl, options) => {
@@ -2984,9 +3109,10 @@ const createSignal = createSignal$1;
2984
3109
  * recalculated.
2985
3110
  *
2986
3111
  * The QRL must be a function which returns the value of the signal. The function must not have side
2987
- * effects, and it must be synchronous.
2988
- *
2989
- * 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.
2990
3116
  *
2991
3117
  * @public
2992
3118
  */
@@ -2995,6 +3121,7 @@ const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedSignal);
2995
3121
  * Create a signal holding a `.value` which is calculated from the given async function (QRL). The
2996
3122
  * standalone version of `useAsync$`.
2997
3123
  *
3124
+ * @deprecated Use `createComputed$` instead, it has async support now.
2998
3125
  * @public
2999
3126
  */
3000
3127
  const createAsync$ = /*#__PURE__*/ implicit$FirstArg(createAsyncSignal);
@@ -3041,10 +3168,10 @@ const _wrapProp = (...args) => {
3041
3168
  return obj[prop];
3042
3169
  }
3043
3170
  if (isSignal(obj)) {
3044
- if (!(obj instanceof AsyncSignalImpl)) {
3171
+ if (!(obj instanceof ComputedSignalImpl)) {
3045
3172
  isDev && assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
3046
3173
  }
3047
- if (obj instanceof WrappedSignalImpl && obj.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */) {
3174
+ if (obj instanceof WrappedSignalImpl) {
3048
3175
  return obj;
3049
3176
  }
3050
3177
  return getWrapped(args);
@@ -3098,7 +3225,7 @@ class WrappedSignalImpl extends SignalImpl {
3098
3225
  constructor(container, fn, args, fnStr,
3099
3226
  // We need a separate flag to know when the computation needs running because
3100
3227
  // we need the old value to know if effects need running after computation
3101
- flags = 1 /* SignalFlags.INVALID */ | 4 /* WrappedSignalFlags.UNWRAP */) {
3228
+ flags = 1 /* ComputedSignalFlags.INVALID */) {
3102
3229
  super(container, NEEDS_COMPUTATION);
3103
3230
  this.$args$ = args;
3104
3231
  this.$func$ = fn;
@@ -3106,7 +3233,7 @@ class WrappedSignalImpl extends SignalImpl {
3106
3233
  this.$flags$ = flags;
3107
3234
  }
3108
3235
  invalidate() {
3109
- this.$flags$ |= 1 /* SignalFlags.INVALID */;
3236
+ this.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
3110
3237
  // we are trying to run computation without creating a chore, which can be expensive
3111
3238
  // for many signals. If it fails, we schedule a chore to run the computation.
3112
3239
  try {
@@ -3119,8 +3246,8 @@ class WrappedSignalImpl extends SignalImpl {
3119
3246
  }
3120
3247
  }
3121
3248
  // if the computation not failed, we can run the effects directly
3122
- if (this.$flags$ & 2 /* SignalFlags.RUN_EFFECTS */) {
3123
- this.$flags$ &= -3 /* SignalFlags.RUN_EFFECTS */;
3249
+ if (this.$flags$ & 2 /* ComputedSignalFlags.RUN_EFFECTS */) {
3250
+ this.$flags$ &= -3 /* ComputedSignalFlags.RUN_EFFECTS */;
3124
3251
  scheduleEffects(this.$container$, this, this.$effects$);
3125
3252
  }
3126
3253
  }
@@ -3130,7 +3257,7 @@ class WrappedSignalImpl extends SignalImpl {
3130
3257
  return this.$untrackedValue$;
3131
3258
  }
3132
3259
  $computeIfNeeded$() {
3133
- if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
3260
+ if (!(this.$flags$ & 1 /* ComputedSignalFlags.INVALID */)) {
3134
3261
  return;
3135
3262
  }
3136
3263
  const untrackedValue = trackSignal(() => this.$func$(...this.$args$), this, "." /* EffectProperty.VNODE */, this.$container$);
@@ -3141,10 +3268,10 @@ class WrappedSignalImpl extends SignalImpl {
3141
3268
  // this.$flags$ &= ~SignalFlags.INVALID;
3142
3269
  // reset flag in case we call computedIfNeeded twice and the value was changed only the first time
3143
3270
  // TODO: change to version number?
3144
- this.$flags$ &= -3 /* SignalFlags.RUN_EFFECTS */;
3271
+ this.$flags$ &= -3 /* ComputedSignalFlags.RUN_EFFECTS */;
3145
3272
  const didChange = untrackedValue !== this.$untrackedValue$;
3146
3273
  if (didChange) {
3147
- this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
3274
+ this.$flags$ |= 2 /* ComputedSignalFlags.RUN_EFFECTS */;
3148
3275
  this.$untrackedValue$ = untrackedValue;
3149
3276
  }
3150
3277
  }
@@ -3248,12 +3375,15 @@ const isJSXNode = (n) => {
3248
3375
  * load the QRL captured scope ourselves. Otherwise, we are being called as a QRL and the captures
3249
3376
  * are already set.
3250
3377
  */
3251
- const maybeScopeFromQL = (captureIds, element) => {
3252
- if (typeof captureIds === 'string') {
3378
+ const withScopeFromQL = (captureDeltas, element, callback) => {
3379
+ if (typeof captureDeltas === 'string') {
3253
3380
  const container = getDomContainer(element);
3254
- setCaptures(deserializeCaptures(container, captureIds));
3381
+ return whenContainerDataReady(container, () => {
3382
+ setCaptures(deserializeCaptureDeltas(container, captureDeltas));
3383
+ return callback();
3384
+ });
3255
3385
  }
3256
- return null;
3386
+ return callback();
3257
3387
  };
3258
3388
  /**
3259
3389
  * Handles events for bind:value
@@ -3261,9 +3391,10 @@ const maybeScopeFromQL = (captureIds, element) => {
3261
3391
  * @internal
3262
3392
  */
3263
3393
  function _val(_, element) {
3264
- maybeScopeFromQL(this, element);
3265
- const signal = _captures[0];
3266
- 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
+ });
3267
3398
  }
3268
3399
  /**
3269
3400
  * Handles events for bind:checked
@@ -3271,9 +3402,10 @@ function _val(_, element) {
3271
3402
  * @internal
3272
3403
  */
3273
3404
  function _chk(_, element) {
3274
- maybeScopeFromQL(this, element);
3275
- const signal = _captures[0];
3276
- signal.value = element.checked;
3405
+ return withScopeFromQL(this, element, () => {
3406
+ const signal = _captures[0];
3407
+ signal.value = element.checked;
3408
+ });
3277
3409
  }
3278
3410
  /**
3279
3411
  * Resumes selected state (e.g. polling AsyncSignals) by deserializing captures. Used for
@@ -3282,17 +3414,18 @@ function _chk(_, element) {
3282
3414
  * @internal
3283
3415
  */
3284
3416
  function _res(_, element) {
3285
- maybeScopeFromQL(this, element);
3286
- // Captures are deserialized, now trigger computation on AsyncSignals
3287
- if (_captures) {
3288
- for (let i = 0; i < _captures.length; i++) {
3289
- const capture = _captures[i];
3290
- if (capture instanceof AsyncSignalImpl && capture.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
3291
- 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
3292
3426
  }
3293
- // note that polling async signals will automatically schedule themselves so no action needed
3294
3427
  }
3295
- }
3428
+ });
3296
3429
  }
3297
3430
 
3298
3431
  const BIND_VALUE = 'bind:value';
@@ -4072,7 +4205,8 @@ function runEventHandlerQRL(handler, event, element, ctx) {
4072
4205
  return;
4073
4206
  }
4074
4207
  if (!ctx) {
4075
- ctx = newInvokeContextFromDOM(event, element);
4208
+ const container = getDomContainer(element);
4209
+ return whenContainerDataReady(container, () => runEventHandlerQRL(handler, event, element, newInvokeContextFromDOM(event, element, container)));
4076
4210
  }
4077
4211
  const container = ctx.$container$;
4078
4212
  const hostElement = ctx.$hostElement$;
@@ -4113,13 +4247,16 @@ function _run(event, element) {
4113
4247
  // ignore events on disconnected elements, this can happen when the event is triggered while the element is being removed
4114
4248
  return;
4115
4249
  }
4116
- const ctx = newInvokeContextFromDOM(event, element);
4117
- if (typeof this === 'string') {
4118
- setCaptures(deserializeCaptures(ctx.$container$, this));
4119
- }
4120
- const qrlToRun = _captures[0];
4121
- isDev && assertQrl(qrlToRun);
4122
- 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
+ });
4123
4260
  }
4124
4261
 
4125
4262
  function peekNextSibling(vCurrent) {
@@ -4280,9 +4417,7 @@ function diff(diffContext, jsxNode, vStartNode, vCurrent = vnode_getFirstChild(v
4280
4417
  else if (type === Projection) {
4281
4418
  expectProjection(diffContext);
4282
4419
  descend(diffContext, diffContext.$jsxValue$.children, true,
4283
- // special case for projection, we don't want to expect no children
4284
- // because the projection's children are not removed
4285
- false);
4420
+ /* isProjection */ true);
4286
4421
  }
4287
4422
  else if (type === SSRComment) {
4288
4423
  expectNoMore(diffContext);
@@ -4395,9 +4530,11 @@ function advance(diffContext) {
4395
4530
  *
4396
4531
  * In the above example all nodes are on same level so we don't `descendVNode` even thought there is
4397
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.
4398
4535
  */
4399
- function descend(diffContext, children, descendVNode, shouldExpectNoChildren = true) {
4400
- if (shouldExpectNoChildren &&
4536
+ function descend(diffContext, children, descendVNode, isProjection = false) {
4537
+ if (!isProjection &&
4401
4538
  (children == null || (descendVNode && isArray(children) && children.length === 0))) {
4402
4539
  expectNoChildren(diffContext);
4403
4540
  return;
@@ -4406,7 +4543,10 @@ function descend(diffContext, children, descendVNode, shouldExpectNoChildren = t
4406
4543
  if (descendVNode) {
4407
4544
  isDev &&
4408
4545
  assertDefined(diffContext.$vCurrent$ || diffContext.$vNewNode$, 'Expecting vCurrent to be defined.');
4409
- 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$) ||
4410
4550
  !!diffContext.$vNewNode$ ||
4411
4551
  !vnode_getFirstChild(diffContext.$vCurrent$);
4412
4552
  diffContext.$isCreationMode$ = creationMode;
@@ -4839,18 +4979,20 @@ function diffProps(diffContext, vnode, newAttrs, currentFile) {
4839
4979
  vnode_ensureElementInflated(diffContext.$container$, vnode);
4840
4980
  }
4841
4981
  const oldAttrs = vnode.props;
4982
+ const isHtml = (vnode.flags & 1536 /* VNodeFlags.NAMESPACE_MASK */) === 0 /* VNodeFlags.NS_html */;
4842
4983
  // Actual diffing logic
4843
4984
  // Apply all new attributes
4844
4985
  for (const key in newAttrs) {
4845
4986
  const newValue = newAttrs[key];
4846
- if (oldAttrs && _hasOwnProperty.call(oldAttrs, key)) {
4847
- const oldValue = oldAttrs[key];
4848
- if (newValue !== oldValue) {
4849
- if (newValue instanceof WrappedSignalImpl &&
4850
- oldValue instanceof WrappedSignalImpl &&
4851
- areWrappedSignalsEqual(newValue, oldValue)) {
4852
- continue;
4853
- }
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)) {
4854
4996
  patchProperty(diffContext, vnode, key, newValue, currentFile);
4855
4997
  }
4856
4998
  }
@@ -5320,13 +5462,26 @@ function handleChangedProps(src, dst, propsHandler, container, triggerEffects =
5320
5462
  if (key === 'children' || key === QBackRefs) {
5321
5463
  continue;
5322
5464
  }
5323
- 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
+ }
5324
5479
  if (triggerEffects) {
5325
5480
  if (dst) {
5326
5481
  // Update the value in dst BEFORE triggering effects
5327
5482
  // so effects see the new value
5328
5483
  // Note: Value is not triggering effects, because we are modyfing direct VAR_PROPS object
5329
- dst[key] = src[key];
5484
+ dst[key] = newValue;
5330
5485
  }
5331
5486
  const didTigger = triggerPropsProxyEffect(propsHandler, key);
5332
5487
  if (!didTigger) {
@@ -5413,10 +5568,16 @@ function cleanup(container, journal, vNode, cursorRoot = null) {
5413
5568
  // don't call cleanupDestroyable yet, do it by the scheduler
5414
5569
  continue;
5415
5570
  }
5416
- else if (obj instanceof SignalImpl || isStore(obj)) {
5417
- 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
+ }
5418
5577
  }
5419
- if (objIsTask || obj instanceof AsyncSignalImpl) {
5578
+ if (objIsTask ||
5579
+ (obj instanceof ComputedSignalImpl &&
5580
+ (obj.$flags$ & 512 /* AsyncSignalFlags.ASYNC_MODE */ || obj.$current$))) {
5420
5581
  cleanupDestroyable(obj);
5421
5582
  }
5422
5583
  }
@@ -5535,6 +5696,37 @@ function markVNodeAsDeleted(vCursor) {
5535
5696
  */
5536
5697
  vCursor.flags |= 32 /* VNodeFlags.Deleted */;
5537
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
+ }
5538
5730
  function areWrappedSignalsEqual(oldSignal, newSignal) {
5539
5731
  if (oldSignal === newSignal) {
5540
5732
  return true;
@@ -6222,9 +6414,17 @@ function executeNodeDiff(vNode, container, journal, cursor) {
6222
6414
  * @returns Promise if component execution is async, void otherwise
6223
6415
  */
6224
6416
  function executeComponentChore(vNode, container, journal, cursor) {
6225
- vNode.dirty &= -5 /* ChoreBits.COMPONENT */;
6226
6417
  const host = vNode;
6227
6418
  const componentQRL = container.getHostProp(host, OnRenderProp);
6419
+ if (componentQRL && !componentQRL.resolved) {
6420
+ /**
6421
+ * If the component QRL is not resolved, we need to wait for it to resolve before we can execute
6422
+ * it. We just return the promise and we'll be called again, possibly with updated props, which
6423
+ * is why we don't clear the dirty bit yet.
6424
+ */
6425
+ return componentQRL.resolve();
6426
+ }
6427
+ vNode.dirty &= -5 /* ChoreBits.COMPONENT */;
6228
6428
  if (!componentQRL) {
6229
6429
  return;
6230
6430
  }
@@ -6375,8 +6575,8 @@ function executeCompute(vNode, container) {
6375
6575
  ctx.$container$ = container;
6376
6576
  // needed for computed signals and throwing QRLs
6377
6577
  return maybeThen(retryOnPromise(() => invoke.call(target, ctx, target.$computeIfNeeded$)), () => {
6378
- if (target.$flags$ & 2 /* SignalFlags.RUN_EFFECTS */) {
6379
- target.$flags$ &= -3 /* SignalFlags.RUN_EFFECTS */;
6578
+ if (target.$flags$ & 2 /* ComputedSignalFlags.RUN_EFFECTS */) {
6579
+ target.$flags$ &= -3 /* ComputedSignalFlags.RUN_EFFECTS */;
6380
6580
  return scheduleEffects(container, target, effects);
6381
6581
  }
6382
6582
  });
@@ -6917,6 +7117,10 @@ function splitCursorBoundary(container, vNode) {
6917
7117
  * @returns The vNode itself, now acting as a cursor
6918
7118
  */
6919
7119
  function addCursor(container, root, priority) {
7120
+ if (isCursor(root)) {
7121
+ triggerCursors();
7122
+ return root;
7123
+ }
6920
7124
  const cursorData = {
6921
7125
  afterFlushTasks: null,
6922
7126
  extraPromises: null,
@@ -6969,7 +7173,11 @@ function _executeSsrChores(container, ssrNode) {
6969
7173
  if (ssrNode.dirty & 32 /* ChoreBits.COMPUTE */) {
6970
7174
  executeCompute(ssrNode, container);
6971
7175
  }
6972
- 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 */;
6973
7181
  // We are running on the server.
6974
7182
  // On server we can't schedule task for a different host!
6975
7183
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -8686,8 +8894,17 @@ const processVNodeData$1 = (vData, callback) => {
8686
8894
  }
8687
8895
  return vData.substring(start, nextToConsumeIdx);
8688
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
+ };
8689
8906
  while (peek() !== 0) {
8690
- callback(peek, consumeValue, consume, getChar, nextToConsumeIdx);
8907
+ callback(peek, consumeValue, consume, getChar, nextToConsumeIdx, consumeEncodedValue);
8691
8908
  }
8692
8909
  };
8693
8910
  /** @internal */
@@ -8878,13 +9095,13 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8878
9095
  return !nodeIsElement || (nodeIsElement && shouldSkipElement(node));
8879
9096
  };
8880
9097
  let components = null;
8881
- processVNodeData$1(vData, (peek, consumeValue, consume, getChar, nextToConsumeIdx) => {
9098
+ processVNodeData$1(vData, (peek, consumeValue, consume, getChar, dataIdx, consumeEncodedValue) => {
8882
9099
  if (isNumber(peek())) {
8883
9100
  // Element counts get encoded as numbers.
8884
9101
  while (shouldSkipNode(child)) {
8885
9102
  child = fastNextSibling(child);
8886
9103
  if (!child) {
8887
- throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), nextToConsumeIdx]);
9104
+ throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), dataIdx]);
8888
9105
  }
8889
9106
  }
8890
9107
  combinedText = null;
@@ -8905,7 +9122,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8905
9122
  shouldSkipElement(child)) {
8906
9123
  child = fastNextSibling(child);
8907
9124
  if (!child && value > 0) {
8908
- throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), nextToConsumeIdx]);
9125
+ throw qError(27 /* QError.materializeVNodeDataError */, [vData, peek(), dataIdx]);
8909
9126
  }
8910
9127
  }
8911
9128
  }
@@ -8930,17 +9147,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8930
9147
  vnode_setProp(vParent, ELEMENT_PROPS, consumeValue());
8931
9148
  }
8932
9149
  else if (peek() === VNodeDataChar.KEY) {
8933
- const isEscapedValue = getChar(nextToConsumeIdx + 1) === VNodeDataChar.SEPARATOR;
8934
- let value;
8935
- if (isEscapedValue) {
8936
- consume();
8937
- value = decodeURIComponent(decodeVNodeDataString(consumeValue()));
8938
- consume();
8939
- }
8940
- else {
8941
- value = consumeValue();
8942
- }
8943
- vParent.key = value;
9150
+ vParent.key = consumeEncodedValue();
8944
9151
  }
8945
9152
  else if (peek() === VNodeDataChar.SEQ) {
8946
9153
  vnode_setProp(vParent, ELEMENT_SEQ, consumeValue());
@@ -8975,7 +9182,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8975
9182
  // Custom attribute: |key|value
8976
9183
  const keyValue = consumeValue();
8977
9184
  const key = decodeVNodeDataString(keyValue);
8978
- const valueSeparatorIdx = nextToConsumeIdx + keyValue.length + 1;
9185
+ const valueSeparatorIdx = dataIdx + keyValue.length + 1;
8979
9186
  const isEscapedValue = getChar(valueSeparatorIdx + 1) === VNodeDataChar.SEPARATOR;
8980
9187
  let value;
8981
9188
  if (isEscapedValue) {
@@ -8998,8 +9205,7 @@ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8998
9205
  vParent = stack.pop();
8999
9206
  }
9000
9207
  else if (peek() === VNodeDataChar.SLOT) {
9001
- const value = decodeVNodeDataString(consumeValue());
9002
- vnode_setProp(vParent, QSlot, value);
9208
+ vnode_setProp(vParent, QSlot, consumeEncodedValue());
9003
9209
  }
9004
9210
  else {
9005
9211
  // skip over style or non-qwik elements in front of text nodes, where text node is the first child (except the style node)
@@ -9110,12 +9316,12 @@ function clearEffectSubscription(container, effect) {
9110
9316
  return;
9111
9317
  }
9112
9318
  for (const producer of backRefs) {
9113
- // Check AsyncSignalImpl before SignalImpl since it extends SignalImpl
9114
- if (producer instanceof AsyncSignalImpl) {
9115
- clearAsyncSignal(producer, effect);
9116
- }
9117
- else if (producer instanceof SignalImpl) {
9319
+ if (producer instanceof SignalImpl) {
9118
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
+ }
9119
9325
  }
9120
9326
  else if (isPropsProxy(producer)) {
9121
9327
  const propsHandler = producer[_PROPS_HANDLER];
@@ -9140,11 +9346,7 @@ function clearSignal(container, producer, effect) {
9140
9346
  clearAllEffects(container, producer);
9141
9347
  }
9142
9348
  }
9143
- function clearAsyncSignal(producer, effect) {
9144
- const effects = producer.$effects$;
9145
- if (effects && effects.has(effect)) {
9146
- effects.delete(effect);
9147
- }
9349
+ function clearAsyncSignalEffects(producer, effect) {
9148
9350
  const pendingEffects = producer.$loadingEffects$;
9149
9351
  if (pendingEffects && pendingEffects.has(effect)) {
9150
9352
  pendingEffects.delete(effect);
@@ -9180,7 +9382,7 @@ const useTaskQrl = (qrl, opts) => {
9180
9382
  const taskFlags =
9181
9383
  // enabled by default
9182
9384
  opts?.deferUpdates === false ? 0 : 8 /* TaskFlags.RENDER_BLOCKING */;
9183
- 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);
9184
9386
  // In V2 we add the task to the sequential scope. We need to do this
9185
9387
  // in order to be able to retrieve it later when the parent element is
9186
9388
  // deleted and we need to be able to release the task subscriptions.
@@ -9233,22 +9435,21 @@ const runTask = (task, container, host) => {
9233
9435
  }
9234
9436
  return result;
9235
9437
  };
9438
+ /** @internal */
9236
9439
  class Task extends BackRef {
9237
9440
  $flags$;
9238
9441
  $index$;
9239
9442
  $el$;
9240
9443
  $qrl$;
9241
- $state$;
9242
9444
  $destroy$;
9243
9445
  $destroyPromise$;
9244
9446
  $taskPromise$ = null;
9245
- constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
9447
+ constructor($flags$, $index$, $el$, $qrl$, $destroy$) {
9246
9448
  super();
9247
9449
  this.$flags$ = $flags$;
9248
9450
  this.$index$ = $index$;
9249
9451
  this.$el$ = $el$;
9250
9452
  this.$qrl$ = $qrl$;
9251
- this.$state$ = $state$;
9252
9453
  this.$destroy$ = $destroy$;
9253
9454
  }
9254
9455
  }
@@ -9264,12 +9465,14 @@ const isTask = (value) => {
9264
9465
  */
9265
9466
  function scheduleTask(_event, element) {
9266
9467
  const container = getDomContainer(element);
9267
- if (typeof this === 'string') {
9268
- setCaptures(deserializeCaptures(container, this));
9269
- }
9270
- const task = _captures[0];
9271
- task.$flags$ |= 4 /* TaskFlags.DIRTY */;
9272
- 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
+ });
9273
9476
  }
9274
9477
 
9275
9478
  const throwIfQRLNotResolved = (qrl) => {
@@ -9381,7 +9584,7 @@ const isSerializerObj = (obj) => {
9381
9584
  return isObject(obj) && typeof obj[SerializerSymbol] === 'function';
9382
9585
  };
9383
9586
  const getComputedSignalFlags = (serializationStrategy) => {
9384
- let flags = 1 /* SignalFlags.INVALID */;
9587
+ let flags = 1 /* ComputedSignalFlags.INVALID */;
9385
9588
  switch (serializationStrategy) {
9386
9589
  // TODO: implement this in the future
9387
9590
  // case 'auto':
@@ -9396,6 +9599,18 @@ const getComputedSignalFlags = (serializationStrategy) => {
9396
9599
  }
9397
9600
  return flags;
9398
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
+ };
9399
9614
 
9400
9615
  const stringifyPath = [];
9401
9616
  function qwikDebugToString(value) {
@@ -9556,6 +9771,7 @@ const unwrapStore = (value) => {
9556
9771
  const isStore = (value) => {
9557
9772
  return STORE_TARGET in value;
9558
9773
  };
9774
+ /** @internal */
9559
9775
  function createStore(container, obj, flags) {
9560
9776
  return new Proxy(obj, new StoreHandler(flags, container || null));
9561
9777
  }
@@ -9754,6 +9970,7 @@ function getEffects(target, prop, storeEffects) {
9754
9970
 
9755
9971
  /** Used to represent an undefined value that must be serialized */
9756
9972
  const explicitUndefined = Symbol('undefined');
9973
+ const EMPTY_OBJECT_PAYLOAD = 0;
9757
9974
  // Used for allocate, make sure they are in sync with Constants
9758
9975
  const _constants = [
9759
9976
  undefined,
@@ -9774,6 +9991,10 @@ const _constants = [
9774
9991
  Number.MAX_SAFE_INTEGER,
9775
9992
  Number.MAX_SAFE_INTEGER - 1,
9776
9993
  Number.MIN_SAFE_INTEGER,
9994
+ ':',
9995
+ '.',
9996
+ 'id',
9997
+ 'ref',
9777
9998
  ];
9778
9999
  // Used for dumpState, make sure they are in sync with Constants
9779
10000
  const _constantNames = [
@@ -9795,6 +10016,10 @@ const _constantNames = [
9795
10016
  'MAX_SAFE_INTEGER',
9796
10017
  'MAX_SAFE_INTEGER-1',
9797
10018
  'MIN_SAFE_INTEGER',
10019
+ "':'",
10020
+ "'.'",
10021
+ "'id'",
10022
+ "'ref'",
9798
10023
  ];
9799
10024
  // Used for dumpState, make sure they are in sync with TypeIds
9800
10025
  const _typeIdNames = [
@@ -9840,6 +10065,9 @@ const _typeIdNames = [
9840
10065
  'SubscriptionData',
9841
10066
  'EffectSubscription',
9842
10067
  'SubscriptionPatch',
10068
+ 'SubscriptionDataConstTrue',
10069
+ 'SubscriptionDataConstFalse',
10070
+ 'EffectSubscriptionNoData',
9843
10071
  ];
9844
10072
 
9845
10073
  function qrlToString(serializationContext, qrl, raw) {
@@ -9886,17 +10114,25 @@ function qrlToString(serializationContext, qrl, raw) {
9886
10114
  symbol = String(serializationContext.$addSyncFn$(null, 0, fn));
9887
10115
  }
9888
10116
  const captures = qrl.getCaptured();
9889
- let captureIds = null;
10117
+ let captureDeltas = null;
9890
10118
  if (captures && captures.length > 0) {
9891
10119
  // We refer by id so every capture needs to be a root
9892
- 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;
9893
10129
  }
9894
10130
  if (raw) {
9895
- return [chunk, symbol, captureIds];
10131
+ return [chunk, symbol, captureDeltas];
9896
10132
  }
9897
10133
  let qrlStringInline = `${chunk}#${symbol}`;
9898
- if (captureIds) {
9899
- qrlStringInline += `#${captureIds}`;
10134
+ if (captureDeltas) {
10135
+ qrlStringInline += `#${captureDeltas}`;
9900
10136
  }
9901
10137
  return qrlStringInline;
9902
10138
  }
@@ -9908,12 +10144,23 @@ function qrlToChunks(serializationContext, qrl) {
9908
10144
  return prefix;
9909
10145
  }
9910
10146
  const chunks = [prefix, '#'];
9911
- const captureIds = captures.split(' ');
9912
- for (let i = 0; i < captureIds.length; i++) {
9913
- if (i > 0) {
9914
- 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;
9915
10163
  }
9916
- chunks.push(Number(captureIds[i]));
9917
10164
  }
9918
10165
  return chunks;
9919
10166
  }
@@ -9929,10 +10176,14 @@ function createQRLWithBackChannel(chunk, symbol, captures, container) {
9929
10176
  }
9930
10177
  return createQRL(chunk, symbol, null, qrlImporter, captures, container);
9931
10178
  }
9932
- /** Parses "chunk#hash#...rootRef" */
10179
+ /** Parses "chunk#hash#...captureDelta" */
9933
10180
  function parseQRL(qrl, container) {
9934
- const [chunk, symbol, captures] = qrl.split('#');
9935
- 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);
9936
10187
  }
9937
10188
  const QRL_RUNTIME_CHUNK = 'mock-chunk';
9938
10189
 
@@ -10086,6 +10337,36 @@ class Serializer {
10086
10337
  }
10087
10338
  return Number(key);
10088
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
+ }
10089
10370
  outputString(value) {
10090
10371
  const s = this.stringNeedsJsonEscape$(value) ? JSON.stringify(value) : QUOTE + value + QUOTE;
10091
10372
  let angleBracketIdx = -1;
@@ -10103,6 +10384,9 @@ class Serializer {
10103
10384
  writeRootRefPath(path) {
10104
10385
  this.$writer$.writeRootRefPath(path);
10105
10386
  }
10387
+ writeRootRefDelta(id, base) {
10388
+ this.$writer$.writeRootRefDelta(id, base);
10389
+ }
10106
10390
  outputStringChunks(chunks) {
10107
10391
  this.$writer$.write(QUOTE);
10108
10392
  for (let i = 0; i < chunks.length; i++) {
@@ -10113,6 +10397,9 @@ class Serializer {
10113
10397
  else if (typeof chunk === 'number') {
10114
10398
  this.writeRootRef(chunk);
10115
10399
  }
10400
+ else if ('base' in chunk) {
10401
+ this.writeRootRefDelta(chunk.id, chunk.base);
10402
+ }
10116
10403
  else {
10117
10404
  this.writeRootRefPath(chunk.path);
10118
10405
  }
@@ -10242,6 +10529,9 @@ class Serializer {
10242
10529
  if (value.length === 0) {
10243
10530
  this.output(3 /* TypeIds.Constant */, 4 /* Constants.EmptyString */);
10244
10531
  }
10532
+ else if (this.outputShortStringConstant$(value)) {
10533
+ break;
10534
+ }
10245
10535
  else {
10246
10536
  // If the string is short, we output directly
10247
10537
  // Very short strings add overhead to tracking
@@ -10268,6 +10558,9 @@ class Serializer {
10268
10558
  else if (value === explicitUndefined) {
10269
10559
  this.output(3 /* TypeIds.Constant */, 0 /* Constants.Undefined */);
10270
10560
  }
10561
+ else {
10562
+ throw qError(20 /* QError.serializeErrorUnknownType */, [typeof value]);
10563
+ }
10271
10564
  break;
10272
10565
  case 'function':
10273
10566
  if (value === Slot) {
@@ -10278,27 +10571,38 @@ class Serializer {
10278
10571
  }
10279
10572
  else if (isQrl(value)) {
10280
10573
  if (this.getSeenRefOrOutput(value, index)) {
10281
- const [chunk, symbol, captures] = qrlToString(this.$serializationContext$, value, true);
10574
+ const [chunk, symbol, captureDeltasFromZero] = qrlToString(this.$serializationContext$, value, true);
10282
10575
  let data;
10283
10576
  if (chunk !== '') {
10284
10577
  // not a sync QRL, replace all parts with string references
10285
- data = [
10286
- this.$serializationContext$.$addRoot$(chunk),
10287
- '#',
10288
- this.$serializationContext$.$addRoot$(symbol),
10289
- ];
10290
- if (captures) {
10291
- 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;
10292
10588
  data.push('#');
10293
- for (let i = 0; i < captureIds.length; i++) {
10294
- if (i > 0) {
10295
- 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;
10296
10601
  }
10297
- data.push(Number(captureIds[i]));
10298
10602
  }
10299
10603
  }
10300
10604
  // Since we map QRLs to strings, we need to keep track of this secondary mapping
10301
- const qrlKey = data.join('');
10605
+ const qrlKey = `${chunkRootId}#${symbolRootId - chunkRootId}${captureDeltas ? '#' + captureDeltas : ''}`;
10302
10606
  const existing = this.$qrlMap$.get(qrlKey);
10303
10607
  if (existing) {
10304
10608
  // We encountered the same QRL again, make it a root
@@ -10366,15 +10670,21 @@ class Serializer {
10366
10670
  ]);
10367
10671
  }
10368
10672
  else if (value instanceof SubscriptionData) {
10369
- // TODO make everything optional or use two types
10370
- this.output(39 /* TypeIds.SubscriptionData */, [
10371
- value.data.$scopedStyleIdPrefix$,
10372
- value.data.$isConst$,
10373
- ]);
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
+ }
10374
10680
  }
10375
10681
  else if (value instanceof EffectSubscription) {
10376
- // TODO no data if [null, true]
10377
- 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
+ }
10378
10688
  }
10379
10689
  else if (value instanceof SubscriptionPatch) {
10380
10690
  this.output(41 /* TypeIds.SubscriptionPatch */, [value.rootId, value.subscriptions]);
@@ -10428,7 +10738,7 @@ class Serializer {
10428
10738
  }
10429
10739
  }
10430
10740
  }
10431
- this.output(5 /* TypeIds.Object */, out.length ? out : 0);
10741
+ this.output(5 /* TypeIds.Object */, out.length ? out : EMPTY_OBJECT_PAYLOAD);
10432
10742
  }
10433
10743
  }
10434
10744
  else if (this.$serializationContext$.$isDomRef$(value)) {
@@ -10461,16 +10771,18 @@ class Serializer {
10461
10771
  let v = value.$untrackedValue$;
10462
10772
  const shouldAlwaysSerialize = value.$flags$ & 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
10463
10773
  const shouldNeverSerialize = value.$flags$ & 8 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
10464
- const isInvalid = value.$flags$ & 1 /* SignalFlags.INVALID */;
10774
+ const isInvalid = value.$flags$ & 1 /* ComputedSignalFlags.INVALID */;
10465
10775
  const isSkippable = fastSkipSerialize(value.$untrackedValue$);
10466
- 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$;
10467
10779
  const expires = isAsync && value.$expires$ !== 0 ? value.$expires$ : undefined;
10468
10780
  const concurrency = isAsync && value.$concurrency$ !== 1 ? value.$concurrency$ : undefined;
10469
10781
  const timeout = isAsync && value.$timeoutMs$ !== 0 ? value.$timeoutMs$ : undefined;
10470
10782
  // Send the flags but remove the serialization bits and default to 0 when undefined
10471
10783
  const asyncFlags = (isAsync && value.$flags$ & -25 /* SerializationSignalFlags.SERIALIZATION_ALL_STRATEGIES */) ||
10472
10784
  undefined;
10473
- if (isInvalid || isSkippable) {
10785
+ if (isInvalid || isSkippable || isErrored) {
10474
10786
  v = NEEDS_COMPUTATION;
10475
10787
  }
10476
10788
  else if (shouldAlwaysSerialize) {
@@ -10518,30 +10830,6 @@ class Serializer {
10518
10830
  else if (value instanceof Date) {
10519
10831
  this.output(7 /* TypeIds.Date */, Number.isNaN(value.valueOf()) ? '' : value.valueOf());
10520
10832
  }
10521
- else if (this.$hasTemporal$ && value instanceof Temporal.Duration) {
10522
- this.output(15 /* TypeIds.TemporalDuration */, value.toJSON());
10523
- }
10524
- else if (this.$hasTemporal$ && value instanceof Temporal.Instant) {
10525
- this.output(16 /* TypeIds.TemporalInstant */, value.toJSON());
10526
- }
10527
- else if (this.$hasTemporal$ && value instanceof Temporal.PlainDate) {
10528
- this.output(17 /* TypeIds.TemporalPlainDate */, value.toJSON());
10529
- }
10530
- else if (this.$hasTemporal$ && value instanceof Temporal.PlainDateTime) {
10531
- this.output(18 /* TypeIds.TemporalPlainDateTime */, value.toJSON());
10532
- }
10533
- else if (this.$hasTemporal$ && value instanceof Temporal.PlainMonthDay) {
10534
- this.output(19 /* TypeIds.TemporalPlainMonthDay */, value.toJSON());
10535
- }
10536
- else if (this.$hasTemporal$ && value instanceof Temporal.PlainTime) {
10537
- this.output(20 /* TypeIds.TemporalPlainTime */, value.toJSON());
10538
- }
10539
- else if (this.$hasTemporal$ && value instanceof Temporal.PlainYearMonth) {
10540
- this.output(21 /* TypeIds.TemporalPlainYearMonth */, value.toJSON());
10541
- }
10542
- else if (this.$hasTemporal$ && value instanceof Temporal.ZonedDateTime) {
10543
- this.output(22 /* TypeIds.TemporalZonedDateTime */, value.toJSON());
10544
- }
10545
10833
  else if (value instanceof RegExp) {
10546
10834
  this.output(8 /* TypeIds.Regex */, value.toString());
10547
10835
  }
@@ -10614,7 +10902,7 @@ class Serializer {
10614
10902
  this.output(37 /* TypeIds.JSXNode */, out);
10615
10903
  }
10616
10904
  else if (value instanceof Task) {
10617
- const out = [value.$qrl$, value.$flags$, value.$index$, value.$el$, value.$state$];
10905
+ const out = [value.$qrl$, value.$flags$, value.$index$, value.$el$];
10618
10906
  while (out[out.length - 1] === undefined) {
10619
10907
  out.pop();
10620
10908
  }
@@ -10668,6 +10956,30 @@ class Serializer {
10668
10956
  this.output(2 /* TypeIds.ForwardRef */, this.getForwardRefId(forwardRefId));
10669
10957
  }
10670
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
+ }
10671
10983
  else if (vnode_isVNode(value)) {
10672
10984
  this.output(3 /* TypeIds.Constant */, 0 /* Constants.Undefined */);
10673
10985
  }
@@ -10834,7 +11146,9 @@ const discoverValuesForVNodeData = (vnodeData, callback) => {
10834
11146
  if (attrValue == null ||
10835
11147
  typeof attrValue === 'string' ||
10836
11148
  (typeof attrValue === 'number' && key === ELEMENT_ID) ||
10837
- (key === ELEMENT_PROPS && isObjectEmpty(attrValue))) {
11149
+ (key === ELEMENT_PROPS &&
11150
+ (isObjectEmpty(attrValue) ||
11151
+ shouldSkipComponentProps(value, attrValue)))) {
10838
11152
  continue;
10839
11153
  }
10840
11154
  callback(attrValue);
@@ -10843,6 +11157,21 @@ const discoverValuesForVNodeData = (vnodeData, callback) => {
10843
11157
  }
10844
11158
  };
10845
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
+ };
10846
11175
  /**
10847
11176
  * When serializing the object we need check if it is URL, RegExp, Map, Set, etc. This is time
10848
11177
  * consuming. So if we could know that this is a basic object literal we could skip the check, and
@@ -11341,6 +11670,12 @@ const constantToName = (code) => {
11341
11670
  * @internal
11342
11671
  */
11343
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) {
11344
11679
  const isRootDeepRef = (type, value) => {
11345
11680
  return type === 1 /* TypeIds.RootRef */ && typeof value === 'string' && value.indexOf(' ') !== -1;
11346
11681
  };
@@ -11383,11 +11718,39 @@ function preprocessState(data, container, segmentId, startIndex = 0) {
11383
11718
  else if (isForwardRefsMap(data[i])) {
11384
11719
  container.$forwardRefs$ = data[i + 1];
11385
11720
  }
11721
+ yield;
11386
11722
  }
11387
11723
  }
11388
11724
 
11389
11725
  const resolvers = new WeakMap();
11390
- 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
+ };
11391
11754
  const allocate = (container, typeId, value) => {
11392
11755
  switch (typeId) {
11393
11756
  case 0 /* TypeIds.Plain */:
@@ -11413,10 +11776,17 @@ const allocate = (container, typeId, value) => {
11413
11776
  case 9 /* TypeIds.QRL */: {
11414
11777
  let qrl;
11415
11778
  if (typeof value === 'string') {
11416
- const [chunkId, symbolId, captureIds] = value.split('#');
11417
- const chunk = container.$getObjectById$(chunkId);
11418
- const symbol = container.$getObjectById$(symbolId);
11419
- 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);
11420
11790
  }
11421
11791
  else {
11422
11792
  // Sync qrl
@@ -11425,7 +11795,7 @@ const allocate = (container, typeId, value) => {
11425
11795
  return qrl;
11426
11796
  }
11427
11797
  case 28 /* TypeIds.Task */:
11428
- return new Task(-1, -1, null, null, null, null);
11798
+ return new Task(-1, -1, null, null, null);
11429
11799
  case 6 /* TypeIds.URL */:
11430
11800
  return new URL(value);
11431
11801
  case 7 /* TypeIds.Date */:
@@ -11473,7 +11843,7 @@ const allocate = (container, typeId, value) => {
11473
11843
  const storeValue = allocate(container, t, v);
11474
11844
  const store = getOrCreateStore(storeValue, 0 /* StoreFlags.NONE */, container);
11475
11845
  if (needsInflation(t)) {
11476
- pendingStoreTargets.set(storeValue, { t, v });
11846
+ container.$pendingStoreTargets$.set(storeValue, { t, v });
11477
11847
  }
11478
11848
  // We must store the reference so it doesn't get deserialized again in inflate()
11479
11849
  data[0] = 0 /* TypeIds.Plain */;
@@ -11503,12 +11873,16 @@ const allocate = (container, typeId, value) => {
11503
11873
  // Don't leave unhandled promise rejections
11504
11874
  promise.catch(() => { });
11505
11875
  return promise;
11506
- case 27 /* TypeIds.Uint8Array */:
11876
+ case 27 /* TypeIds.Uint8Array */: {
11877
+ if (!isCanonicalBase64(value)) {
11878
+ throw qError(38 /* QError.invalidUint8ArrayPayload */);
11879
+ }
11507
11880
  const encodedLength = value.length;
11508
- const blocks = encodedLength >>> 2;
11509
- const rest = encodedLength & 3;
11881
+ const blocks = Math.floor(encodedLength / 4);
11882
+ const rest = encodedLength % 4;
11510
11883
  const decodedLength = blocks * 3 + (rest ? rest - 1 : 0);
11511
11884
  return new Uint8Array(decodedLength);
11885
+ }
11512
11886
  case 38 /* TypeIds.PropsProxy */:
11513
11887
  return createPropsProxy(null);
11514
11888
  case 10 /* TypeIds.VNode */:
@@ -11549,7 +11923,12 @@ const allocate = (container, typeId, value) => {
11549
11923
  }
11550
11924
  case 39 /* TypeIds.SubscriptionData */:
11551
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 });
11552
11930
  case 40 /* TypeIds.EffectSubscription */:
11931
+ case 44 /* TypeIds.EffectSubscriptionNoData */:
11553
11932
  return new EffectSubscription(null, null, null, null);
11554
11933
  case 41 /* TypeIds.SubscriptionPatch */:
11555
11934
  return new SubscriptionPatch();
@@ -11985,6 +12364,7 @@ function processJSXNode(ssr, enqueue, value, options) {
11985
12364
  if (isPromise(jsxOutput)) {
11986
12365
  // Defer reading QScopedStyle until after the promise resolves
11987
12366
  enqueue(async () => {
12367
+ await ssr.streamHandler.flush();
11988
12368
  const resolvedOutput = await jsxOutput;
11989
12369
  const compStyleComponentId = addComponentStylePrefix(host.getProp(QScopedStyle));
11990
12370
  enqueue(resolvedOutput);
@@ -12016,7 +12396,7 @@ function processJSXNode(ssr, enqueue, value, options) {
12016
12396
  function maybeAddPollingAsyncSignalToEagerResume(serializationCtx, signal) {
12017
12397
  // Unwrap if it's a WrappedSignalImpl
12018
12398
  const unwrappedSignal = signal instanceof WrappedSignalImpl ? signal.$unwrapIfSignal$() : signal;
12019
- if (unwrappedSignal instanceof AsyncSignalImpl) {
12399
+ if (unwrappedSignal instanceof ComputedSignalImpl) {
12020
12400
  const expires = unwrappedSignal.$expires$;
12021
12401
  // Don't check for $effects$ here - effects are added later during tracking.
12022
12402
  // The AsyncSignal's polling mechanism will check for effects before scheduling.
@@ -12199,7 +12579,7 @@ function setEvent(serializationCtx, key, rawValue, hasMovedCaptures) {
12199
12579
  *
12200
12580
  * For internal qrls (starting with `_`) we assume that they do the right thing.
12201
12581
  */
12202
- if (!qrl.$symbol$.startsWith('_') && (qrl.$captures$?.length || hasMovedCaptures)) {
12582
+ if (!qrl.$symbol$.startsWith('_') && (Array.isArray(qrl.$captures$) || hasMovedCaptures)) {
12203
12583
  qrl = createQRL(null, '_run', _run, null, [qrl]);
12204
12584
  }
12205
12585
  return qrlToChunks(serializationCtx, qrl);
@@ -12411,20 +12791,26 @@ const useOnEventsSequentialScope = () => {
12411
12791
  * @internal
12412
12792
  */
12413
12793
  const _hmr = function (event, element) {
12414
- if (!event.detail.files.some((file) => element.getAttribute('data-qwik-inspector')?.startsWith(file))) {
12415
- return;
12416
- }
12417
12794
  // Deserialize captures from `this` when called via qwikloader/attribute dispatch
12418
- if (typeof this === 'string') {
12419
- const container = getDomContainer(element);
12420
- setCaptures(deserializeCaptures(container, this));
12421
- }
12422
- const host = _captures[0];
12423
12795
  const container = getDomContainer(element);
12424
- markVNodeDirty(container, host, 4 /* ChoreBits.COMPONENT */);
12425
- // Mark HMR as handled
12426
- const doc = element.ownerDocument;
12427
- 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
+ });
12428
12814
  };
12429
12815
  let hmrQrl;
12430
12816
  /**
@@ -12441,7 +12827,7 @@ function _useHmr(devPath) {
12441
12827
  hmrQrl ||= inlinedQrl(_hmr, '_hmr');
12442
12828
  const hostElement = iCtx.$hostElement$;
12443
12829
  // We must capture the vnode to be able to re-render
12444
- const qrl = hmrQrl.w([hostElement]);
12830
+ const qrl = hmrQrl.w([hostElement, devPath]);
12445
12831
  useOnDocument('qHmr', qrl);
12446
12832
  }
12447
12833
 
@@ -12529,22 +12915,93 @@ const isSafeObjectKV = (key, value) => {
12529
12915
  (typeof value !== 'function' || !dangerousObjectKeys.has(key)));
12530
12916
  };
12531
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) {
12532
12987
  if (typeId === 0 /* TypeIds.Plain */) {
12533
12988
  // Already processed
12534
12989
  return;
12535
12990
  }
12991
+ if (typeId === 5 /* TypeIds.Object */ && data !== EMPTY_OBJECT_PAYLOAD && !Array.isArray(data)) {
12992
+ throw new Error('Invalid Object payload');
12993
+ }
12536
12994
  // Restore the complex data, special case for Array
12537
12995
  if (typeId !== 4 /* TypeIds.Array */ && Array.isArray(data)) {
12538
- data = _eagerDeserializeArray(container, data);
12996
+ data = yield* eagerDeserializeArrayIterator(container, data);
12539
12997
  }
12540
12998
  switch (typeId) {
12541
12999
  case 4 /* TypeIds.Array */:
12542
13000
  // Arrays are special, we need to fill the array in place
12543
- _eagerDeserializeArray(container, data, target);
13001
+ yield* eagerDeserializeArrayIterator(container, data, target);
12544
13002
  break;
12545
13003
  case 5 /* TypeIds.Object */:
12546
- if (data === 0) {
12547
- // Special case, was an empty object
13004
+ if (data === EMPTY_OBJECT_PAYLOAD) {
12548
13005
  break;
12549
13006
  }
12550
13007
  for (let i = 0; i < data.length; i += 2) {
@@ -12556,37 +13013,65 @@ const inflate = (container, target, typeId, data) => {
12556
13013
  target[key] = value;
12557
13014
  }
12558
13015
  break;
12559
- 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 */: {
12560
13064
  const task = target;
12561
13065
  const v = data;
12562
13066
  task.$qrl$ = v[0];
12563
13067
  task.$flags$ = v[1];
12564
13068
  task.$index$ = v[2];
12565
13069
  task.$el$ = v[3];
12566
- task.$state$ = v[4];
12567
13070
  break;
13071
+ }
12568
13072
  case 29 /* TypeIds.Component */:
12569
13073
  target[SERIALIZABLE_STATE][0] = data[0];
12570
13074
  break;
12571
- case 35 /* TypeIds.Store */: {
12572
- // Inflate the store target
12573
- const store = unwrapStore(target);
12574
- const storeTarget = pendingStoreTargets.get(store);
12575
- if (storeTarget) {
12576
- pendingStoreTargets.delete(store);
12577
- inflate(container, store, storeTarget.t, storeTarget.v);
12578
- }
12579
- /**
12580
- * Note that we don't do anything with the innerstores we added during serialization, because
12581
- * they are already inflated in the deserialize of the data, above.
12582
- */
12583
- const [, flags, effects] = data;
12584
- const storeHandler = getStoreHandler(target);
12585
- storeHandler.$flags$ = flags;
12586
- storeHandler.$effects$ = effects;
12587
- restoreEffectBackRefForEffectsMap(storeHandler.$effects$, store);
12588
- break;
12589
- }
12590
13075
  case 30 /* TypeIds.Signal */: {
12591
13076
  const signal = target;
12592
13077
  const d = data;
@@ -12602,7 +13087,7 @@ const inflate = (container, target, typeId, data) => {
12602
13087
  signal.$args$ = d[1];
12603
13088
  signal.$untrackedValue$ = NEEDS_COMPUTATION;
12604
13089
  signal.$flags$ = d[2];
12605
- signal.$flags$ |= 1 /* SignalFlags.INVALID */;
13090
+ signal.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
12606
13091
  signal.$hostElement$ = d[3];
12607
13092
  signal.$effects$ = new Set(d.slice(4));
12608
13093
  inflateWrappedSignalValue(signal);
@@ -12628,7 +13113,7 @@ const inflate = (container, target, typeId, data) => {
12628
13113
  asyncSignal.$flags$ = d[5] ?? 0;
12629
13114
  if (asyncSignal.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
12630
13115
  // If it's client only, it was serialized because it pretended to be loading
12631
- asyncSignal.$untrackedLoading$ = true;
13116
+ asyncSignal.$untrackedPending$ = true;
12632
13117
  }
12633
13118
  const hasValue = d.length > 6;
12634
13119
  if (hasValue) {
@@ -12636,7 +13121,7 @@ const inflate = (container, target, typeId, data) => {
12636
13121
  }
12637
13122
  // can happen when never serialize etc
12638
13123
  if (asyncSignal.$untrackedValue$ === NEEDS_COMPUTATION) {
12639
- asyncSignal.$flags$ |= 1 /* SignalFlags.INVALID */;
13124
+ asyncSignal.$flags$ |= 1 /* ComputedSignalFlags.INVALID */;
12640
13125
  }
12641
13126
  // Handle old format (negative = no poll) and new format (always positive, flag in d[5])
12642
13127
  const rawExpires = (d[7] ?? 0);
@@ -12676,84 +13161,44 @@ const inflate = (container, target, typeId, data) => {
12676
13161
  if (hasValue) {
12677
13162
  computed.$untrackedValue$ = d[2];
12678
13163
  }
12679
- if (typeId !== 34 /* TypeIds.SerializerSignal */ && computed.$untrackedValue$ !== NEEDS_COMPUTATION) {
12680
- // If we have a value after SSR, it will always be mean the signal was not invalid
12681
- // The serialized signal is always left invalid so it can recreate the custom object
12682
- computed.$flags$ &= -2 /* SignalFlags.INVALID */;
12683
- }
12684
- restoreEffectBackRefForEffects(computed.$effects$, computed);
12685
- break;
12686
- }
12687
- case 23 /* TypeIds.Error */: {
12688
- const d = data;
12689
- target.message = d[0];
12690
- for (let i = 1; i < d.length; i += 2) {
12691
- target[d[i]] = d[i + 1];
12692
- }
12693
- break;
12694
- }
12695
- case 36 /* TypeIds.FormData */: {
12696
- const formData = target;
12697
- const d = data;
12698
- for (let i = 0; i < d.length; i++) {
12699
- formData.append(d[i++], d[i]);
12700
- }
12701
- break;
12702
- }
12703
- case 37 /* TypeIds.JSXNode */: {
12704
- const jsx = target;
12705
- const [type, key, varProps, constProps, children, toSort] = data;
12706
- jsx.type = type;
12707
- jsx.key = key;
12708
- jsx.varProps = varProps;
12709
- jsx.constProps = constProps || null;
12710
- jsx.children = children;
12711
- jsx.toSort = !!toSort;
12712
- break;
12713
- }
12714
- case 25 /* TypeIds.Set */: {
12715
- const set = target;
12716
- const d = data;
12717
- for (let i = 0; i < d.length; i++) {
12718
- set.add(d[i]);
12719
- }
12720
- break;
12721
- }
12722
- case 26 /* TypeIds.Map */: {
12723
- const map = target;
12724
- const d = data;
12725
- for (let i = 0; i < d.length; i++) {
12726
- map.set(d[i++], d[i]);
12727
- }
12728
- break;
12729
- }
12730
- case 24 /* TypeIds.Promise */: {
12731
- const promise = target;
12732
- const [resolved, result] = data;
12733
- const [resolve, reject] = resolvers.get(promise);
12734
- if (resolved) {
12735
- resolve(result);
12736
- }
12737
- else {
12738
- reject(result);
12739
- }
13164
+ if (typeId !== 34 /* TypeIds.SerializerSignal */ && computed.$untrackedValue$ !== NEEDS_COMPUTATION) {
13165
+ // If we have a value after SSR, it will always be mean the signal was not invalid
13166
+ // The serialized signal is always left invalid so it can recreate the custom object
13167
+ computed.$flags$ &= -2 /* ComputedSignalFlags.INVALID */;
13168
+ }
13169
+ restoreEffectBackRefForEffects(computed.$effects$, computed);
12740
13170
  break;
12741
13171
  }
12742
- case 27 /* TypeIds.Uint8Array */:
12743
- const bytes = target;
12744
- const buf = atob(data);
12745
- let i = 0;
12746
- for (let j = 0; j < buf.length; j++) {
12747
- const s = buf[j];
12748
- bytes[i++] = s.charCodeAt(0);
13172
+ case 36 /* TypeIds.FormData */: {
13173
+ const formData = target;
13174
+ const d = data;
13175
+ for (let i = 0; i < d.length; i++) {
13176
+ formData.append(d[i++], d[i]);
13177
+ yield;
12749
13178
  }
12750
13179
  break;
12751
- case 38 /* TypeIds.PropsProxy */:
13180
+ }
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;
13190
+ break;
13191
+ }
13192
+ case 38 /* TypeIds.PropsProxy */: {
12752
13193
  const propsProxy = target;
12753
13194
  const d = data;
12754
13195
  let owner = d[0];
12755
13196
  if (owner === _UNINITIALIZED) {
12756
- 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];
12757
13202
  owner._proxy = propsProxy;
12758
13203
  }
12759
13204
  propsProxy[_OWNER] = owner;
@@ -12761,12 +13206,16 @@ const inflate = (container, target, typeId, data) => {
12761
13206
  propsHandler.$effects$ = d[3];
12762
13207
  restoreEffectBackRefForEffectsMap(propsHandler.$effects$, propsProxy);
12763
13208
  break;
13209
+ }
12764
13210
  case 39 /* TypeIds.SubscriptionData */: {
12765
13211
  const effectData = target;
12766
13212
  effectData.data.$scopedStyleIdPrefix$ = data[0];
12767
13213
  effectData.data.$isConst$ = data[1];
12768
13214
  break;
12769
13215
  }
13216
+ case 42 /* TypeIds.SubscriptionDataConstTrue */:
13217
+ case 43 /* TypeIds.SubscriptionDataConstFalse */:
13218
+ break;
12770
13219
  case 40 /* TypeIds.EffectSubscription */: {
12771
13220
  const effectSub = target;
12772
13221
  const d = data;
@@ -12783,29 +13232,43 @@ const inflate = (container, target, typeId, data) => {
12783
13232
  patch.subscriptions = d[1];
12784
13233
  break;
12785
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
+ }
12786
13269
  default:
12787
13270
  throw qError(16 /* QError.serializeErrorNotImplemented */, [typeId]);
12788
13271
  }
12789
- };
12790
- /**
12791
- * Restores an array eagerly. If you need it lazily, use `deserializeData(container, TypeIds.Array,
12792
- * array)` instead
12793
- */
12794
- const _eagerDeserializeArray = (container, data, output = Array(data.length / 2)) => {
12795
- for (let i = 0; i < data.length; i += 2) {
12796
- output[i / 2] = deserializeData(container, data[i], data[i + 1]);
12797
- }
12798
- return output;
12799
- };
12800
- function deserializeData(container, typeId, value) {
12801
- if (typeId === 0 /* TypeIds.Plain */) {
12802
- return value;
12803
- }
12804
- const propValue = allocate(container, typeId, value);
12805
- if (needsInflation(typeId)) {
12806
- inflate(container, propValue, typeId, value);
12807
- }
12808
- return propValue;
12809
13272
  }
12810
13273
  function inflateWrappedSignalValue(signal) {
12811
13274
  if (signal.$hostElement$ !== null && vnode_isVNode(signal.$hostElement$)) {
@@ -12916,15 +13379,21 @@ class DeserializationHandler {
12916
13379
  return value;
12917
13380
  }
12918
13381
  const container = this.$container$;
12919
- const propValue = allocate(container, typeId, value);
12920
- Reflect.set(target, property, propValue);
12921
- this.$data$[idx] = 0 /* TypeIds.Plain */;
12922
- this.$data$[idx + 1] = propValue;
12923
- /** We stored the reference, so now we can inflate, allowing cycles */
12924
- if (needsInflation(typeId)) {
12925
- 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);
12926
13396
  }
12927
- return propValue;
12928
13397
  }
12929
13398
  has(target, property) {
12930
13399
  if (property === SERIALIZER_PROXY_UNWRAP) {
@@ -12948,6 +13417,54 @@ class DeserializationHandler {
12948
13417
  }
12949
13418
  }
12950
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
+
12951
13468
  /**
12952
13469
  * Serialize data to string using SerializationContext.
12953
13470
  *
@@ -12960,12 +13477,12 @@ async function _serialize(data) {
12960
13477
  return serializationContext.$writer$.toString();
12961
13478
  }
12962
13479
  /**
12963
- * Deserialize data from string to an array of objects.
13480
+ * Deserialize data from string.
12964
13481
  *
12965
13482
  * @param rawStateData - Data to deserialize
12966
13483
  * @internal
12967
13484
  */
12968
- function _deserialize(rawStateData) {
13485
+ async function _deserialize(rawStateData) {
12969
13486
  if (rawStateData == null) {
12970
13487
  throw new Error('No state data to deserialize');
12971
13488
  }
@@ -12973,9 +13490,18 @@ function _deserialize(rawStateData) {
12973
13490
  if (!Array.isArray(stateData) || stateData.length < 2 || typeof stateData[0] !== 'number') {
12974
13491
  throw new Error('Invalid state data');
12975
13492
  }
12976
- const container = _createDeserializeContainer(stateData);
12977
- 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];
12978
13498
  }
13499
+ const runDeserializeIterator = (iterator) => {
13500
+ return new Promise((resolve, reject) => {
13501
+ const state = createYieldingIteratorState(iterator, resolve, reject);
13502
+ scheduleYieldingIterator(state);
13503
+ });
13504
+ };
12979
13505
  function getObjectById(id, stateData) {
12980
13506
  if (typeof id === 'string') {
12981
13507
  id = parseInt(id, 10);
@@ -12989,8 +13515,14 @@ function getObjectById(id, stateData) {
12989
13515
  function _createDeserializeContainer(stateData) {
12990
13516
  // eslint-disable-next-line prefer-const
12991
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) {
12992
13524
  const container = {
12993
- $getObjectById$: (id) => getObjectById(id, state),
13525
+ $getObjectById$: (id) => getObjectById(id, getState()),
12994
13526
  $getForwardRef$: (id) => container.$forwardRefs$?.[Number(id)],
12995
13527
  getSyncFn: (_) => {
12996
13528
  const fn = () => { };
@@ -13001,8 +13533,6 @@ function _createDeserializeContainer(stateData) {
13001
13533
  $forwardRefs$: null,
13002
13534
  };
13003
13535
  preprocessState(stateData, container);
13004
- state = wrapDeserializerProxy(container, stateData);
13005
- container.$state$ = state;
13006
13536
  return container;
13007
13537
  }
13008
13538
 
@@ -13554,13 +14084,12 @@ const mergeSubscriptionSet = (container, producer, backRef, rootEffects, patchEf
13554
14084
  };
13555
14085
 
13556
14086
  const processedStatePatchScripts = new WeakMap();
13557
- /** @internal */
13558
- const processSegmentStateScripts = (container, segmentId) => {
14087
+ function* processSegmentStateScriptsIterator(container, segmentId) {
13559
14088
  if (!__EXPERIMENTAL__.suspense) {
13560
14089
  return;
13561
14090
  }
13562
14091
  const stateContainer = container;
13563
- const qwikStates = stateContainer.element.querySelectorAll(`${stateScriptSelector(stateContainer.$instanceHash$)}${QStatePatchAttrSelector}`);
14092
+ const qwikStates = stateContainer.element.querySelectorAll(`script[type="qwik/state"][q\\:instance="${stateContainer.$instanceHash$}"]${QStatePatchAttrSelector}`);
13564
14093
  const processedScripts = getProcessedStatePatchScripts(container);
13565
14094
  for (let i = 0; i < qwikStates.length; i++) {
13566
14095
  const stateScript = qwikStates[i];
@@ -13571,9 +14100,10 @@ const processSegmentStateScripts = (container, segmentId) => {
13571
14100
  continue;
13572
14101
  }
13573
14102
  processedScripts.add(stateScript);
13574
- processStatePatch(container, stateContainer, stateScript.textContent);
14103
+ yield* processStatePatch(container, stateContainer, stateScript.textContent);
14104
+ yield;
13575
14105
  }
13576
- };
14106
+ }
13577
14107
  const getProcessedStatePatchScripts = (container) => {
13578
14108
  let processedScripts = processedStatePatchScripts.get(container);
13579
14109
  if (!processedScripts) {
@@ -13582,20 +14112,18 @@ const getProcessedStatePatchScripts = (container) => {
13582
14112
  }
13583
14113
  return processedScripts;
13584
14114
  };
13585
- const stateScriptSelector = (instanceHash) => {
13586
- return `script[type="qwik/state"][q\\:instance="${instanceHash}"]`;
13587
- };
13588
- const processStatePatch = (container, stateContainer, textContent) => {
13589
- if (textContent) {
13590
- const [rootStart, rawStateData, forwardRefs, subscriptionPatchRootId] = JSON.parse(textContent);
13591
- appendStatePatchRoots(container, stateContainer, rootStart, rawStateData);
13592
- mergeForwardRefs(stateContainer, forwardRefs || undefined);
13593
- applySubscriptionPatches(container, subscriptionPatchRootId === undefined
13594
- ? undefined
13595
- : stateContainer.$getObjectById$(subscriptionPatchRootId));
14115
+ function* processStatePatch(container, stateContainer, textContent) {
14116
+ if (!textContent) {
14117
+ return;
13596
14118
  }
13597
- };
13598
- 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) {
13599
14127
  const currentRootCount = stateContainer.$rawStateData$.length / 2;
13600
14128
  if (rootStart !== currentRootCount) {
13601
14129
  if (qDev) {
@@ -13605,12 +14133,13 @@ const appendStatePatchRoots = (container, stateContainer, rootStart, rawStateDat
13605
14133
  }
13606
14134
  for (let i = 0; i < rawStateData.length; i++) {
13607
14135
  stateContainer.$rawStateData$[rootStart * 2 + i] = rawStateData[i];
14136
+ yield;
13608
14137
  }
13609
- preprocessState(stateContainer.$rawStateData$, container, undefined, rootStart * 2);
14138
+ yield* preprocessStateIterator(stateContainer.$rawStateData$, container, rootStart * 2);
13610
14139
  stateContainer.$stateData$ = wrapDeserializerProxy(container, stateContainer.$rawStateData$);
13611
14140
  stateContainer.$stateData$.length = stateContainer.$rawStateData$.length / 2;
13612
14141
  stateContainer.$rootForwardRefs$ = stateContainer.$forwardRefs$;
13613
- };
14142
+ }
13614
14143
  const mergeForwardRefs = (stateContainer, forwardRefs) => {
13615
14144
  if (!forwardRefs) {
13616
14145
  return;
@@ -13625,24 +14154,172 @@ const mergeForwardRefs = (stateContainer, forwardRefs) => {
13625
14154
  stateContainer.$forwardRefs$ = rootForwardRefs;
13626
14155
  };
13627
14156
 
13628
- // 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
+ }
13629
14224
  function processOutOfOrderSegmentVNodeData(document, segmentId, contentNode) {
13630
14225
  if (!__EXPERIMENTAL__.suspense || !contentNode) {
13631
14226
  return;
13632
14227
  }
13633
14228
  const qContainerElement = contentNode.closest('[q\\:container]');
13634
- const script = qContainerElement?.querySelector(`script[type="qwik/vnode"][q\\:r="${segmentId}"]:not([q\\:patch])`);
13635
- processVNodeDataImpl(document, segmentId, qContainerElement, contentNode, script?.textContent || undefined);
13636
- const patches = qContainerElement?.querySelectorAll(`script[type="qwik/vnode"][q\\:r="${segmentId}"][q\\:patch]`);
13637
- for (let i = 0; patches && i < patches.length; i++) {
13638
- 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
+ }
13639
14296
  }
13640
14297
  }
13641
- function processVNodeData(document) {
13642
- processVNodeDataImpl(document);
13643
- document.qVNodeDataProcessed = true;
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]);
14309
+ }
13644
14310
  }
13645
- function processVNodeDataImpl(document, segmentId, segmentContainer, segmentContent, segmentVNodeData) {
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
+ }
14321
+ }
14322
+ function* processVNodeDataImpl(document, segmentId, segmentContainer, segmentContent, segmentVNodeData) {
13646
14323
  const Q_CONTAINER = 'q:container';
13647
14324
  const Q_CONTAINER_END = '/' + Q_CONTAINER;
13648
14325
  const Q_PROPS_SEPARATOR = ':';
@@ -13651,9 +14328,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13651
14328
  const Q_IGNORE_END = '/' + Q_IGNORE;
13652
14329
  const Q_CONTAINER_ISLAND = 'q:container-island';
13653
14330
  const Q_CONTAINER_ISLAND_END = '/' + Q_CONTAINER_ISLAND;
13654
- const Q_SUSPENSE_RESOLVED = 'q:r';
13655
14331
  const Q_SUSPENSE_RESULT_PARENT = 'q:rp';
13656
- const Q_PATCH = 'q:patch';
13657
14332
  const qDocument = document;
13658
14333
  const vNodeDataMap = (qDocument.qVNodeData ||= new WeakMap());
13659
14334
  const prototype = document.body;
@@ -13671,7 +14346,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13671
14346
  const hasAttribute = prototype.hasAttribute;
13672
14347
  const getNodeType = getter(prototype, 'nodeType');
13673
14348
  // Process all of the `qwik/vnode` script tags by attaching them to the corresponding containers.
13674
- const attachVnodeDataAndRefs = (element) => {
14349
+ const attachVnodeDataAndRefs = function* (element) {
13675
14350
  const scripts = element.querySelectorAll('script[type="qwik/vnode"]');
13676
14351
  for (let i = 0; i < scripts.length; i++) {
13677
14352
  const script = scripts[i];
@@ -13679,6 +14354,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13679
14354
  qContainer.qVNodeRefs ||= new Map();
13680
14355
  if (script.hasAttribute(Q_PATCH)) {
13681
14356
  qDocument.qProcessVNodeDataPatch?.(script);
14357
+ yield;
13682
14358
  continue;
13683
14359
  }
13684
14360
  const scriptContent = script.textContent;
@@ -13689,14 +14365,21 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13689
14365
  else {
13690
14366
  qContainer.qVnodeData = scriptContent;
13691
14367
  }
14368
+ yield;
13692
14369
  }
13693
14370
  const shadowRoots = element.querySelectorAll('[q\\:shadowroot]');
13694
14371
  for (let i = 0; i < shadowRoots.length; i++) {
13695
14372
  const parent = shadowRoots[i];
13696
14373
  const shadowRoot = parent.shadowRoot;
13697
- shadowRoot && attachVnodeDataAndRefs(shadowRoot);
14374
+ if (shadowRoot) {
14375
+ yield* attachVnodeDataAndRefs(shadowRoot);
14376
+ }
14377
+ yield;
13698
14378
  }
13699
14379
  };
14380
+ ///////////////////////////////
14381
+ // Functions to consume the tree.
14382
+ ///////////////////////////////
13700
14383
  /**
13701
14384
  * Looks up which type of node this is in a monomorphic way which should be faster.
13702
14385
  *
@@ -13752,7 +14435,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13752
14435
  * @param exitNode The node which represents the last node and we should exit.
13753
14436
  * @param qVNodeRefs Place to store the VNodeRefs
13754
14437
  */
13755
- const walkContainer = (walker, containerNode, node, exitNode, vData, qVNodeRefs, qContainerElement, segmentId) => {
14438
+ const walkContainer = function* (walker, containerNode, node, exitNode, vData, qVNodeRefs, qContainerElement, segmentId) {
13756
14439
  const isSegment = __EXPERIMENTAL__.suspense && segmentId !== undefined;
13757
14440
  const vData_length = vData.length;
13758
14441
  /// Stores the current element index as the TreeWalker traverses the DOM.
@@ -13778,6 +14461,9 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13778
14461
  }
13779
14462
  return elementsToSkip;
13780
14463
  };
14464
+ if (!node) {
14465
+ return;
14466
+ }
13781
14467
  do {
13782
14468
  if (node === exitNode) {
13783
14469
  return;
@@ -13791,7 +14477,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13791
14477
  while (cursor && !(nextNode = nextSibling(cursor))) {
13792
14478
  cursor = cursor.parentNode;
13793
14479
  }
13794
- walkContainer(walker, container, node, nextNode, container.qVnodeData || '', container.qVNodeRefs, container);
14480
+ yield* walkContainer(walker, container, node, nextNode, container.qVnodeData || '', container.qVNodeRefs, container);
13795
14481
  }
13796
14482
  else if (nodeType === 16 /* NodeType.COMMENT_IGNORE_START */) {
13797
14483
  let islandNode = node;
@@ -13826,7 +14512,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13826
14512
  throw new Error(`<!--${node?.nodeValue}--> not closed!`);
13827
14513
  }
13828
14514
  } while (getFastNodeType(nextNode) !== 8 /* NodeType.COMMENT_SKIP_END */);
13829
- walkContainer(walker, node, node, nextNode, '', null, qContainerElement);
14515
+ yield* walkContainer(walker, node, node, nextNode, '', null, qContainerElement);
13830
14516
  }
13831
14517
  else if (nodeType === 6 /* NodeType.ELEMENT_SHADOW_ROOT_WRAPPER */) {
13832
14518
  // If we are in a shadow root, we need to get the shadow root element.
@@ -13834,7 +14520,7 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13834
14520
  const shadowRootContainer = node;
13835
14521
  const shadowRoot = shadowRootContainer?.shadowRoot;
13836
14522
  if (shadowRoot) {
13837
- walkContainer(
14523
+ yield* walkContainer(
13838
14524
  // we need to create a new walker for the shadow root
13839
14525
  document.createTreeWalker(shadowRoot, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */), null, shadowRoot, null, '', null, null);
13840
14526
  }
@@ -13897,51 +14583,42 @@ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentCont
13897
14583
  }
13898
14584
  elementIdx++;
13899
14585
  if (__EXPERIMENTAL__.suspense && contentBoundaryId !== null) {
13900
- processSuspenseContentSegment(qContainerElement, node, contentBoundaryId);
14586
+ yield* processSuspenseContentSegment(qContainerElement, node, contentBoundaryId);
13901
14587
  nextNode = nextSibling(node);
13902
14588
  if (nextNode) {
13903
14589
  walker.currentNode = nextNode;
13904
14590
  }
13905
14591
  }
13906
14592
  }
14593
+ yield;
13907
14594
  } while ((node = nextNode || walker.nextNode()));
13908
14595
  };
13909
- const processVNodeDataScope = (qContainerElement, contentNode, vData, scopeSegmentId) => {
14596
+ const processVNodeDataScope = function* (qContainerElement, contentNode, vData, scopeSegmentId) {
13910
14597
  qContainerElement.qVNodeRefs ||= new Map();
13911
14598
  const scopeWalker = document.createTreeWalker(document, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */);
13912
14599
  scopeWalker.currentNode = contentNode;
13913
- walkContainer(scopeWalker, contentNode, contentNode, nextSibling(contentNode), vData, qContainerElement.qVNodeRefs, qContainerElement, scopeSegmentId);
14600
+ yield* walkContainer(scopeWalker, contentNode, contentNode, nextSibling(contentNode), vData, qContainerElement.qVNodeRefs, qContainerElement, scopeSegmentId);
13914
14601
  };
13915
- const processSuspenseContentSegment = __EXPERIMENTAL__.suspense
13916
- ? (qContainerElement, contentNode, boundaryId, vData) => {
13917
- vData ||= qContainerElement?.qSegmentVnodeData?.get(boundaryId);
13918
- if (qContainerElement && vData) {
13919
- processVNodeDataScope(qContainerElement, contentNode, vData, boundaryId);
13920
- }
13921
- }
13922
- : null;
13923
- qDocument.qProcessVNodeDataPatch = (script) => {
13924
- const qContainerElement = script?.closest('[q\\:container]');
13925
- const patchSegment = script?.getAttribute(Q_SUSPENSE_RESOLVED);
13926
- const contentNode = qContainerElement &&
13927
- (patchSegment
13928
- ? qContainerElement.querySelector(`[q\\:rp="${patchSegment}"]`)
13929
- : qContainerElement);
13930
- if (qContainerElement && contentNode) {
13931
- 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);
13932
14606
  }
13933
14607
  };
13934
- if (__EXPERIMENTAL__.suspense && segmentId !== undefined) {
13935
- if (segmentContainer && segmentContent && segmentVNodeData) {
13936
- segmentContainer.qVNodeRefs ||= new Map();
13937
- 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);
13938
14615
  }
13939
14616
  return;
13940
14617
  }
13941
- attachVnodeDataAndRefs(document);
14618
+ yield* attachVnodeDataAndRefs(document);
13942
14619
  // Walk the tree and process each `q:container` element.
13943
14620
  const walker = document.createTreeWalker(document, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */);
13944
- walkContainer(walker, null, walker.firstChild(), null, '', null, null);
14621
+ yield* walkContainer(walker, null, walker.firstChild(), null, '', null, null);
13945
14622
  }
13946
14623
  const isSeparator = (ch) =>
13947
14624
  /* `!` */ VNodeDataSeparator.ADVANCE_1 <= ch && ch <= VNodeDataSeparator.ADVANCE_8192; /* `.` */
@@ -13976,6 +14653,87 @@ const findVDataSectionEnd = (vData, start, end) => {
13976
14653
  return start;
13977
14654
  };
13978
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
+
13979
14737
  /** @file Public APIs for the SSR */
13980
14738
  /** @public */
13981
14739
  function getDomContainer(element) {
@@ -13998,8 +14756,12 @@ function getOutOfOrderStreamingScript(boundaryId, content) {
13998
14756
  const qContainerElement = content?.closest(QContainerSelector);
13999
14757
  const qContainer = qContainerElement?.qContainer;
14000
14758
  if (qContainer) {
14001
- processOutOfOrderSegmentVNodeData(qContainer.element.ownerDocument, segmentId, content);
14002
- 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
+ });
14003
14765
  }
14004
14766
  }
14005
14767
  /** @internal */
@@ -14014,6 +14776,9 @@ class DomContainer extends _SharedContainer {
14014
14776
  $instanceHash$;
14015
14777
  $forwardRefs$ = null;
14016
14778
  vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
14779
+ $containerDataProcessState$ = 0 /* ContainerDataProcessState.NotStarted */;
14780
+ $containerStateReadyCallbacks$ = undefined;
14781
+ $containerStateDataState$ = undefined;
14017
14782
  $rawStateData$;
14018
14783
  $stateData$;
14019
14784
  $rootForwardRefs$ = null;
@@ -14024,7 +14789,8 @@ class DomContainer extends _SharedContainer {
14024
14789
  if (!this.qContainer) {
14025
14790
  throw qError(25 /* QError.elementWithoutContainer */);
14026
14791
  }
14027
- this.document = element.ownerDocument;
14792
+ const document = element.ownerDocument;
14793
+ this.document = document;
14028
14794
  this.element = element;
14029
14795
  this.$buildBase$ = element.getAttribute(QBaseAttr);
14030
14796
  this.$instanceHash$ = element.getAttribute(QInstanceAttr);
@@ -14032,23 +14798,46 @@ class DomContainer extends _SharedContainer {
14032
14798
  this.rootVNode = vnode_newUnMaterializedElement(this.element);
14033
14799
  this.$rawStateData$ = [];
14034
14800
  this.$stateData$ = [];
14035
- const document = this.element.ownerDocument;
14036
- if (!document.qVNodeDataProcessed) {
14037
- processVNodeData(document);
14038
- }
14039
14801
  if (__EXPERIMENTAL__.suspense && document.querySelector('template[q\\:r]')) {
14040
14802
  document.qProcessOOOS ||= getOutOfOrderStreamingScript;
14041
14803
  }
14042
14804
  this.$qFuncs$ = getQFuncs(document, this.$instanceHash$) || EMPTY_ARRAY;
14043
14805
  this.$setServerData$();
14044
- element.setAttribute(QContainerAttr, "resumed" /* QContainerValue.RESUMED */);
14045
14806
  element.qContainer = this;
14046
14807
  element.qDestroy = () => this.$destroy$();
14047
- 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
+ }
14048
14836
  if (__EXPERIMENTAL__.suspense) {
14049
- processSegmentStateScripts(this);
14837
+ yield* processSegmentStateScriptsIterator(this);
14050
14838
  }
14051
14839
  this.$hoistStyles$();
14840
+ element.setAttribute(QContainerAttr, "resumed" /* QContainerValue.RESUMED */);
14052
14841
  if (!qTest && element.isConnected) {
14053
14842
  element.dispatchEvent(new CustomEvent('qresume', { bubbles: true }));
14054
14843
  }
@@ -14068,9 +14857,15 @@ class DomContainer extends _SharedContainer {
14068
14857
  }
14069
14858
  el.removeAttribute(QContainerAttr);
14070
14859
  const document = el.ownerDocument;
14860
+ this.$containerStateDataState$ = undefined;
14861
+ this.$containerDataProcessState$ = 0 /* ContainerDataProcessState.NotStarted */;
14071
14862
  const hasContainers = document.querySelector(QContainerSelector) !== null;
14072
14863
  if (!hasContainers) {
14073
14864
  document.qVNodeData = undefined;
14865
+ document.qVNodeDataStarted = undefined;
14866
+ document.qVNodeDataReady = undefined;
14867
+ document.qVNodeDataState = undefined;
14868
+ document.qVNodeDataCallbacks = undefined;
14074
14869
  document.qVNodeDataProcessed = undefined;
14075
14870
  document.qProcessVNodeDataPatch = undefined;
14076
14871
  }
@@ -14080,18 +14875,6 @@ class DomContainer extends _SharedContainer {
14080
14875
  }
14081
14876
  }
14082
14877
  }
14083
- $processRootStateScript$() {
14084
- const rootState = this.element.querySelector(`${this.$stateScriptSelector$()}:not(${QStatePatchAttrSelector})`);
14085
- if (rootState) {
14086
- this.$rawStateData$ = JSON.parse(rootState.textContent);
14087
- preprocessState(this.$rawStateData$, this);
14088
- this.$rootForwardRefs$ = this.$forwardRefs$;
14089
- this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
14090
- }
14091
- }
14092
- $stateScriptSelector$() {
14093
- return `script[type="qwik/state"][q\\:instance="${this.$instanceHash$}"]`;
14094
- }
14095
14878
  /**
14096
14879
  * The first time we render we need to hoist the styles. (Meaning we need to move all styles from
14097
14880
  * component inline to <head>)
@@ -14361,7 +15144,11 @@ const useInvokeContext = () => {
14361
15144
  isDev && assertDefined(ctx.$effectSubscriber$, `invoke: $effectSubscriber$ must be defined`, ctx);
14362
15145
  return ctx;
14363
15146
  };
14364
- /** Call a function with the given InvokeContext and given arguments. */
15147
+ /**
15148
+ * Call a function with the given InvokeContext and given arguments.
15149
+ *
15150
+ * @internal
15151
+ */
14365
15152
  function invoke(context, fn, ...args) {
14366
15153
  return invokeApply.call(this, context, fn, args);
14367
15154
  }
@@ -14376,8 +15163,7 @@ function invokeApply(context, fn, args) {
14376
15163
  _context = previousContext;
14377
15164
  }
14378
15165
  }
14379
- const newInvokeContextFromDOM = (event, element) => {
14380
- const domContainer = getDomContainer(element);
15166
+ const newInvokeContextFromDOM = (event, element, domContainer = getDomContainer(element)) => {
14381
15167
  const hostElement = vnode_locate(domContainer.rootVNode, element);
14382
15168
  const locale = domContainer.$locale$;
14383
15169
  locale && setLocale(locale);
@@ -14397,6 +15183,7 @@ function newRenderInvokeContext(locale, hostElement, container) {
14397
15183
  seal(ctx);
14398
15184
  return ctx;
14399
15185
  }
15186
+ /** @internal */
14400
15187
  // TODO how about putting url and locale (and event/custom?) in to a "static" object
14401
15188
  function newInvokeContext(locale, hostElement, event) {
14402
15189
  // ServerRequestEvent has .locale, but it's not always defined.
@@ -14518,7 +15305,7 @@ const initLazyRefDev = (lazy) => {
14518
15305
  };
14519
15306
  /** Validate captured scope and register WeakRef tracking on a QRLClass instance. */
14520
15307
  const initQrlClassDev = (lazy, captures, qrl) => {
14521
- if (captures && typeof captures === 'object') {
15308
+ if (Array.isArray(captures)) {
14522
15309
  for (let i = 0; i < captures.length; i++) {
14523
15310
  const item = captures[i];
14524
15311
  verifySerializable(item, 'Captured variable in the closure can not be serialized');
@@ -14555,7 +15342,7 @@ const setupHmr = (LazyRefClass, setGetLazyRef) => {
14555
15342
  params.push(`t=${t}`);
14556
15343
  return `${path}?${params.join('&')}`;
14557
15344
  };
14558
- document.addEventListener('qHmr', (ev) => {
15345
+ const hmrHandler = (ev) => {
14559
15346
  const files = ev.detail.files;
14560
15347
  const t = ev.detail.t || document.__hmrT || Date.now();
14561
15348
  let didReload = false;
@@ -14591,7 +15378,6 @@ const setupHmr = (LazyRefClass, setGetLazyRef) => {
14591
15378
  }
14592
15379
  if (didReload) {
14593
15380
  lazy.$ref$ = undefined;
14594
- document.__hmrDone = document.__hmrT;
14595
15381
  if (lazy.qrls) {
14596
15382
  for (const qrlRef of lazy.qrls) {
14597
15383
  const qrl = qrlRef.deref();
@@ -14607,7 +15393,8 @@ const setupHmr = (LazyRefClass, setGetLazyRef) => {
14607
15393
  }
14608
15394
  }
14609
15395
  }
14610
- });
15396
+ };
15397
+ document.addEventListener('qHmr', hmrHandler, true);
14611
15398
  };
14612
15399
 
14613
15400
  // keep these imports above the rest to prevent circular dep issues
@@ -14720,7 +15507,9 @@ class QRLClass {
14720
15507
  }
14721
15508
  // If it is plain value with deserialized or missing captures, resolve it immediately
14722
15509
  // Otherwise we keep using the async path so we can wait for qrls to load
14723
- 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$)) {
14724
15513
  this.resolved = bindCaptures(this, $lazy$.$ref$);
14725
15514
  }
14726
15515
  }
@@ -14875,31 +15664,50 @@ let _captures = null;
14875
15664
  const setCaptures = (captures) => {
14876
15665
  _captures = captures;
14877
15666
  };
14878
- const deserializeCaptures = (container, captures) => {
15667
+ const deserializeCaptureDeltas = (container, deltaString, startIndex = 0, previousRootId) => {
14879
15668
  const refs = [];
14880
- const captureIds = captures.split(' ');
14881
- for (let i = 0; i < captureIds.length; i++) {
14882
- const id = captureIds[i];
14883
- 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
+ }
14884
15680
  }
14885
15681
  return refs;
14886
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
+ };
14887
15692
  /** Puts the qrl captures into `_captures`, and returns a Promise that should be awaited if possible */
14888
15693
  const ensureQrlCaptures = (qrl) => {
14889
15694
  // We read the captures once, synchronously, so no need to keep previous
14890
- _captures = qrl.$captures$;
15695
+ const serializedCaptures = qrl.$captures$;
14891
15696
  const container = qrl.$container$;
14892
- if (typeof _captures === 'string') {
15697
+ if (typeof serializedCaptures === 'string') {
14893
15698
  if (!container) {
14894
15699
  throw qError(13 /* QError.qrlMissingContainer */);
14895
15700
  }
14896
15701
  const prevLoading = loading;
14897
- _captures = qrl.$captures$ = deserializeCaptures(container, _captures);
15702
+ _captures = qrl.$captures$ = deserializeQrlCaptureDeltas(container, serializedCaptures);
14898
15703
  if (loading !== prevLoading) {
14899
15704
  // return the loading promise so callers can await it
14900
15705
  return loading;
14901
15706
  }
14902
15707
  }
15708
+ else {
15709
+ _captures = serializedCaptures || null;
15710
+ }
14903
15711
  };
14904
15712
  // Wrap functions to provide their lexical scope
14905
15713
  const bindCaptures = (qrl, ref) => {
@@ -15866,7 +16674,13 @@ const useComputedQrl = (qrl, options) => {
15866
16674
  * recalculated, and if the result changed, all tasks which are tracking the signal will be re-run
15867
16675
  * and all components that read the signal will be re-rendered.
15868
16676
  *
15869
- * 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.
15870
16684
  *
15871
16685
  * @public
15872
16686
  */
@@ -15964,7 +16778,7 @@ const useVisibleTaskQrl = (qrl, opts) => {
15964
16778
  // In SSR we defer execution until triggered in DOM
15965
16779
  flags = 1 /* TaskFlags.VISIBLE_TASK */;
15966
16780
  }
15967
- const task = new Task(flags, i, iCtx.$hostElement$, qrl, undefined, null);
16781
+ const task = new Task(flags, i, iCtx.$hostElement$, qrl, null);
15968
16782
  set(task);
15969
16783
  useRegisterTaskEvents(task, eagerness);
15970
16784
  };
@@ -16002,11 +16816,11 @@ const getTaskHandlerQrl = (task) => {
16002
16816
  * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
16003
16817
  * re-throw it (or a new Error), the resource status will never be `rejected`.
16004
16818
  *
16005
- * @deprecated Use `useAsync$` instead, which is more powerful and flexible. `useResource$` is still
16006
- * available for backward compatibility but it is recommended to migrate to `useAsync$` for new
16007
- * 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.
16008
16822
  * @public
16009
- * @see useAsync$
16823
+ * @see useComputed$
16010
16824
  * @see Resource
16011
16825
  * @see ResourceReturn
16012
16826
  */
@@ -16111,7 +16925,10 @@ const creator = (qrl, options) => {
16111
16925
  qrl.resolve();
16112
16926
  return createAsyncSignal(qrl, options);
16113
16927
  };
16114
- /** @internal */
16928
+ /**
16929
+ * @deprecated Use `useComputed$` instead, it has async support now.
16930
+ * @internal
16931
+ */
16115
16932
  const useAsyncQrl = (qrl, options) => {
16116
16933
  return useConstant((creator), qrl, options);
16117
16934
  };
@@ -16134,6 +16951,8 @@ const useAsyncQrl = (qrl, options) => {
16134
16951
  * will subscribe to it and return the last resolved value until the new value is ready. As soon as
16135
16952
  * the new value is ready, the subscribers will be updated.
16136
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.
16137
16956
  * @public
16138
16957
  */
16139
16958
  const useAsync$ = implicit$FirstArg(useAsyncQrl);
@@ -16170,6 +16989,46 @@ const eachCmp = (props) => {
16170
16989
  const Each = /*#__PURE__*/ componentQrl(
16171
16990
  /*#__PURE__*/ inlinedQrl(eachCmp, '_eaC'));
16172
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
+
16173
17032
  const RevealContext = /*#__PURE__*/ createContextId('qk-reveal');
16174
17033
  const createRevealContext = (props) => {
16175
17034
  return {
@@ -16563,5 +17422,5 @@ if (import.meta.hot) {
16563
17422
  });
16564
17423
  }
16565
17424
 
16566
- 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, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, SubscriptionPatch as _SubscriptionPatch, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, _createDeserializeContainer, createQRL as _createQRL, _deserialize, _dumpState, eachCmp as _eaC, eachCmpTask as _eaT, _executeSsrChores, _fnSignal, getAsyncLocalStorage as _getAsyncLocalStorage, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, _getVarProps, _hasStoreEffects, _hmr, 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, _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, _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 };
16567
17426
  //# sourceMappingURL=core.mjs.map