@qwik.dev/core 2.0.0-alpha.6 → 2.0.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/bindings/qwik.darwin-arm64.node +0 -0
  2. package/bindings/qwik.darwin-x64.node +0 -0
  3. package/bindings/qwik.linux-x64-gnu.node +0 -0
  4. package/bindings/qwik.win32-x64-msvc.node +0 -0
  5. package/bindings/qwik_wasm_bg.wasm +0 -0
  6. package/dist/build/package.json +1 -1
  7. package/dist/cli.cjs +65 -42
  8. package/dist/core-internal.d.ts +77 -76
  9. package/dist/core.cjs +1498 -1323
  10. package/dist/core.cjs.map +1 -1
  11. package/dist/core.min.mjs +1 -1
  12. package/dist/core.mjs +1496 -1323
  13. package/dist/core.mjs.map +1 -1
  14. package/dist/core.prod.cjs +884 -782
  15. package/dist/core.prod.mjs +1006 -867
  16. package/dist/insights/index.qwik.cjs +3652 -154
  17. package/dist/insights/index.qwik.mjs +3652 -154
  18. package/dist/loader/index.cjs +2 -2
  19. package/dist/loader/index.mjs +2 -2
  20. package/dist/loader/package.json +1 -1
  21. package/dist/optimizer.cjs +210 -5714
  22. package/dist/optimizer.mjs +191 -6037
  23. package/dist/prefetch/package.json +1 -1
  24. package/dist/qwikloader.debug.js +12 -15
  25. package/dist/qwikloader.js +2 -2
  26. package/dist/server.cjs +762 -7127
  27. package/dist/server.mjs +778 -7121
  28. package/dist/starters/adapters/fastify/src/plugins/fastify-qwik.ts +2 -0
  29. package/dist/starters/features/cypress/package.json +1 -1
  30. package/dist/starters/features/drizzle/drizzle/schema.ts +6 -18
  31. package/dist/starters/features/drizzle/drizzle.config.ts +5 -4
  32. package/dist/starters/features/drizzle/package.json +14 -11
  33. package/dist/starters/features/pandacss/package.json +1 -1
  34. package/dist/starters/features/partytown/package.json +1 -1
  35. package/dist/starters/features/postcss/package.json +1 -1
  36. package/dist/starters/features/prisma/package.json +1 -1
  37. package/dist/starters/features/react/package.json +1 -1
  38. package/dist/starters/features/storybook/package.json +1 -1
  39. package/dist/starters/features/styled-vanilla-extract/package.json +2 -1
  40. package/dist/starters/features/tailwind/package.json +15 -9
  41. package/dist/starters/features/tailwind/src/global.css +1 -7
  42. package/dist/starters/features/turso/package.json +1 -1
  43. package/dist/starters/features/vitest/package.json +1 -1
  44. package/dist/testing/index.cjs +1265 -1124
  45. package/dist/testing/index.mjs +1271 -1124
  46. package/dist/testing/package.json +1 -1
  47. package/handlers.mjs +9 -0
  48. package/package.json +6 -4
  49. package/public.d.ts +2 -0
  50. package/dist/starters/features/tailwind/.vscode/settings.json +0 -3
  51. package/dist/starters/features/tailwind/postcss.config.cjs +0 -6
  52. package/dist/starters/features/tailwind/tailwind.config.js +0 -8
package/dist/core.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * @license
3
- * @qwik.dev/core 2.0.0-alpha.6-dev+d848ba5
3
+ * @qwik.dev/core 2.0.0-alpha.7-dev+a26598a
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
7
7
  */
8
- import { isServer, isDev } from '@qwik.dev/core/build';
8
+ import { isDev, isServer } from '@qwik.dev/core/build';
9
9
  export { isBrowser, isDev, isServer } from '@qwik.dev/core/build';
10
10
 
11
11
  // same as isDev but separate so we can test
@@ -118,8 +118,8 @@ const codeToText = (code, ...parts) => {
118
118
  // Keep one error, one line to make it easier to search for the error message.
119
119
  const MAP = [
120
120
  'Error while serializing class or style attributes', // 0
121
- '', // 1 unused
122
- '', // 2 unused
121
+ 'Scheduler not found', // 1
122
+ 'track() received object, without prop to track', // 2
123
123
  'Only primitive and object literals can be serialized. {{0}}', // 3
124
124
  '', // 4 unused
125
125
  'You can render over a existing q:container. Skipping render().', // 5
@@ -162,12 +162,11 @@ const codeToText = (code, ...parts) => {
162
162
  "Element must have 'q:container' attribute.", // 42
163
163
  'Unknown vnode type {{0}}.', // 43
164
164
  'Materialize error: missing element: {{0}} {{1}} {{2}}', // 44
165
- 'Cannot coerce a Signal, use `.value` instead', // 46
166
- 'useComputedSignal$ QRL {{0}} {{1}} returned a Promise', // 47
167
- 'ComputedSignal is read-only', // 48
168
- 'WrappedSignal is read-only', // 49
169
- 'SsrError: Promises not expected here.', // 50
170
- 'Attribute value is unsafe for SSR', // 51
165
+ 'Cannot coerce a Signal, use `.value` instead', // 45
166
+ 'useComputedSignal$ QRL {{0}} {{1}} returned a Promise', // 46
167
+ 'ComputedSignal is read-only', // 47
168
+ 'WrappedSignal is read-only', // 48
169
+ 'Attribute value is unsafe for SSR', // 49
171
170
  ];
172
171
  let text = MAP[code] ?? '';
173
172
  if (parts.length) {
@@ -189,8 +188,8 @@ const codeToText = (code, ...parts) => {
189
188
  var QError;
190
189
  (function (QError) {
191
190
  QError[QError["stringifyClassOrStyle"] = 0] = "stringifyClassOrStyle";
192
- QError[QError["UNUSED_1"] = 1] = "UNUSED_1";
193
- QError[QError["UNUSED_2"] = 2] = "UNUSED_2";
191
+ QError[QError["schedulerNotFound"] = 1] = "schedulerNotFound";
192
+ QError[QError["trackObjectWithoutProp"] = 2] = "trackObjectWithoutProp";
194
193
  QError[QError["verifySerializable"] = 3] = "verifySerializable";
195
194
  QError[QError["UNUSED_4"] = 4] = "UNUSED_4";
196
195
  QError[QError["cannotRenderOverExistingContainer"] = 5] = "cannotRenderOverExistingContainer";
@@ -237,14 +236,37 @@ var QError;
237
236
  QError[QError["computedNotSync"] = 46] = "computedNotSync";
238
237
  QError[QError["computedReadOnly"] = 47] = "computedReadOnly";
239
238
  QError[QError["wrappedReadOnly"] = 48] = "wrappedReadOnly";
240
- QError[QError["promisesNotExpected"] = 49] = "promisesNotExpected";
241
- QError[QError["unsafeAttr"] = 50] = "unsafeAttr";
239
+ QError[QError["unsafeAttr"] = 49] = "unsafeAttr";
242
240
  })(QError || (QError = {}));
243
241
  const qError = (code, errorMessageArgs = []) => {
244
242
  const text = codeToText(code, ...errorMessageArgs);
245
243
  return logErrorAndStop(text, ...errorMessageArgs);
246
244
  };
247
245
 
246
+ /** QRL related utilities that you can import without importing all of Qwik. */
247
+ const SYNC_QRL = '<sync>';
248
+ /** Sync QRL is a function which is serialized into `<script q:func="qwik/json">` tag. */
249
+ const isSyncQrl = (value) => {
250
+ return isQrl$1(value) && value.$symbol$ == SYNC_QRL;
251
+ };
252
+ const isQrl$1 = (value) => {
253
+ return typeof value === 'function' && typeof value.getSymbol === 'function';
254
+ };
255
+ function assertQrl(qrl) {
256
+ if (isDev) {
257
+ if (!isQrl$1(qrl)) {
258
+ throw new Error('Not a QRL');
259
+ }
260
+ }
261
+ }
262
+ const getSymbolHash = (symbolName) => {
263
+ const index = symbolName.lastIndexOf('_');
264
+ if (index > -1) {
265
+ return symbolName.slice(index + 1);
266
+ }
267
+ return symbolName;
268
+ };
269
+
248
270
  /**
249
271
  * A friendly name tag for a VirtualVNode.
250
272
  *
@@ -288,7 +310,7 @@ var QContainerValue;
288
310
  /** State factory of the component. */
289
311
  const OnRenderProp = 'q:renderFn';
290
312
  /** Component style content prefix */
291
- const ComponentStylesPrefixContent = '⭐️';
313
+ const ComponentStylesPrefixContent = '⚡️';
292
314
  /** `<some-element q:slot="...">` */
293
315
  const QSlot = 'q:slot';
294
316
  const QSlotParent = ':';
@@ -300,7 +322,7 @@ const QStyleSSelector = 'style[q\\:sstyle]';
300
322
  const QStylesAllSelector = QStyleSelector + ',' + QStyleSSelector;
301
323
  const QScopedStyle = 'q:sstyle';
302
324
  const QCtxAttr = 'q:ctx';
303
- const QSubscribers = 'q:subs';
325
+ const QBackRefs = 'q:brefs';
304
326
  const QFuncsPrefix = 'qFuncs_';
305
327
  const getQFuncs = (document, hash) => {
306
328
  return document[QFuncsPrefix + hash] || [];
@@ -496,11 +518,6 @@ const maybeThen = (valueOrPromise, thenFn) => {
496
518
  ? valueOrPromise.then(thenFn, shouldNotError)
497
519
  : thenFn(valueOrPromise);
498
520
  };
499
- const maybeThenPassError = (valueOrPromise, thenFn) => {
500
- return isPromise(valueOrPromise)
501
- ? valueOrPromise.then(thenFn)
502
- : thenFn(valueOrPromise);
503
- };
504
521
  const shouldNotError = (reason) => {
505
522
  throwErrorAndStop(reason);
506
523
  };
@@ -618,7 +635,7 @@ function setLocale(locale) {
618
635
  _locale = locale;
619
636
  }
620
637
 
621
- const isQrl$1 = (value) => {
638
+ const isQrl = (value) => {
622
639
  return typeof value === 'function' && typeof value.getSymbol === 'function';
623
640
  };
624
641
 
@@ -690,21 +707,21 @@ const qrl = (chunkOrFn, symbol, lexicalScopeCapture = EMPTY_ARRAY, stackOffset =
690
707
  // Emit event
691
708
  announcedQRL.add(symbol);
692
709
  emitEvent('qprefetch', {
693
- symbols: [getSymbolHash(symbol)],
710
+ symbols: [symbol],
694
711
  bundles: chunk && [chunk],
695
712
  });
696
713
  }
697
714
  // Unwrap subscribers
698
- return createQRL(chunk, symbol, null, symbolFn, null, lexicalScopeCapture, null);
715
+ return createQRL(chunk, symbol, null, symbolFn, null, lexicalScopeCapture);
699
716
  };
700
717
  /** @internal */
701
718
  const inlinedQrl = (symbol, symbolName, lexicalScopeCapture = EMPTY_ARRAY) => {
702
719
  // Unwrap subscribers
703
- return createQRL(null, symbolName, symbol, null, null, lexicalScopeCapture, null);
720
+ return createQRL(null, symbolName, symbol, null, null, lexicalScopeCapture);
704
721
  };
705
722
  /** @internal */
706
723
  const _noopQrl = (symbolName, lexicalScopeCapture = EMPTY_ARRAY) => {
707
- return createQRL(null, symbolName, null, null, null, lexicalScopeCapture, null);
724
+ return createQRL(null, symbolName, null, null, null, lexicalScopeCapture);
708
725
  };
709
726
  /** @internal */
710
727
  const _noopQrlDEV = (symbolName, opts, lexicalScopeCapture = EMPTY_ARRAY) => {
@@ -733,6 +750,80 @@ const _regSymbol = (symbol, hash) => {
733
750
  return symbol;
734
751
  };
735
752
 
753
+ var ChoreType;
754
+ (function (ChoreType) {
755
+ /// MASKS defining three levels of sorting
756
+ ChoreType[ChoreType["MACRO"] = 240] = "MACRO";
757
+ /* order of elements (not encoded here) */
758
+ ChoreType[ChoreType["MICRO"] = 15] = "MICRO";
759
+ /** Ensure that the QRL promise is resolved before processing next chores in the queue */
760
+ ChoreType[ChoreType["QRL_RESOLVE"] = 1] = "QRL_RESOLVE";
761
+ ChoreType[ChoreType["RUN_QRL"] = 2] = "RUN_QRL";
762
+ ChoreType[ChoreType["TASK"] = 3] = "TASK";
763
+ ChoreType[ChoreType["NODE_DIFF"] = 4] = "NODE_DIFF";
764
+ ChoreType[ChoreType["NODE_PROP"] = 5] = "NODE_PROP";
765
+ ChoreType[ChoreType["COMPONENT"] = 6] = "COMPONENT";
766
+ ChoreType[ChoreType["RECOMPUTE_AND_SCHEDULE_EFFECTS"] = 7] = "RECOMPUTE_AND_SCHEDULE_EFFECTS";
767
+ // Next macro level
768
+ ChoreType[ChoreType["JOURNAL_FLUSH"] = 16] = "JOURNAL_FLUSH";
769
+ // Next macro level
770
+ ChoreType[ChoreType["VISIBLE"] = 32] = "VISIBLE";
771
+ // Next macro level
772
+ ChoreType[ChoreType["CLEANUP_VISIBLE"] = 48] = "CLEANUP_VISIBLE";
773
+ // Next macro level
774
+ ChoreType[ChoreType["WAIT_FOR_ALL"] = 255] = "WAIT_FOR_ALL";
775
+ })(ChoreType || (ChoreType = {}));
776
+
777
+ // <docs markdown="../readme.md#useLexicalScope">
778
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
779
+ // (edit ../readme.md#useLexicalScope instead and run `pnpm docs.sync`)
780
+ /**
781
+ * Used by the Qwik Optimizer to restore the lexically scoped variables.
782
+ *
783
+ * This method should not be present in the application source code.
784
+ *
785
+ * NOTE: `useLexicalScope` method can only be used in the synchronous portion of the callback
786
+ * (before any `await` statements.)
787
+ *
788
+ * @internal
789
+ */
790
+ // </docs>
791
+ const useLexicalScope = () => {
792
+ const context = getInvokeContext();
793
+ let qrl = context.$qrl$;
794
+ if (!qrl) {
795
+ const el = context.$element$;
796
+ assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
797
+ const containerElement = _getQContainerElement(el);
798
+ assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
799
+ const container = getDomContainer(containerElement);
800
+ qrl = container.parseQRL(decodeURIComponent(String(context.$url$)));
801
+ }
802
+ else {
803
+ assertQrl(qrl);
804
+ assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
805
+ }
806
+ return qrl.$captureRef$;
807
+ };
808
+
809
+ /**
810
+ * This is called by qwik-loader to schedule a QRL. It has to be synchronous.
811
+ *
812
+ * @internal
813
+ */
814
+ const queueQRL = (...args) => {
815
+ // This will already check container
816
+ const [runQrl] = useLexicalScope();
817
+ const context = getInvokeContext();
818
+ const hostElement = context.$hostElement$;
819
+ const container = getDomContainer(hostElement);
820
+ const scheduler = container.$scheduler$;
821
+ if (!scheduler) {
822
+ throw qError(QError.schedulerNotFound);
823
+ }
824
+ return scheduler(ChoreType.RUN_QRL, hostElement, runQrl, args);
825
+ };
826
+
736
827
  /**
737
828
  * Allows to project the children of the current component. <Slot/> can only be used within the
738
829
  * context of a component defined with `component$`.
@@ -857,6 +948,47 @@ function isPreventDefault(key) {
857
948
  return key.startsWith('preventdefault:');
858
949
  }
859
950
 
951
+ function getFileLocationFromJsx(jsxDev) {
952
+ if (!jsxDev) {
953
+ return null;
954
+ }
955
+ const sanitizedFileName = jsxDev.fileName?.replace(/\\/g, '/');
956
+ if (sanitizedFileName) {
957
+ return `${sanitizedFileName}:${jsxDev.lineNumber}:${jsxDev.columnNumber}`;
958
+ }
959
+ return null;
960
+ }
961
+
962
+ const styleContent = (styleId) => {
963
+ return ComponentStylesPrefixContent + styleId;
964
+ };
965
+ function hasClassAttr(props) {
966
+ for (const key in props) {
967
+ if (Object.prototype.hasOwnProperty.call(props, key) && isClassAttr(key)) {
968
+ return true;
969
+ }
970
+ }
971
+ return false;
972
+ }
973
+ function isClassAttr(key) {
974
+ return key === 'class' || key === 'className';
975
+ }
976
+ function convertScopedStyleIdsToArray(scopedStyleIds) {
977
+ return scopedStyleIds?.split(' ') ?? null;
978
+ }
979
+ function convertStyleIdsToString(scopedStyleIds) {
980
+ return Array.from(scopedStyleIds).join(' ');
981
+ }
982
+ const addComponentStylePrefix = (styleId) => {
983
+ if (styleId) {
984
+ let idx = 0;
985
+ do {
986
+ styleId = styleId.substring(0, idx) + styleContent(styleId.substring(idx));
987
+ } while ((idx = styleId.indexOf(' ', idx) + 1) !== 0);
988
+ }
989
+ return styleId || null;
990
+ };
991
+
860
992
  /** CSS properties which accept numbers but are not in units of "px". */
861
993
  const unitlessNumbers = new Set([
862
994
  'animationIterationCount',
@@ -1015,36 +1147,6 @@ const styleKey = (qStyles, index) => {
1015
1147
  assertQrl(qStyles);
1016
1148
  return `${hashCode(qStyles.$hash$)}-${index}`;
1017
1149
  };
1018
- const styleContent = (styleId) => {
1019
- return ComponentStylesPrefixContent + styleId;
1020
- };
1021
-
1022
- function hasClassAttr(props) {
1023
- for (const key in props) {
1024
- if (Object.prototype.hasOwnProperty.call(props, key) && isClassAttr(key)) {
1025
- return true;
1026
- }
1027
- }
1028
- return false;
1029
- }
1030
- function isClassAttr(key) {
1031
- return key === 'class' || key === 'className';
1032
- }
1033
- function convertScopedStyleIdsToArray(scopedStyleIds) {
1034
- return scopedStyleIds?.split(' ') ?? null;
1035
- }
1036
- function convertStyleIdsToString(scopedStyleIds) {
1037
- return Array.from(scopedStyleIds).join(' ');
1038
- }
1039
- const addComponentStylePrefix = (styleId) => {
1040
- if (styleId) {
1041
- let idx = 0;
1042
- do {
1043
- styleId = styleId.substring(0, idx) + styleContent(styleId.substring(idx));
1044
- } while ((idx = styleId.indexOf(' ', idx) + 1) !== 0);
1045
- }
1046
- return styleId || null;
1047
- };
1048
1150
 
1049
1151
  const STORE_TARGET = Symbol('store.target');
1050
1152
  const STORE_HANDLER = Symbol('store.handler');
@@ -1189,8 +1291,12 @@ class StoreHandler {
1189
1291
  return Reflect.ownKeys(target);
1190
1292
  }
1191
1293
  getOwnPropertyDescriptor(target, prop) {
1294
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
1192
1295
  if (Array.isArray(target) || typeof prop === 'symbol') {
1193
- return Object.getOwnPropertyDescriptor(target, prop);
1296
+ return descriptor;
1297
+ }
1298
+ if (descriptor && !descriptor.configurable) {
1299
+ return descriptor;
1194
1300
  }
1195
1301
  return {
1196
1302
  enumerable: true,
@@ -1198,355 +1304,280 @@ class StoreHandler {
1198
1304
  };
1199
1305
  }
1200
1306
  }
1201
- function addEffect(target, prop, store, effectSubscriber) {
1202
- const effectsMap = (store.$effects$ ||= {});
1203
- const effects = (Object.prototype.hasOwnProperty.call(effectsMap, prop) && effectsMap[prop]) ||
1204
- (effectsMap[prop] = []);
1307
+ function addEffect(target, prop, store, effectSubscription) {
1308
+ const effectsMap = (store.$effects$ ||= new Map());
1309
+ let effects = effectsMap.get(prop);
1310
+ if (!effects) {
1311
+ effects = new Set();
1312
+ effectsMap.set(prop, effects);
1313
+ }
1205
1314
  // Let's make sure that we have a reference to this effect.
1206
1315
  // Adding reference is essentially adding a subscription, so if the signal
1207
1316
  // changes we know who to notify.
1208
- ensureContainsEffect(effects, effectSubscriber);
1317
+ ensureContainsSubscription(effects, effectSubscription);
1209
1318
  // But when effect is scheduled in needs to be able to know which signals
1210
1319
  // to unsubscribe from. So we need to store the reference from the effect back
1211
1320
  // to this signal.
1212
- ensureContains(effectSubscriber, target);
1213
- // We need to add the subscriber to the effect so that we can clean it up later
1214
- ensureEffectContainsSubscriber(effectSubscriber[EffectSubscriptionsProp.EFFECT], target, store.$container$);
1321
+ ensureContainsBackRef(effectSubscription, target);
1322
+ addQrlToSerializationCtx(effectSubscription, store.$container$);
1215
1323
  }
1216
1324
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
1217
1325
  target[prop] = value;
1218
1326
  triggerEffects(currentStore.$container$, currentStore, getEffects(target, prop, currentStore.$effects$));
1219
1327
  }
1220
1328
  function getEffects(target, prop, storeEffects) {
1221
- let effectsToTrigger = storeEffects
1222
- ? Array.isArray(target)
1223
- ? Object.values(storeEffects).flatMap((effects) => effects)
1224
- : storeEffects[prop]
1225
- : null;
1226
- const storeArrayValue = storeEffects?.[STORE_ARRAY_PROP];
1329
+ let effectsToTrigger;
1330
+ if (storeEffects) {
1331
+ if (Array.isArray(target)) {
1332
+ for (const effects of storeEffects.values()) {
1333
+ effectsToTrigger ||= new Set();
1334
+ for (const effect of effects) {
1335
+ effectsToTrigger.add(effect);
1336
+ }
1337
+ }
1338
+ }
1339
+ else {
1340
+ effectsToTrigger = storeEffects.get(prop);
1341
+ }
1342
+ }
1343
+ const storeArrayValue = storeEffects?.get(STORE_ARRAY_PROP);
1227
1344
  if (storeArrayValue) {
1228
- effectsToTrigger ||= [];
1229
- effectsToTrigger.push(...storeArrayValue);
1345
+ effectsToTrigger ||= new Set();
1346
+ for (const effect of storeArrayValue) {
1347
+ effectsToTrigger.add(effect);
1348
+ }
1230
1349
  }
1231
- return effectsToTrigger;
1350
+ return effectsToTrigger || null;
1232
1351
  }
1233
1352
 
1234
1353
  /**
1235
- * @internal
1236
- * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
1354
+ * Special value used to mark that a given signal needs to be computed. This is essentially a
1355
+ * "marked as dirty" flag.
1237
1356
  */
1238
- const useSequentialScope = () => {
1239
- const iCtx = useInvokeContext();
1240
- const hostElement = iCtx.$hostElement$;
1241
- const host = hostElement;
1242
- let seq = iCtx.$container$.getHostProp(host, ELEMENT_SEQ);
1243
- if (seq === null) {
1244
- seq = [];
1245
- iCtx.$container$.setHostProp(host, ELEMENT_SEQ, seq);
1246
- }
1247
- let seqIdx = iCtx.$container$.getHostProp(host, ELEMENT_SEQ_IDX);
1248
- if (seqIdx === null) {
1249
- seqIdx = 0;
1250
- }
1251
- iCtx.$container$.setHostProp(host, ELEMENT_SEQ_IDX, seqIdx + 1);
1252
- while (seq.length <= seqIdx) {
1253
- seq.push(undefined);
1254
- }
1255
- const set = (value) => {
1256
- if (qDev && qSerialize) {
1257
- verifySerializable(value);
1258
- }
1259
- return (seq[seqIdx] = value);
1260
- };
1261
- return {
1262
- val: seq[seqIdx],
1263
- set,
1264
- i: seqIdx,
1265
- iCtx,
1266
- };
1267
- };
1357
+ const NEEDS_COMPUTATION = Symbol('invalid');
1358
+ /** @internal */
1359
+ const _EFFECT_BACK_REF = Symbol('backRef');
1268
1360
 
1269
- class Subscriber {
1270
- $effectDependencies$ = null;
1361
+ /** Class for back reference to the EffectSubscription */
1362
+ class BackRef {
1363
+ [_EFFECT_BACK_REF] = null;
1271
1364
  }
1272
- function isSubscriber(value) {
1273
- return value instanceof Subscriber || value instanceof WrappedSignal;
1274
- }
1275
- function clearVNodeEffectDependencies(container, value) {
1276
- if (vnode_isElementVNode(value)) {
1277
- ensureMaterialized(value);
1365
+ function clearAllEffects(container, consumer) {
1366
+ if (vnode_isVNode(consumer) && vnode_isElementVNode(consumer)) {
1367
+ ensureMaterialized(consumer);
1278
1368
  }
1279
- const effects = vnode_getProp(value, QSubscribers, container.$getObjectById$);
1369
+ const effects = consumer[_EFFECT_BACK_REF];
1280
1370
  if (!effects) {
1281
1371
  return;
1282
1372
  }
1283
- for (let i = effects.length - 1; i >= 0; i--) {
1284
- const subscriber = effects[i];
1285
- clearEffects(subscriber, value, effects, i, container);
1286
- }
1287
- if (effects.length === 0) {
1288
- vnode_setProp(value, QSubscribers, null);
1289
- }
1290
- }
1291
- function clearSubscriberEffectDependencies(container, value) {
1292
- if (value.$effectDependencies$) {
1293
- for (let i = value.$effectDependencies$.length - 1; i >= 0; i--) {
1294
- const subscriber = value.$effectDependencies$[i];
1295
- clearEffects(subscriber, value, value.$effectDependencies$, i, container);
1296
- }
1297
- if (value.$effectDependencies$.length === 0) {
1298
- value.$effectDependencies$ = null;
1373
+ for (const [, effect] of effects) {
1374
+ const backRefs = effect[EffectSubscriptionProp.BACK_REF];
1375
+ if (!backRefs) {
1376
+ return;
1299
1377
  }
1300
- }
1301
- }
1302
- function clearEffects(subscriber, value, effectArray, indexToRemove, container) {
1303
- let subscriptionRemoved = false;
1304
- const seenSet = new Set();
1305
- if (subscriber instanceof WrappedSignal) {
1306
- subscriptionRemoved = clearSignalEffects(subscriber, value, seenSet);
1307
- }
1308
- else if (container.$storeProxyMap$.has(subscriber)) {
1309
- const store = container.$storeProxyMap$.get(subscriber);
1310
- const handler = getStoreHandler(store);
1311
- subscriptionRemoved = clearStoreEffects(handler, value);
1312
- }
1313
- if (subscriptionRemoved) {
1314
- effectArray.splice(indexToRemove, 1);
1315
- }
1316
- }
1317
- function clearSignalEffects(subscriber, value, seenSet) {
1318
- const effectSubscriptions = subscriber.$effects$;
1319
- let subscriptionRemoved = false;
1320
- if (effectSubscriptions) {
1321
- for (let i = effectSubscriptions.length - 1; i >= 0; i--) {
1322
- const effect = effectSubscriptions[i];
1323
- if (effect[EffectSubscriptionsProp.EFFECT] === value) {
1324
- effectSubscriptions.splice(i, 1);
1325
- subscriptionRemoved = true;
1378
+ for (const producer of backRefs) {
1379
+ if (producer instanceof Signal) {
1380
+ clearSignal(container, producer, effect);
1326
1381
  }
1327
- }
1328
- }
1329
- if (subscriber instanceof WrappedSignal) {
1330
- const hostElement = subscriber.$hostElement$;
1331
- if (hostElement && hostElement === value) {
1332
- subscriber.$hostElement$ = null;
1333
- }
1334
- // clear the effects of the arguments
1335
- const args = subscriber.$args$;
1336
- if (args) {
1337
- clearArgsEffects(args, subscriber, seenSet);
1338
- }
1339
- }
1340
- return subscriptionRemoved;
1341
- }
1342
- function clearStoreEffects(storeHandler, value) {
1343
- const effectSubscriptions = storeHandler.$effects$;
1344
- if (!effectSubscriptions) {
1345
- return false;
1346
- }
1347
- let subscriptionRemoved = false;
1348
- for (const key in effectSubscriptions) {
1349
- const effects = effectSubscriptions[key];
1350
- for (let i = effects.length - 1; i >= 0; i--) {
1351
- const effect = effects[i];
1352
- if (effect[EffectSubscriptionsProp.EFFECT] === value) {
1353
- effects.splice(i, 1);
1354
- subscriptionRemoved = true;
1382
+ else if (container.$storeProxyMap$.has(producer)) {
1383
+ const target = container.$storeProxyMap$.get(producer);
1384
+ const storeHandler = getStoreHandler(target);
1385
+ clearStore(storeHandler, effect);
1355
1386
  }
1356
1387
  }
1357
- if (effects.length === 0) {
1358
- delete effectSubscriptions[key];
1359
- }
1360
- }
1361
- return subscriptionRemoved;
1362
- }
1363
- function clearArgsEffects(args, subscriber, seenSet) {
1364
- for (let i = args.length - 1; i >= 0; i--) {
1365
- const arg = args[i];
1366
- clearArgEffect(arg, subscriber, seenSet);
1367
1388
  }
1368
1389
  }
1369
- function clearArgEffect(arg, subscriber, seenSet) {
1370
- if (seenSet.has(arg)) {
1371
- return;
1390
+ function clearSignal(container, producer, effect) {
1391
+ const effects = producer.$effects$;
1392
+ if (effects) {
1393
+ effects.delete(effect);
1372
1394
  }
1373
- seenSet.add(arg);
1374
- if (isSignal(arg)) {
1375
- clearSignalEffects(arg, subscriber, seenSet);
1395
+ if (producer instanceof WrappedSignal) {
1396
+ producer.$hostElement$ = null;
1397
+ clearAllEffects(container, producer);
1376
1398
  }
1377
- else if (typeof arg === 'object' && arg !== null) {
1378
- if (isStore(arg)) {
1379
- clearStoreEffects(getStoreHandler(arg), subscriber);
1380
- }
1381
- else if (isPropsProxy(arg)) {
1382
- // Separate check for props proxy, because props proxy getter could call signal getter.
1383
- // To avoid that we need to get the constProps and varProps directly
1384
- // from the props proxy object and loop over them.
1385
- const constProps = arg[_CONST_PROPS];
1386
- const varProps = arg[_VAR_PROPS];
1387
- if (constProps) {
1388
- for (const key in constProps) {
1389
- clearArgEffect(constProps[key], subscriber, seenSet);
1390
- }
1391
- }
1392
- for (const key in varProps) {
1393
- clearArgEffect(varProps[key], subscriber, seenSet);
1394
- }
1395
- }
1396
- else {
1397
- for (const key in arg) {
1398
- clearArgEffect(arg[key], subscriber, seenSet);
1399
- }
1399
+ }
1400
+ function clearStore(producer, effect) {
1401
+ const effects = producer?.$effects$;
1402
+ if (effects) {
1403
+ for (const propEffects of effects.values()) {
1404
+ propEffects.delete(effect);
1400
1405
  }
1401
1406
  }
1402
- else if (Array.isArray(arg)) {
1403
- clearArgsEffects(arg, subscriber, seenSet);
1404
- }
1405
- else ;
1406
1407
  }
1407
1408
 
1408
- /** @internal */
1409
- const useResourceQrl = (qrl, opts) => {
1410
- const { val, set, i, iCtx } = useSequentialScope();
1411
- if (val != null) {
1412
- return val;
1413
- }
1414
- assertQrl(qrl);
1415
- const container = iCtx.$container$;
1416
- const resource = createResourceReturn(container, opts);
1417
- const el = iCtx.$hostElement$;
1418
- const task = new Task(TaskFlags.DIRTY | TaskFlags.RESOURCE, i, el, qrl, resource, null);
1419
- runResource(task, container, iCtx.$hostElement$);
1420
- set(resource);
1421
- return resource;
1422
- };
1423
- // <docs markdown="../readme.md#useResource">
1409
+ // <docs markdown="../../readme.md#implicit$FirstArg">
1424
1410
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
1425
- // (edit ../readme.md#useResource instead and run `pnpm docs.sync`)
1411
+ // (edit ../../readme.md#implicit$FirstArg instead and run `pnpm docs.sync`)
1426
1412
  /**
1427
- * This method works like an async memoized function that runs whenever some tracked value changes
1428
- * and returns some data.
1413
+ * Create a `____$(...)` convenience method from `___(...)`.
1429
1414
  *
1430
- * `useResource` however returns immediate a `ResourceReturn` object that contains the data and a
1431
- * state that indicates if the data is available or not.
1415
+ * It is very common for functions to take a lazy-loadable resource as a first argument. For this
1416
+ * reason, the Qwik Optimizer automatically extracts the first argument from any function which ends
1417
+ * in `$`.
1432
1418
  *
1433
- * The status can be one of the following:
1419
+ * This means that `foo$(arg0)` and `foo($(arg0))` are equivalent with respect to Qwik Optimizer.
1420
+ * The former is just a shorthand for the latter.
1434
1421
  *
1435
- * - `pending` - the data is not yet available.
1436
- * - `resolved` - the data is available.
1437
- * - `rejected` - the data is not available due to an error or timeout.
1422
+ * For example, these function calls are equivalent:
1438
1423
  *
1439
- * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
1440
- * re-throw it (or a new Error), the resource status will never be `rejected`.
1424
+ * - `component$(() => {...})` is same as `component($(() => {...}))`
1441
1425
  *
1442
- * ### Example
1426
+ * ```tsx
1427
+ * export function myApi(callback: QRL<() => void>): void {
1428
+ * // ...
1429
+ * }
1443
1430
  *
1444
- * Example showing how `useResource` to perform a fetch to request the weather, whenever the input
1445
- * city name changes.
1431
+ * export const myApi$ = implicit$FirstArg(myApi);
1432
+ * // type of myApi$: (callback: () => void): void
1446
1433
  *
1447
- * ```tsx
1448
- * const Cmp = component$(() => {
1449
- * const cityS = useSignal('');
1434
+ * // can be used as:
1435
+ * myApi$(() => console.log('callback'));
1450
1436
  *
1451
- * const weatherResource = useResource$(async ({ track, cleanup }) => {
1452
- * const cityName = track(cityS);
1453
- * const abortController = new AbortController();
1454
- * cleanup(() => abortController.abort('cleanup'));
1455
- * const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
1456
- * signal: abortController.signal,
1457
- * });
1458
- * const data = await res.json();
1459
- * return data as { temp: number };
1460
- * });
1437
+ * // will be transpiled to:
1438
+ * // FILE: <current file>
1439
+ * myApi(qrl('./chunk-abc.js', 'callback'));
1461
1440
  *
1462
- * return (
1463
- * <div>
1464
- * <input name="city" bind:value={cityS} />
1465
- * <Resource
1466
- * value={weatherResource}
1467
- * onResolved={(weather) => {
1468
- * return <div>Temperature: {weather.temp}</div>;
1469
- * }}
1470
- * />
1471
- * </div>
1472
- * );
1473
- * });
1441
+ * // FILE: chunk-abc.js
1442
+ * export const callback = () => console.log('callback');
1474
1443
  * ```
1475
1444
  *
1445
+ * @param fn - A function that should have its first argument automatically `$`.
1476
1446
  * @public
1477
- * @see Resource
1478
- * @see ResourceReturn
1479
1447
  */
1480
1448
  // </docs>
1481
- const Resource = (props) => {
1482
- // Resource path
1483
- return _jsxSorted(Fragment, null, null, getResourceValueAsPromise(props), 0, null);
1449
+ const implicit$FirstArg = (fn) => {
1450
+ return function (first, ...rest) {
1451
+ return fn.call(null, dollar(first), ...rest);
1452
+ };
1484
1453
  };
1485
- function getResourceValueAsPromise(props) {
1486
- const resource = props.value;
1487
- if (isResourceReturn(resource)) {
1488
- const isBrowser = !isServerPlatform();
1489
- if (isBrowser) {
1490
- // create a subscription for the resource._state changes
1491
- const state = resource._state;
1492
- if (state === 'pending' && props.onPending) {
1493
- return Promise.resolve().then(useBindInvokeContext(props.onPending));
1494
- }
1495
- else if (state === 'rejected' && props.onRejected) {
1496
- return Promise.resolve(resource._error).then(useBindInvokeContext(props.onRejected));
1497
- }
1498
- else {
1499
- const resolvedValue = untrack(() => resource._resolved);
1500
- if (resolvedValue !== undefined) {
1501
- // resolved, pending without onPending prop or rejected without onRejected prop
1502
- return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved));
1503
- }
1504
- }
1505
- }
1506
- return resource.value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1507
- }
1508
- else if (isPromise(resource)) {
1509
- return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1454
+
1455
+ const createSignal$1 = (value) => {
1456
+ return new Signal(null, value);
1457
+ };
1458
+ const createComputedSignal = (qrl) => {
1459
+ throwIfQRLNotResolved(qrl);
1460
+ return new ComputedSignal(null, qrl);
1461
+ };
1462
+
1463
+ /**
1464
+ * Creates a Signal with the given value. If no value is given, the signal is created with
1465
+ * `undefined`.
1466
+ *
1467
+ * @public
1468
+ */
1469
+ const createSignal = createSignal$1;
1470
+ /** @internal */
1471
+ const createComputedQrl = createComputedSignal;
1472
+ /**
1473
+ * Create a computed signal which is calculated from the given QRL. A computed signal is a signal
1474
+ * which is calculated from other signals. When the signals change, the computed signal is
1475
+ * recalculated.
1476
+ *
1477
+ * The QRL must be a function which returns the value of the signal. The function must not have side
1478
+ * effects, and it mus be synchronous.
1479
+ *
1480
+ * If you need the function to be async, use `useSignal` and `useTask$` instead.
1481
+ *
1482
+ * @public
1483
+ */
1484
+ const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedQrl);
1485
+
1486
+ /**
1487
+ * @internal
1488
+ * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
1489
+ */
1490
+ const useSequentialScope = () => {
1491
+ const iCtx = useInvokeContext();
1492
+ const hostElement = iCtx.$hostElement$;
1493
+ const host = hostElement;
1494
+ let seq = iCtx.$container$.getHostProp(host, ELEMENT_SEQ);
1495
+ if (seq === null) {
1496
+ seq = [];
1497
+ iCtx.$container$.setHostProp(host, ELEMENT_SEQ, seq);
1510
1498
  }
1511
- else if (isSignal(resource)) {
1512
- return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1499
+ let seqIdx = iCtx.$container$.getHostProp(host, ELEMENT_SEQ_IDX);
1500
+ if (seqIdx === null) {
1501
+ seqIdx = 0;
1513
1502
  }
1514
- else {
1515
- return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1503
+ iCtx.$container$.setHostProp(host, ELEMENT_SEQ_IDX, seqIdx + 1);
1504
+ while (seq.length <= seqIdx) {
1505
+ seq.push(undefined);
1516
1506
  }
1517
- }
1518
- const _createResourceReturn = (opts) => {
1519
- const resource = {
1520
- __brand: 'resource',
1521
- value: undefined,
1522
- loading: isServerPlatform() ? false : true,
1523
- _resolved: undefined,
1524
- _error: undefined,
1525
- _state: 'pending',
1526
- _timeout: opts?.timeout ?? -1,
1527
- _cache: 0,
1507
+ const set = (value) => {
1508
+ if (qDev && qSerialize) {
1509
+ verifySerializable(value);
1510
+ }
1511
+ return (seq[seqIdx] = value);
1512
+ };
1513
+ return {
1514
+ val: seq[seqIdx],
1515
+ set,
1516
+ i: seqIdx,
1517
+ iCtx,
1528
1518
  };
1529
- return resource;
1530
- };
1531
- const createResourceReturn = (container, opts, initialPromise) => {
1532
- const result = _createResourceReturn(opts);
1533
- result.value = initialPromise;
1534
- return createStore(container, result, StoreFlags.RECURSIVE);
1535
1519
  };
1536
- const isResourceReturn = (obj) => {
1537
- return isObject(obj) && (getStoreTarget(obj) || obj).__brand === 'resource';
1520
+
1521
+ function getSubscriber(effect, prop, data) {
1522
+ if (!effect[_EFFECT_BACK_REF]) {
1523
+ if (isServer && isSsrNode(effect)) {
1524
+ effect.setProp(QBackRefs, new Map());
1525
+ }
1526
+ else {
1527
+ effect[_EFFECT_BACK_REF] = new Map();
1528
+ }
1529
+ }
1530
+ const subMap = effect[_EFFECT_BACK_REF];
1531
+ let sub = subMap.get(prop);
1532
+ if (!sub) {
1533
+ sub = [effect, prop];
1534
+ subMap.set(prop, sub);
1535
+ }
1536
+ if (data) {
1537
+ sub[EffectSubscriptionProp.DATA] = data;
1538
+ }
1539
+ return sub;
1540
+ }
1541
+ function isSsrNode(value) {
1542
+ return '__brand__' in value && 'currentComponentNode' in value;
1543
+ }
1544
+
1545
+ var TaskFlags;
1546
+ (function (TaskFlags) {
1547
+ TaskFlags[TaskFlags["VISIBLE_TASK"] = 1] = "VISIBLE_TASK";
1548
+ TaskFlags[TaskFlags["TASK"] = 2] = "TASK";
1549
+ TaskFlags[TaskFlags["RESOURCE"] = 4] = "RESOURCE";
1550
+ TaskFlags[TaskFlags["DIRTY"] = 8] = "DIRTY";
1551
+ })(TaskFlags || (TaskFlags = {}));
1552
+ /** @internal */
1553
+ const useTaskQrl = (qrl) => {
1554
+ const { val, set, iCtx, i } = useSequentialScope();
1555
+ if (val) {
1556
+ return;
1557
+ }
1558
+ assertQrl(qrl);
1559
+ set(1);
1560
+ const task = new Task(TaskFlags.DIRTY | TaskFlags.TASK, i, iCtx.$hostElement$, qrl, undefined, null);
1561
+ // In V2 we add the task to the sequential scope. We need to do this
1562
+ // in order to be able to retrieve it later when the parent element is
1563
+ // deleted and we need to be able to release the task subscriptions.
1564
+ set(task);
1565
+ const container = iCtx.$container$;
1566
+ const promise = container.$scheduler$(ChoreType.TASK, task);
1567
+ if (isPromise(promise)) {
1568
+ // TODO: should we handle this differently?
1569
+ promise.catch(() => { });
1570
+ }
1538
1571
  };
1539
- const runResource = (task, container, host) => {
1572
+ const runTask = (task, container, host) => {
1540
1573
  task.$flags$ &= ~TaskFlags.DIRTY;
1541
1574
  cleanupTask(task);
1542
- const iCtx = newInvokeContext(container.$locale$, host, undefined, ResourceEvent);
1575
+ const iCtx = newInvokeContext(container.$locale$, host, undefined, TaskEvent);
1543
1576
  iCtx.$container$ = container;
1544
- const taskFn = task.$qrl$.getFn(iCtx, () => clearSubscriberEffectDependencies(container, task));
1545
- const resource = task.$state$;
1546
- assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
1577
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
1547
1578
  const track = (obj, prop) => {
1548
1579
  const ctx = newInvokeContext();
1549
- ctx.$effectSubscriber$ = [task, EffectProperty.COMPONENT];
1580
+ ctx.$effectSubscriber$ = getSubscriber(task, EffectProperty.COMPONENT);
1550
1581
  ctx.$container$ = container;
1551
1582
  return invoke(ctx, () => {
1552
1583
  if (isFunction(obj)) {
@@ -1559,115 +1590,85 @@ const runResource = (task, container, host) => {
1559
1590
  return obj.value;
1560
1591
  }
1561
1592
  else {
1562
- return obj;
1593
+ throw qError(QError.trackObjectWithoutProp);
1563
1594
  }
1564
1595
  });
1565
1596
  };
1566
1597
  const handleError = (reason) => container.handleError(reason, host);
1567
- const cleanups = [];
1568
- task.$destroy$ = noSerialize(() => {
1569
- cleanups.forEach((fn) => {
1570
- try {
1571
- fn();
1572
- }
1573
- catch (err) {
1574
- handleError(err);
1598
+ let cleanupFns = null;
1599
+ const cleanup = (fn) => {
1600
+ if (typeof fn == 'function') {
1601
+ if (!cleanupFns) {
1602
+ cleanupFns = [];
1603
+ task.$destroy$ = noSerialize(() => {
1604
+ task.$destroy$ = null;
1605
+ cleanupFns.forEach((fn) => {
1606
+ try {
1607
+ fn();
1608
+ }
1609
+ catch (err) {
1610
+ handleError(err);
1611
+ }
1612
+ });
1613
+ });
1575
1614
  }
1576
- });
1577
- done = true;
1615
+ cleanupFns.push(fn);
1616
+ }
1617
+ };
1618
+ const taskApi = { track, cleanup };
1619
+ const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
1620
+ // If a Promise is thrown, that means we need to re-run the task.
1621
+ if (isPromise(err)) {
1622
+ return err.then(() => runTask(task, container, host));
1623
+ }
1624
+ else {
1625
+ throw err;
1626
+ }
1578
1627
  });
1579
- const resourceTarget = unwrapStore(resource);
1580
- const opts = {
1581
- track,
1582
- cleanup(fn) {
1583
- if (typeof fn === 'function') {
1584
- cleanups.push(fn);
1585
- }
1586
- },
1587
- cache(policy) {
1588
- let milliseconds = 0;
1589
- if (policy === 'immutable') {
1590
- milliseconds = Infinity;
1591
- }
1592
- else {
1593
- milliseconds = policy;
1594
- }
1595
- resource._cache = milliseconds;
1596
- },
1597
- previous: resourceTarget._resolved,
1598
- };
1599
- let resolve;
1600
- let reject;
1601
- let done = false;
1602
- const setState = (resolved, value) => {
1603
- if (!done) {
1604
- done = true;
1605
- if (resolved) {
1606
- done = true;
1607
- resource.loading = false;
1608
- resource._state = 'resolved';
1609
- resource._resolved = value;
1610
- resource._error = undefined;
1611
- resolve(value);
1612
- }
1613
- else {
1614
- done = true;
1615
- resource.loading = false;
1616
- resource._state = 'rejected';
1617
- resource._error = value;
1618
- reject(value);
1619
- }
1620
- return true;
1621
- }
1622
- return false;
1623
- };
1624
- /**
1625
- * Add cleanup to resolve the resource if we are trying to run the same resource again while the
1626
- * previous one is not resolved yet. The next `runResource` run will call this cleanup
1627
- */
1628
- cleanups.push(() => {
1629
- if (untrack(() => resource.loading) === true) {
1630
- const value = untrack(() => resource._resolved);
1631
- setState(true, value);
1632
- }
1633
- });
1634
- // Execute mutation inside empty invocation
1635
- invoke(iCtx, () => {
1636
- // console.log('RESOURCE.pending: ');
1637
- resource._state = 'pending';
1638
- resource.loading = !isServerPlatform();
1639
- const promise = (resource.value = new Promise((r, re) => {
1640
- resolve = r;
1641
- reject = re;
1642
- }));
1643
- promise.catch(ignoreErrorToPreventNodeFromCrashing);
1644
- });
1645
- const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
1646
- setState(true, value);
1647
- }, (err) => {
1648
- if (isPromise(err)) {
1649
- return err.then(() => runResource(task, container, host));
1628
+ return result;
1629
+ };
1630
+ const cleanupTask = (task) => {
1631
+ const destroy = task.$destroy$;
1632
+ if (destroy) {
1633
+ task.$destroy$ = null;
1634
+ try {
1635
+ destroy();
1650
1636
  }
1651
- else {
1652
- setState(false, err);
1637
+ catch (err) {
1638
+ logError(err);
1653
1639
  }
1654
- });
1655
- const timeout = resourceTarget._timeout;
1656
- if (timeout > 0) {
1657
- return Promise.race([
1658
- promise,
1659
- delay(timeout).then(() => {
1660
- if (setState(false, new Error('timeout'))) {
1661
- cleanupTask(task);
1662
- }
1663
- }),
1664
- ]);
1665
1640
  }
1666
- return promise;
1667
1641
  };
1668
- const ignoreErrorToPreventNodeFromCrashing = (err) => {
1669
- // ignore error to prevent node from crashing
1670
- // node will crash in promise is rejected and no one is listening to the rejection.
1642
+ class Task extends BackRef {
1643
+ $flags$;
1644
+ $index$;
1645
+ $el$;
1646
+ $qrl$;
1647
+ $state$;
1648
+ $destroy$;
1649
+ constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
1650
+ super();
1651
+ this.$flags$ = $flags$;
1652
+ this.$index$ = $index$;
1653
+ this.$el$ = $el$;
1654
+ this.$qrl$ = $qrl$;
1655
+ this.$state$ = $state$;
1656
+ this.$destroy$ = $destroy$;
1657
+ }
1658
+ }
1659
+ const isTask = (value) => {
1660
+ return value instanceof Task;
1661
+ };
1662
+ /**
1663
+ * Used internally as a qrl event handler to schedule a task.
1664
+ *
1665
+ * @internal
1666
+ */
1667
+ const scheduleTask = (_event, element) => {
1668
+ const [task] = useLexicalScope();
1669
+ const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK;
1670
+ const container = getDomContainer(element);
1671
+ container.$scheduler$(type, task);
1671
1672
  };
1672
1673
 
1673
1674
  /** @file Public types for the client deserialization */
@@ -1745,22 +1746,67 @@ var VirtualVNodeProps;
1745
1746
  VirtualVNodeProps[VirtualVNodeProps["PROPS_OFFSET"] = 6] = "PROPS_OFFSET";
1746
1747
  })(VirtualVNodeProps || (VirtualVNodeProps = {}));
1747
1748
 
1748
- const isForeignObjectElement = (elementName) => elementName.toLowerCase() === 'foreignobject';
1749
+ const mapApp_findIndx = (elementVNode, key, start) => {
1750
+ assertTrue(start % 2 === 0, 'Expecting even number.');
1751
+ let bottom = start >> 1;
1752
+ let top = (elementVNode.length - 2) >> 1;
1753
+ while (bottom <= top) {
1754
+ const mid = bottom + ((top - bottom) >> 1);
1755
+ const midKey = elementVNode[mid << 1];
1756
+ if (midKey === key) {
1757
+ return mid << 1;
1758
+ }
1759
+ if (midKey < key) {
1760
+ bottom = mid + 1;
1761
+ }
1762
+ else {
1763
+ top = mid - 1;
1764
+ }
1765
+ }
1766
+ return (bottom << 1) ^ -1;
1767
+ };
1768
+ const mapArray_set = (elementVNode, key, value, start) => {
1769
+ const indx = mapApp_findIndx(elementVNode, key, start);
1770
+ if (indx >= 0) {
1771
+ if (value == null) {
1772
+ elementVNode.splice(indx, 2);
1773
+ }
1774
+ else {
1775
+ elementVNode[indx + 1] = value;
1776
+ }
1777
+ }
1778
+ else if (value != null) {
1779
+ elementVNode.splice(indx ^ -1, 0, key, value);
1780
+ }
1781
+ };
1782
+ const mapArray_get = (elementVNode, key, start) => {
1783
+ const indx = mapApp_findIndx(elementVNode, key, start);
1784
+ if (indx >= 0) {
1785
+ return elementVNode[indx + 1];
1786
+ }
1787
+ else {
1788
+ return null;
1789
+ }
1790
+ };
1791
+
1792
+ const isForeignObjectElement = (elementName) => {
1793
+ return isDev ? elementName.toLowerCase() === 'foreignobject' : elementName === 'foreignObject';
1794
+ };
1749
1795
  const isSvgElement = (elementName) => elementName === 'svg' || isForeignObjectElement(elementName);
1750
1796
  const isMathElement = (elementName) => elementName === 'math';
1751
1797
  const vnode_isDefaultNamespace = (vnode) => {
1752
1798
  const flags = vnode[VNodeProps.flags];
1753
1799
  return (flags & VNodeFlags.NAMESPACE_MASK) === 0;
1754
1800
  };
1755
- const vnode_getElementNamespaceFlags = (elementName) => {
1756
- if (isSvgElement(elementName)) {
1757
- return VNodeFlags.NS_svg;
1758
- }
1759
- else if (isMathElement(elementName)) {
1760
- return VNodeFlags.NS_math;
1761
- }
1762
- else {
1763
- return VNodeFlags.NS_html;
1801
+ const vnode_getElementNamespaceFlags = (element) => {
1802
+ const namespace = fastNamespaceURI(element);
1803
+ switch (namespace) {
1804
+ case SVG_NS:
1805
+ return VNodeFlags.NS_svg;
1806
+ case MATH_NS:
1807
+ return VNodeFlags.NS_math;
1808
+ default:
1809
+ return VNodeFlags.NS_html;
1764
1810
  }
1765
1811
  };
1766
1812
  function vnode_getDomChildrenWithCorrectNamespacesToInsert(journal, domParentVNode, newChild) {
@@ -2114,19 +2160,21 @@ const useOnEventsSequentialScope = () => {
2114
2160
  * @returns
2115
2161
  */
2116
2162
  const executeComponent = (container, renderHost, subscriptionHost, componentQRL, props) => {
2117
- const iCtx = newInvokeContext(container.$locale$, subscriptionHost, undefined, RenderEvent);
2118
- iCtx.$effectSubscriber$ = [subscriptionHost, EffectProperty.COMPONENT];
2119
- iCtx.$container$ = container;
2163
+ const iCtx = newInvokeContext(container.$locale$, subscriptionHost || undefined, undefined, RenderEvent);
2164
+ if (subscriptionHost) {
2165
+ iCtx.$effectSubscriber$ = getSubscriber(subscriptionHost, EffectProperty.COMPONENT);
2166
+ iCtx.$container$ = container;
2167
+ }
2120
2168
  let componentFn;
2121
2169
  container.ensureProjectionResolved(renderHost);
2122
2170
  let isInlineComponent = false;
2123
2171
  if (componentQRL === null) {
2124
- componentQRL = componentQRL || container.getHostProp(renderHost, OnRenderProp);
2172
+ componentQRL = container.getHostProp(renderHost, OnRenderProp);
2125
2173
  assertDefined(componentQRL, 'No Component found at this location');
2126
2174
  }
2127
- if (isQrl(componentQRL)) {
2175
+ if (isQrl$1(componentQRL)) {
2128
2176
  props = props || container.getHostProp(renderHost, ELEMENT_PROPS) || EMPTY_OBJ;
2129
- if (props && props.children) {
2177
+ if (props.children) {
2130
2178
  delete props.children;
2131
2179
  }
2132
2180
  componentFn = componentQRL.getFn(iCtx);
@@ -2144,18 +2192,16 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2144
2192
  if (!isInlineComponent) {
2145
2193
  container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
2146
2194
  container.setHostProp(renderHost, USE_ON_LOCAL_SEQ_IDX, null);
2147
- if (container.getHostProp(renderHost, ELEMENT_PROPS) !== props) {
2148
- container.setHostProp(renderHost, ELEMENT_PROPS, props);
2149
- }
2195
+ container.setHostProp(renderHost, ELEMENT_PROPS, props);
2150
2196
  }
2151
2197
  if (vnode_isVNode(renderHost)) {
2152
- clearVNodeEffectDependencies(container, renderHost);
2198
+ clearAllEffects(container, renderHost);
2153
2199
  }
2154
2200
  return componentFn(props);
2155
2201
  }, (jsx) => {
2156
2202
  const useOnEvents = container.getHostProp(renderHost, USE_ON_LOCAL);
2157
2203
  if (useOnEvents) {
2158
- return maybeThen(addUseOnEvents(jsx, useOnEvents), () => jsx);
2204
+ return addUseOnEvents(jsx, useOnEvents);
2159
2205
  }
2160
2206
  return jsx;
2161
2207
  }, (err) => {
@@ -2181,6 +2227,7 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2181
2227
  */
2182
2228
  function addUseOnEvents(jsx, useOnEvents) {
2183
2229
  const jsxElement = findFirstStringJSX(jsx);
2230
+ let jsxResult = jsx;
2184
2231
  return maybeThen(jsxElement, (jsxElement) => {
2185
2232
  let isInvisibleComponent = false;
2186
2233
  if (!jsxElement) {
@@ -2199,13 +2246,15 @@ function addUseOnEvents(jsx, useOnEvents) {
2199
2246
  if (Object.prototype.hasOwnProperty.call(useOnEvents, key)) {
2200
2247
  if (isInvisibleComponent) {
2201
2248
  if (key === 'onQvisible$') {
2202
- jsxElement = addScriptNodeForInvisibleComponents(jsx);
2249
+ const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2250
+ jsxResult = jsx;
2203
2251
  if (jsxElement) {
2204
2252
  addUseOnEvent(jsxElement, 'document:onQinit$', useOnEvents[key]);
2205
2253
  }
2206
2254
  }
2207
2255
  else if (key.startsWith('document:') || key.startsWith('window:')) {
2208
- jsxElement = addScriptNodeForInvisibleComponents(jsx);
2256
+ const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2257
+ jsxResult = jsx;
2209
2258
  if (jsxElement) {
2210
2259
  addUseOnEvent(jsxElement, key, useOnEvents[key]);
2211
2260
  }
@@ -2223,7 +2272,7 @@ function addUseOnEvents(jsx, useOnEvents) {
2223
2272
  }
2224
2273
  }
2225
2274
  }
2226
- return jsxElement;
2275
+ return jsxResult;
2227
2276
  });
2228
2277
  }
2229
2278
  function addUseOnEvent(jsxElement, key, value) {
@@ -2269,6 +2318,9 @@ function addScriptNodeForInvisibleComponents(jsx) {
2269
2318
  type: 'placeholder',
2270
2319
  hidden: '',
2271
2320
  }, null, 3);
2321
+ if (jsx.type === Slot) {
2322
+ return [jsxElement, _jsxSorted(Fragment, null, null, [jsx, jsxElement], 0, null)];
2323
+ }
2272
2324
  if (jsx.children == null) {
2273
2325
  jsx.children = jsxElement;
2274
2326
  }
@@ -2278,15 +2330,23 @@ function addScriptNodeForInvisibleComponents(jsx) {
2278
2330
  else {
2279
2331
  jsx.children = [jsx.children, jsxElement];
2280
2332
  }
2281
- return jsxElement;
2333
+ return [jsxElement, jsx];
2282
2334
  }
2283
2335
  else if (Array.isArray(jsx) && jsx.length) {
2284
2336
  // get first element
2285
- return addScriptNodeForInvisibleComponents(jsx[0]);
2337
+ const [jsxElement, _] = addScriptNodeForInvisibleComponents(jsx[0]);
2338
+ return [jsxElement, jsx];
2286
2339
  }
2287
- return null;
2340
+ return [null, null];
2288
2341
  }
2289
2342
 
2343
+ /** @internal */
2344
+ const _CONST_PROPS = Symbol('CONST');
2345
+ /** @internal */
2346
+ const _VAR_PROPS = Symbol('VAR');
2347
+ /** @internal @deprecated v1 compat */
2348
+ const _IMMUTABLE = Symbol('IMMUTABLE');
2349
+
2290
2350
  function isSlotProp(prop) {
2291
2351
  return !prop.startsWith('q:') && !prop.startsWith(NON_SERIALIZABLE_MARKER_PREFIX);
2292
2352
  }
@@ -2295,12 +2355,24 @@ function isParentSlotProp(prop) {
2295
2355
  }
2296
2356
  /** @internal */
2297
2357
  const _restProps = (props, omit, target = {}) => {
2298
- for (const key in props) {
2358
+ let constPropsTarget = null;
2359
+ const constProps = props[_CONST_PROPS];
2360
+ if (constProps) {
2361
+ for (const key in constProps) {
2362
+ if (!omit.includes(key)) {
2363
+ constPropsTarget ||= {};
2364
+ constPropsTarget[key] = constProps[key];
2365
+ }
2366
+ }
2367
+ }
2368
+ const varPropsTarget = target;
2369
+ const varProps = props[_VAR_PROPS];
2370
+ for (const key in varProps) {
2299
2371
  if (!omit.includes(key)) {
2300
- target[key] = props[key];
2372
+ varPropsTarget[key] = varProps[key];
2301
2373
  }
2302
2374
  }
2303
- return target;
2375
+ return createPropsProxy(varPropsTarget, constPropsTarget);
2304
2376
  };
2305
2377
 
2306
2378
  function escapeHTML(html) {
@@ -2342,17 +2414,6 @@ function escapeHTML(html) {
2342
2414
  }
2343
2415
  }
2344
2416
 
2345
- function getFileLocationFromJsx(jsxDev) {
2346
- if (!jsxDev) {
2347
- return null;
2348
- }
2349
- const sanitizedFileName = jsxDev.fileName?.replace(/\\/g, '/');
2350
- if (sanitizedFileName) {
2351
- return `${sanitizedFileName}:${jsxDev.lineNumber}:${jsxDev.columnNumber}`;
2352
- }
2353
- return null;
2354
- }
2355
-
2356
2417
  const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2357
2418
  let journal = container.$journal$;
2358
2419
  /**
@@ -2371,9 +2432,9 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2371
2432
  /// (Node can be null, if we are at the end of the list.)
2372
2433
  let vCurrent = null;
2373
2434
  /// When we insert new node we start it here so that we can descend into it.
2374
- /// NOTE: it can't be stored in `vCurrent` because `vNewCurrent` is in journal
2435
+ /// NOTE: it can't be stored in `vCurrent` because `vNewNode` is in journal
2375
2436
  /// and is not connected to the tree.
2376
- let vNewNode = null; // TODO: delete, because journal is on vNode, the above comment no longer applies
2437
+ let vNewNode = null;
2377
2438
  /// When elements have keys they can be consumed out of order and therefore we can't use nextSibling.
2378
2439
  /// In such a case this array will contain the elements after the current location.
2379
2440
  /// The array even indices will contains keys and odd indices the vNode.
@@ -2426,7 +2487,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2426
2487
  }
2427
2488
  else if (isSignal(jsxValue)) {
2428
2489
  if (vCurrent) {
2429
- clearVNodeEffectDependencies(container, vCurrent);
2490
+ clearAllEffects(container, vCurrent);
2430
2491
  }
2431
2492
  expectVirtual(VirtualType.WrappedSignal, null);
2432
2493
  descend(trackSignalAndAssignHost(jsxValue, (vNewNode || vCurrent), EffectProperty.VNODE, container), true);
@@ -2641,9 +2702,10 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2641
2702
  };
2642
2703
  const projections = [];
2643
2704
  if (host) {
2705
+ const props = vnode_getProps(host);
2644
2706
  // we need to create empty projections for all the slots to remove unused slots content
2645
- for (let i = vnode_getPropStartIndex(host); i < host.length; i = i + 2) {
2646
- const prop = host[i];
2707
+ for (let i = 0; i < props.length; i = i + 2) {
2708
+ const prop = props[i];
2647
2709
  if (isSlotProp(prop)) {
2648
2710
  const slotName = prop;
2649
2711
  projections.push(slotName);
@@ -2811,10 +2873,19 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2811
2873
  // But we need to mark them so that they don't get pulled into the diff.
2812
2874
  const eventName = getEventNameFromJsxProp(key);
2813
2875
  const scope = getEventNameScopeFromJsxProp(key);
2814
- vnode_setProp(vNewNode, HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
2815
2876
  if (eventName) {
2877
+ vnode_setProp(vNewNode, HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
2816
2878
  registerQwikLoaderEvent(eventName);
2817
2879
  }
2880
+ if (scope) {
2881
+ // add an event attr with empty value for qwikloader element selector.
2882
+ // We don't need value here. For ssr this value is a QRL,
2883
+ // but for CSR value should be just empty
2884
+ const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
2885
+ if (htmlEvent) {
2886
+ vnode_setAttr(journal, vNewNode, htmlEvent, '');
2887
+ }
2888
+ }
2818
2889
  needsQDispatchEventPatch = true;
2819
2890
  continue;
2820
2891
  }
@@ -2827,12 +2898,15 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2827
2898
  value(element);
2828
2899
  continue;
2829
2900
  }
2901
+ else if (value == null) {
2902
+ continue;
2903
+ }
2830
2904
  else {
2831
2905
  throw qError(QError.invalidRefValue, [currentFile]);
2832
2906
  }
2833
2907
  }
2834
2908
  if (isSignal(value)) {
2835
- const signalData = new EffectPropData({
2909
+ const signalData = new SubscriptionData({
2836
2910
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
2837
2911
  $isConst$: true,
2838
2912
  });
@@ -2936,7 +3010,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2936
3010
  let returnValue = false;
2937
3011
  qrls.flat(2).forEach((qrl) => {
2938
3012
  if (qrl) {
2939
- const value = qrl(event, element);
3013
+ const value = container.$scheduler$(ChoreType.RUN_QRL, vNode, qrl, [event, element]);
2940
3014
  returnValue = returnValue || value === true;
2941
3015
  }
2942
3016
  });
@@ -2948,10 +3022,10 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2948
3022
  /** @param tag Returns true if `qDispatchEvent` needs patching */
2949
3023
  function setBulkProps(vnode, srcAttrs, currentFile) {
2950
3024
  vnode_ensureElementInflated(vnode);
2951
- const dstAttrs = vnode;
3025
+ const dstAttrs = vnode_getProps(vnode);
2952
3026
  let srcIdx = 0;
2953
3027
  const srcLength = srcAttrs.length;
2954
- let dstIdx = ElementVNodeProps.PROPS_OFFSET;
3028
+ let dstIdx = 0;
2955
3029
  let dstLength = dstAttrs.length;
2956
3030
  let srcKey = srcIdx < srcLength ? srcAttrs[srcIdx++] : null;
2957
3031
  let dstKey = dstIdx < dstLength ? dstAttrs[dstIdx++] : null;
@@ -2971,12 +3045,15 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2971
3045
  value(element);
2972
3046
  return;
2973
3047
  }
3048
+ else if (value == null) {
3049
+ return;
3050
+ }
2974
3051
  else {
2975
3052
  throw qError(QError.invalidRefValue, [currentFile]);
2976
3053
  }
2977
3054
  }
2978
3055
  if (isSignal(value)) {
2979
- const signalData = new EffectPropData({
3056
+ const signalData = new SubscriptionData({
2980
3057
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
2981
3058
  $isConst$: false,
2982
3059
  });
@@ -2990,21 +3067,21 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2990
3067
  };
2991
3068
  const recordJsxEvent = (key, value) => {
2992
3069
  const eventName = getEventNameFromJsxProp(key);
3070
+ const scope = getEventNameScopeFromJsxProp(key);
2993
3071
  if (eventName) {
2994
- const scope = getEventNameScopeFromJsxProp(key);
2995
3072
  record(':' + scope + ':' + eventName, value);
2996
- }
2997
- // add an event attr with empty value for qwikloader element selector.
2998
- // We don't need value here. For ssr this value is a QRL,
2999
- // but for CSR value should be just empty
3000
- const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
3001
- if (htmlEvent) {
3002
- record(htmlEvent, '');
3003
- }
3004
- // register an event for qwik loader
3005
- if (eventName) {
3073
+ // register an event for qwik loader
3006
3074
  registerQwikLoaderEvent(eventName);
3007
3075
  }
3076
+ if (scope) {
3077
+ // add an event attr with empty value for qwikloader element selector.
3078
+ // We don't need value here. For ssr this value is a QRL,
3079
+ // but for CSR value should be just empty
3080
+ const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
3081
+ if (htmlEvent) {
3082
+ record(htmlEvent, '');
3083
+ }
3084
+ }
3008
3085
  };
3009
3086
  while (srcKey !== null || dstKey !== null) {
3010
3087
  if (dstKey?.startsWith(HANDLER_PREFIX) || dstKey?.startsWith(Q_PREFIX)) {
@@ -3200,10 +3277,6 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
3200
3277
  else if (!hashesAreEqual) {
3201
3278
  insertNewComponent(host, componentQRL, jsxProps);
3202
3279
  if (vNewNode) {
3203
- if (host) {
3204
- // TODO(varixo): not sure why we need to copy flags here.
3205
- vNewNode[VNodeProps.flags] = host[VNodeProps.flags];
3206
- }
3207
3280
  host = vNewNode;
3208
3281
  shouldRender = true;
3209
3282
  }
@@ -3256,7 +3329,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
3256
3329
  }
3257
3330
  function insertNewComponent(host, componentQRL, jsxProps) {
3258
3331
  if (host) {
3259
- clearVNodeEffectDependencies(container, host);
3332
+ clearAllEffects(container, host);
3260
3333
  }
3261
3334
  vnode_insertBefore(journal, vParent, (vNewNode = vnode_newVirtual()), vCurrent && getInsertBefore());
3262
3335
  const jsxNode = jsxValue;
@@ -3344,8 +3417,8 @@ function propsDiffer(src, dst) {
3344
3417
  if (!src || !dst) {
3345
3418
  return true;
3346
3419
  }
3347
- let srcKeys = removePropsKeys(Object.keys(src), ['children', QSubscribers]);
3348
- let dstKeys = removePropsKeys(Object.keys(dst), ['children', QSubscribers]);
3420
+ let srcKeys = removePropsKeys(Object.keys(src), ['children', QBackRefs]);
3421
+ let dstKeys = removePropsKeys(Object.keys(dst), ['children', QBackRefs]);
3349
3422
  if (srcKeys.length !== dstKeys.length) {
3350
3423
  return true;
3351
3424
  }
@@ -3391,7 +3464,7 @@ function cleanup(container, vNode) {
3391
3464
  do {
3392
3465
  const type = vCursor[VNodeProps.flags];
3393
3466
  if (type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) {
3394
- clearVNodeEffectDependencies(container, vCursor);
3467
+ clearAllEffects(container, vCursor);
3395
3468
  markVNodeAsDeleted(vCursor);
3396
3469
  // Only elements and virtual nodes need to be traversed for children
3397
3470
  if (type & VNodeFlags.Virtual) {
@@ -3401,7 +3474,7 @@ function cleanup(container, vNode) {
3401
3474
  const obj = seq[i];
3402
3475
  if (isTask(obj)) {
3403
3476
  const task = obj;
3404
- clearSubscriberEffectDependencies(container, task);
3477
+ clearAllEffects(container, task);
3405
3478
  if (task.$flags$ & TaskFlags.VISIBLE_TASK) {
3406
3479
  container.$scheduler$(ChoreType.CLEANUP_VISIBLE, task);
3407
3480
  }
@@ -3416,8 +3489,8 @@ function cleanup(container, vNode) {
3416
3489
  vnode_getProp(vCursor, OnRenderProp, null) !== null;
3417
3490
  if (isComponent) {
3418
3491
  // SPECIAL CASE: If we are a component, we need to descend into the projected content and release the content.
3419
- const attrs = vCursor;
3420
- for (let i = VirtualVNodeProps.PROPS_OFFSET; i < attrs.length; i = i + 2) {
3492
+ const attrs = vnode_getProps(vCursor);
3493
+ for (let i = 0; i < attrs.length; i = i + 2) {
3421
3494
  const key = attrs[i];
3422
3495
  if (!isParentSlotProp(key) && isSlotProp(key)) {
3423
3496
  const value = attrs[i + 1];
@@ -3525,82 +3598,270 @@ var SiblingsArray;
3525
3598
  SiblingsArray[SiblingsArray["NextVNode"] = 5] = "NextVNode";
3526
3599
  })(SiblingsArray || (SiblingsArray = {}));
3527
3600
 
3528
- // <docs markdown="../../readme.md#implicit$FirstArg">
3601
+ /** @internal */
3602
+ const useResourceQrl = (qrl, opts) => {
3603
+ const { val, set, i, iCtx } = useSequentialScope();
3604
+ if (val != null) {
3605
+ return val;
3606
+ }
3607
+ assertQrl(qrl);
3608
+ const container = iCtx.$container$;
3609
+ const resource = createResourceReturn(container, opts);
3610
+ const el = iCtx.$hostElement$;
3611
+ const task = new Task(TaskFlags.DIRTY | TaskFlags.RESOURCE, i, el, qrl, resource, null);
3612
+ container.$scheduler$(ChoreType.TASK, task);
3613
+ set(resource);
3614
+ return resource;
3615
+ };
3616
+ // <docs markdown="../readme.md#useResource">
3529
3617
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
3530
- // (edit ../../readme.md#implicit$FirstArg instead and run `pnpm docs.sync`)
3618
+ // (edit ../readme.md#useResource instead and run `pnpm docs.sync`)
3531
3619
  /**
3532
- * Create a `____$(...)` convenience method from `___(...)`.
3620
+ * This method works like an async memoized function that runs whenever some tracked value changes
3621
+ * and returns some data.
3533
3622
  *
3534
- * It is very common for functions to take a lazy-loadable resource as a first argument. For this
3535
- * reason, the Qwik Optimizer automatically extracts the first argument from any function which ends
3536
- * in `$`.
3623
+ * `useResource` however returns immediate a `ResourceReturn` object that contains the data and a
3624
+ * state that indicates if the data is available or not.
3537
3625
  *
3538
- * This means that `foo$(arg0)` and `foo($(arg0))` are equivalent with respect to Qwik Optimizer.
3539
- * The former is just a shorthand for the latter.
3626
+ * The status can be one of the following:
3540
3627
  *
3541
- * For example, these function calls are equivalent:
3628
+ * - `pending` - the data is not yet available.
3629
+ * - `resolved` - the data is available.
3630
+ * - `rejected` - the data is not available due to an error or timeout.
3542
3631
  *
3543
- * - `component$(() => {...})` is same as `component($(() => {...}))`
3632
+ * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
3633
+ * re-throw it (or a new Error), the resource status will never be `rejected`.
3544
3634
  *
3545
- * ```tsx
3546
- * export function myApi(callback: QRL<() => void>): void {
3547
- * // ...
3548
- * }
3635
+ * ### Example
3549
3636
  *
3550
- * export const myApi$ = implicit$FirstArg(myApi);
3551
- * // type of myApi$: (callback: () => void): void
3637
+ * Example showing how `useResource` to perform a fetch to request the weather, whenever the input
3638
+ * city name changes.
3552
3639
  *
3553
- * // can be used as:
3554
- * myApi$(() => console.log('callback'));
3640
+ * ```tsx
3641
+ * const Cmp = component$(() => {
3642
+ * const cityS = useSignal('');
3555
3643
  *
3556
- * // will be transpiled to:
3557
- * // FILE: <current file>
3558
- * myApi(qrl('./chunk-abc.js', 'callback'));
3644
+ * const weatherResource = useResource$(async ({ track, cleanup }) => {
3645
+ * const cityName = track(cityS);
3646
+ * const abortController = new AbortController();
3647
+ * cleanup(() => abortController.abort('cleanup'));
3648
+ * const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
3649
+ * signal: abortController.signal,
3650
+ * });
3651
+ * const data = await res.json();
3652
+ * return data as { temp: number };
3653
+ * });
3559
3654
  *
3560
- * // FILE: chunk-abc.js
3561
- * export const callback = () => console.log('callback');
3655
+ * return (
3656
+ * <div>
3657
+ * <input name="city" bind:value={cityS} />
3658
+ * <Resource
3659
+ * value={weatherResource}
3660
+ * onResolved={(weather) => {
3661
+ * return <div>Temperature: {weather.temp}</div>;
3662
+ * }}
3663
+ * />
3664
+ * </div>
3665
+ * );
3666
+ * });
3562
3667
  * ```
3563
3668
  *
3564
- * @param fn - A function that should have its first argument automatically `$`.
3565
3669
  * @public
3670
+ * @see Resource
3671
+ * @see ResourceReturn
3566
3672
  */
3567
3673
  // </docs>
3568
- const implicit$FirstArg = (fn) => {
3569
- return function (first, ...rest) {
3570
- return fn.call(null, dollar(first), ...rest);
3674
+ const Resource = (props) => {
3675
+ // Resource path
3676
+ return _jsxSorted(Fragment, null, null, getResourceValueAsPromise(props), 0, null);
3677
+ };
3678
+ function getResourceValueAsPromise(props) {
3679
+ const resource = props.value;
3680
+ if (isResourceReturn(resource) && resource.value) {
3681
+ const isBrowser = !isServerPlatform();
3682
+ if (isBrowser) {
3683
+ // create a subscription for the resource._state changes
3684
+ const state = resource._state;
3685
+ if (state === 'pending' && props.onPending) {
3686
+ return Promise.resolve().then(useBindInvokeContext(props.onPending));
3687
+ }
3688
+ else if (state === 'rejected' && props.onRejected) {
3689
+ return Promise.resolve(resource._error).then(useBindInvokeContext(props.onRejected));
3690
+ }
3691
+ else {
3692
+ const resolvedValue = untrack(() => resource._resolved);
3693
+ if (resolvedValue !== undefined) {
3694
+ // resolved, pending without onPending prop or rejected without onRejected prop
3695
+ return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved));
3696
+ }
3697
+ }
3698
+ }
3699
+ return resource.value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3700
+ }
3701
+ else if (isPromise(resource)) {
3702
+ return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3703
+ }
3704
+ else if (isSignal(resource)) {
3705
+ return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3706
+ }
3707
+ else {
3708
+ return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3709
+ }
3710
+ }
3711
+ const _createResourceReturn = (opts) => {
3712
+ const resource = {
3713
+ __brand: 'resource',
3714
+ value: undefined,
3715
+ loading: isServerPlatform() ? false : true,
3716
+ _resolved: undefined,
3717
+ _error: undefined,
3718
+ _state: 'pending',
3719
+ _timeout: opts?.timeout ?? -1,
3720
+ _cache: 0,
3571
3721
  };
3722
+ return resource;
3572
3723
  };
3573
-
3574
- const createSignal$1 = (value) => {
3575
- return new Signal(null, value);
3724
+ const createResourceReturn = (container, opts, initialPromise) => {
3725
+ const result = _createResourceReturn(opts);
3726
+ result.value = initialPromise;
3727
+ return createStore(container, result, StoreFlags.RECURSIVE);
3576
3728
  };
3577
- const createComputedSignal = (qrl) => {
3578
- throwIfQRLNotResolved(qrl);
3579
- return new ComputedSignal(null, qrl);
3729
+ const isResourceReturn = (obj) => {
3730
+ return isObject(obj) && (getStoreTarget(obj) || obj).__brand === 'resource';
3731
+ };
3732
+ const runResource = (task, container, host) => {
3733
+ task.$flags$ &= ~TaskFlags.DIRTY;
3734
+ cleanupTask(task);
3735
+ const iCtx = newInvokeContext(container.$locale$, host, undefined, ResourceEvent);
3736
+ iCtx.$container$ = container;
3737
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
3738
+ const resource = task.$state$;
3739
+ assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
3740
+ const track = (obj, prop) => {
3741
+ const ctx = newInvokeContext();
3742
+ ctx.$effectSubscriber$ = getSubscriber(task, EffectProperty.COMPONENT);
3743
+ ctx.$container$ = container;
3744
+ return invoke(ctx, () => {
3745
+ if (isFunction(obj)) {
3746
+ return obj();
3747
+ }
3748
+ if (prop) {
3749
+ return obj[prop];
3750
+ }
3751
+ else if (isSignal(obj)) {
3752
+ return obj.value;
3753
+ }
3754
+ else {
3755
+ return obj;
3756
+ }
3757
+ });
3758
+ };
3759
+ const handleError = (reason) => container.handleError(reason, host);
3760
+ const cleanups = [];
3761
+ task.$destroy$ = noSerialize(() => {
3762
+ cleanups.forEach((fn) => {
3763
+ try {
3764
+ fn();
3765
+ }
3766
+ catch (err) {
3767
+ handleError(err);
3768
+ }
3769
+ });
3770
+ done = true;
3771
+ });
3772
+ const resourceTarget = unwrapStore(resource);
3773
+ const opts = {
3774
+ track,
3775
+ cleanup(fn) {
3776
+ if (typeof fn === 'function') {
3777
+ cleanups.push(fn);
3778
+ }
3779
+ },
3780
+ cache(policy) {
3781
+ let milliseconds = 0;
3782
+ if (policy === 'immutable') {
3783
+ milliseconds = Infinity;
3784
+ }
3785
+ else {
3786
+ milliseconds = policy;
3787
+ }
3788
+ resource._cache = milliseconds;
3789
+ },
3790
+ previous: resourceTarget._resolved,
3791
+ };
3792
+ let resolve;
3793
+ let reject;
3794
+ let done = false;
3795
+ const setState = (resolved, value) => {
3796
+ if (!done) {
3797
+ done = true;
3798
+ if (resolved) {
3799
+ done = true;
3800
+ resource.loading = false;
3801
+ resource._state = 'resolved';
3802
+ resource._resolved = value;
3803
+ resource._error = undefined;
3804
+ resolve(value);
3805
+ }
3806
+ else {
3807
+ done = true;
3808
+ resource.loading = false;
3809
+ resource._state = 'rejected';
3810
+ resource._error = value;
3811
+ reject(value);
3812
+ }
3813
+ return true;
3814
+ }
3815
+ return false;
3816
+ };
3817
+ /**
3818
+ * Add cleanup to resolve the resource if we are trying to run the same resource again while the
3819
+ * previous one is not resolved yet. The next `runResource` run will call this cleanup
3820
+ */
3821
+ cleanups.push(() => {
3822
+ if (untrack(() => resource.loading) === true) {
3823
+ const value = untrack(() => resource._resolved);
3824
+ setState(true, value);
3825
+ }
3826
+ });
3827
+ // Execute mutation inside empty invocation
3828
+ invoke(iCtx, () => {
3829
+ // console.log('RESOURCE.pending: ');
3830
+ resource._state = 'pending';
3831
+ resource.loading = !isServerPlatform();
3832
+ const promise = (resource.value = new Promise((r, re) => {
3833
+ resolve = r;
3834
+ reject = re;
3835
+ }));
3836
+ promise.catch(ignoreErrorToPreventNodeFromCrashing);
3837
+ });
3838
+ const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
3839
+ setState(true, value);
3840
+ }, (err) => {
3841
+ if (isPromise(err)) {
3842
+ return err.then(() => runResource(task, container, host));
3843
+ }
3844
+ else {
3845
+ setState(false, err);
3846
+ }
3847
+ });
3848
+ const timeout = resourceTarget._timeout;
3849
+ if (timeout > 0) {
3850
+ return Promise.race([
3851
+ promise,
3852
+ delay(timeout).then(() => {
3853
+ if (setState(false, new Error('timeout'))) {
3854
+ cleanupTask(task);
3855
+ }
3856
+ }),
3857
+ ]);
3858
+ }
3859
+ return promise;
3860
+ };
3861
+ const ignoreErrorToPreventNodeFromCrashing = (err) => {
3862
+ // ignore error to prevent node from crashing
3863
+ // node will crash in promise is rejected and no one is listening to the rejection.
3580
3864
  };
3581
-
3582
- /**
3583
- * Creates a Signal with the given value. If no value is given, the signal is created with
3584
- * `undefined`.
3585
- *
3586
- * @public
3587
- */
3588
- const createSignal = createSignal$1;
3589
- /** @internal */
3590
- const createComputedQrl = createComputedSignal;
3591
- /**
3592
- * Create a computed signal which is calculated from the given QRL. A computed signal is a signal
3593
- * which is calculated from other signals. When the signals change, the computed signal is
3594
- * recalculated.
3595
- *
3596
- * The QRL must be a function which returns the value of the signal. The function must not have side
3597
- * effects, and it mus be synchronous.
3598
- *
3599
- * If you need the function to be async, use `useSignal` and `useTask$` instead.
3600
- *
3601
- * @public
3602
- */
3603
- const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedQrl);
3604
3865
 
3605
3866
  /// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
3606
3867
  const aVNodePath = [];
@@ -3785,38 +4046,29 @@ const ssrNodeDocumentPosition = (a, b) => {
3785
4046
  * - Visible Tasks are sorted afterJournalFlush, than depth first on component and finally in
3786
4047
  * declaration order within component.
3787
4048
  */
3788
- var ChoreType;
3789
- (function (ChoreType) {
3790
- /// MASKS defining three levels of sorting
3791
- ChoreType[ChoreType["MACRO"] = 240] = "MACRO";
3792
- /* order of elements (not encoded here) */
3793
- ChoreType[ChoreType["MICRO"] = 15] = "MICRO";
3794
- /** Ensure tha the QRL promise is resolved before processing next chores in the queue */
3795
- ChoreType[ChoreType["QRL_RESOLVE"] = 1] = "QRL_RESOLVE";
3796
- ChoreType[ChoreType["RESOURCE"] = 2] = "RESOURCE";
3797
- ChoreType[ChoreType["TASK"] = 3] = "TASK";
3798
- ChoreType[ChoreType["NODE_DIFF"] = 4] = "NODE_DIFF";
3799
- ChoreType[ChoreType["NODE_PROP"] = 5] = "NODE_PROP";
3800
- ChoreType[ChoreType["COMPONENT_SSR"] = 6] = "COMPONENT_SSR";
3801
- ChoreType[ChoreType["COMPONENT"] = 7] = "COMPONENT";
3802
- ChoreType[ChoreType["RECOMPUTE_AND_SCHEDULE_EFFECTS"] = 8] = "RECOMPUTE_AND_SCHEDULE_EFFECTS";
3803
- ChoreType[ChoreType["JOURNAL_FLUSH"] = 16] = "JOURNAL_FLUSH";
3804
- ChoreType[ChoreType["VISIBLE"] = 32] = "VISIBLE";
3805
- ChoreType[ChoreType["CLEANUP_VISIBLE"] = 48] = "CLEANUP_VISIBLE";
3806
- ChoreType[ChoreType["WAIT_FOR_ALL"] = 255] = "WAIT_FOR_ALL";
3807
- })(ChoreType || (ChoreType = {}));
4049
+ // Turn this on to get debug output of what the scheduler is doing.
4050
+ const DEBUG$1 = false;
4051
+ const getPromise = (chore) => (chore.$promise$ ||= new Promise((resolve) => {
4052
+ chore.$resolve$ = resolve;
4053
+ }));
3808
4054
  const createScheduler = (container, scheduleDrain, journalFlush) => {
3809
4055
  const choreQueue = [];
4056
+ const qrlRuns = [];
3810
4057
  let currentChore = null;
3811
- let journalFlushScheduled = false;
4058
+ let drainScheduled = false;
3812
4059
  return schedule;
3813
4060
  ///// IMPLEMENTATION /////
3814
4061
  function schedule(type, hostOrTask = null, targetOrQrl = null, payload = null) {
3815
- const runLater = type !== ChoreType.WAIT_FOR_ALL && type !== ChoreType.COMPONENT_SSR;
3816
- const isTask = type === ChoreType.TASK ||
3817
- type === ChoreType.VISIBLE ||
3818
- type === ChoreType.RESOURCE ||
3819
- type === ChoreType.CLEANUP_VISIBLE;
4062
+ const isServer = !isDomContainer(container);
4063
+ const isComponentSsr = isServer && type === ChoreType.COMPONENT;
4064
+ const runLater = type !== ChoreType.WAIT_FOR_ALL && !isComponentSsr && type !== ChoreType.RUN_QRL;
4065
+ const isTask = type === ChoreType.TASK || type === ChoreType.VISIBLE || type === ChoreType.CLEANUP_VISIBLE;
4066
+ const isClientOnly = type === ChoreType.JOURNAL_FLUSH ||
4067
+ type === ChoreType.NODE_DIFF ||
4068
+ type === ChoreType.NODE_PROP;
4069
+ if (isServer && isClientOnly) {
4070
+ return;
4071
+ }
3820
4072
  if (isTask) {
3821
4073
  hostOrTask.$flags$ |= TaskFlags.DIRTY;
3822
4074
  }
@@ -3835,192 +4087,234 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
3835
4087
  $returnValue$: null,
3836
4088
  $executed$: false,
3837
4089
  };
3838
- chore.$promise$ = new Promise((resolve) => (chore.$resolve$ = resolve));
3839
4090
  chore = sortedInsert(choreQueue, chore, container.rootVNode || null);
3840
- if (!journalFlushScheduled && runLater) {
4091
+ if (!drainScheduled && runLater) {
3841
4092
  // If we are not currently draining, we need to schedule a drain.
3842
- journalFlushScheduled = true;
4093
+ drainScheduled = true;
3843
4094
  schedule(ChoreType.JOURNAL_FLUSH);
3844
- scheduleDrain();
4095
+ // Catch here to avoid unhandled promise rejection
4096
+ scheduleDrain()?.catch?.(() => { });
3845
4097
  }
4098
+ // TODO figure out what to do with chore errors
3846
4099
  if (runLater) {
3847
- return chore.$promise$;
4100
+ return getPromise(chore);
3848
4101
  }
3849
4102
  else {
3850
- return drainUpTo(chore, container.rootVNode || null);
4103
+ return drainUpTo(chore, isServer);
3851
4104
  }
3852
4105
  }
3853
- /**
3854
- * Execute all of the chores up to and including the given chore.
3855
- *
3856
- * @param runUptoChore
3857
- */
3858
- function drainUpTo(runUptoChore, rootVNode) {
3859
- // If it already ran, it's not in the queue
3860
- if (runUptoChore.$executed$) {
3861
- return runUptoChore.$returnValue$;
3862
- }
3863
- if (currentChore) {
3864
- // Already running chore, which means we're waiting for async completion
3865
- return runUptoChore.$promise$;
3866
- }
4106
+ /** Execute all of the chores up to and including the given chore. */
4107
+ function drainUpTo(runUptoChore, isServer) {
4108
+ let maxRetries = 5000;
3867
4109
  while (choreQueue.length) {
3868
- const nextChore = choreQueue.shift();
3869
- const order = choreComparator(nextChore, runUptoChore, rootVNode);
3870
- if (order === null) {
3871
- continue;
4110
+ if (maxRetries-- < 0) {
4111
+ throw new Error('drainUpTo: max retries reached');
3872
4112
  }
3873
- if (order > 0) {
3874
- // we have processed all of the chores up to and including the given chore.
3875
- break;
4113
+ if (currentChore) {
4114
+ // Already running chore, which means we're waiting for async completion
4115
+ return getPromise(currentChore)
4116
+ .then(() => drainUpTo(runUptoChore, isServer))
4117
+ .catch((e) => {
4118
+ container.handleError(e, currentChore?.$host$);
4119
+ });
4120
+ }
4121
+ const nextChore = choreQueue[0];
4122
+ if (nextChore.$executed$) {
4123
+ choreQueue.shift();
4124
+ if (nextChore === runUptoChore) {
4125
+ break;
4126
+ }
4127
+ continue;
3876
4128
  }
3877
- const isDeletedVNode = vNodeAlreadyDeleted(nextChore);
3878
- if (isDeletedVNode &&
4129
+ if (vNodeAlreadyDeleted(nextChore) &&
3879
4130
  // we need to process cleanup tasks for deleted nodes
3880
4131
  nextChore.$type$ !== ChoreType.CLEANUP_VISIBLE) {
4132
+ choreQueue.shift();
3881
4133
  continue;
3882
4134
  }
3883
- const returnValue = executeChore(nextChore);
3884
- if (isPromise(returnValue)) {
3885
- const promise = returnValue.then(() => drainUpTo(runUptoChore, rootVNode));
3886
- return promise;
3887
- }
4135
+ executeChore(nextChore, isServer);
3888
4136
  }
3889
4137
  return runUptoChore.$returnValue$;
3890
4138
  }
3891
- function executeChore(chore) {
4139
+ function executeChore(chore, isServer) {
3892
4140
  const host = chore.$host$;
3893
4141
  assertEqual(currentChore, null, 'Chore already running.');
3894
4142
  currentChore = chore;
3895
4143
  let returnValue = null;
3896
- switch (chore.$type$) {
3897
- case ChoreType.JOURNAL_FLUSH:
3898
- returnValue = journalFlush();
3899
- journalFlushScheduled = false;
3900
- break;
3901
- case ChoreType.COMPONENT:
3902
- case ChoreType.COMPONENT_SSR:
3903
- returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
3904
- if (chore.$type$ === ChoreType.COMPONENT) {
3905
- const styleScopedId = container.getHostProp(host, QScopedStyle);
3906
- return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
4144
+ try {
4145
+ switch (chore.$type$) {
4146
+ case ChoreType.WAIT_FOR_ALL:
4147
+ {
4148
+ if (isServer) {
4149
+ drainScheduled = false;
4150
+ }
3907
4151
  }
3908
- else {
3909
- return jsx;
4152
+ break;
4153
+ case ChoreType.JOURNAL_FLUSH:
4154
+ {
4155
+ returnValue = journalFlush();
4156
+ drainScheduled = false;
4157
+ }
4158
+ break;
4159
+ case ChoreType.COMPONENT:
4160
+ {
4161
+ returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
4162
+ if (isServer) {
4163
+ return jsx;
4164
+ }
4165
+ else {
4166
+ const styleScopedId = container.getHostProp(host, QScopedStyle);
4167
+ return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
4168
+ }
4169
+ }, (err) => container.handleError(err, host));
4170
+ }
4171
+ break;
4172
+ case ChoreType.RUN_QRL:
4173
+ {
4174
+ const fn = chore.$target$.getFn();
4175
+ const result = retryOnPromise(() => fn(...chore.$payload$));
4176
+ if (isPromise(result)) {
4177
+ const handled = result
4178
+ .finally(() => {
4179
+ qrlRuns.splice(qrlRuns.indexOf(handled), 1);
4180
+ })
4181
+ .catch((error) => {
4182
+ container.handleError(error, chore.$host$);
4183
+ });
4184
+ // Don't wait for the promise to resolve
4185
+ // TODO come up with a better solution, we also want concurrent signal handling with tasks but serial tasks
4186
+ qrlRuns.push(handled);
4187
+ DEBUG$1 &&
4188
+ debugTrace('execute.DONE (but still running)', chore, currentChore, choreQueue);
4189
+ chore.$returnValue$ = handled;
4190
+ chore.$resolve$?.(handled);
4191
+ currentChore = null;
4192
+ chore.$executed$ = true;
4193
+ // early out so we don't call after()
4194
+ return;
4195
+ }
4196
+ returnValue = null;
4197
+ }
4198
+ break;
4199
+ case ChoreType.TASK:
4200
+ case ChoreType.VISIBLE:
4201
+ {
4202
+ const payload = chore.$payload$;
4203
+ if (payload.$flags$ & TaskFlags.RESOURCE) {
4204
+ const result = runResource(payload, container, host);
4205
+ // Don't await the return value of the resource, because async resources should not be awaited.
4206
+ // The reason for this is that we should be able to update for example a node with loading
4207
+ // text. If we await the resource, the loading text will not be displayed until the resource
4208
+ // is loaded.
4209
+ // Awaiting on the client also causes a deadlock.
4210
+ // In any case, the resource will never throw.
4211
+ returnValue = isServer ? result : null;
4212
+ }
4213
+ else {
4214
+ returnValue = runTask(payload, container, host);
4215
+ }
4216
+ }
4217
+ break;
4218
+ case ChoreType.CLEANUP_VISIBLE:
4219
+ {
4220
+ const task = chore.$payload$;
4221
+ cleanupTask(task);
4222
+ }
4223
+ break;
4224
+ case ChoreType.NODE_DIFF:
4225
+ {
4226
+ const parentVirtualNode = chore.$target$;
4227
+ let jsx = chore.$payload$;
4228
+ if (isSignal(jsx)) {
4229
+ jsx = jsx.value;
4230
+ }
4231
+ returnValue = retryOnPromise(() => vnode_diff(container, jsx, parentVirtualNode, null));
4232
+ }
4233
+ break;
4234
+ case ChoreType.NODE_PROP:
4235
+ {
4236
+ const virtualNode = chore.$host$;
4237
+ const payload = chore.$payload$;
4238
+ let value = payload.$value$;
4239
+ if (isSignal(value)) {
4240
+ value = value.value;
4241
+ }
4242
+ const isConst = payload.$isConst$;
4243
+ const journal = container.$journal$;
4244
+ const property = chore.$idx$;
4245
+ const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
4246
+ if (isConst) {
4247
+ const element = virtualNode[ElementVNodeProps.element];
4248
+ journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue);
4249
+ }
4250
+ else {
4251
+ vnode_setAttr(journal, virtualNode, property, serializedValue);
4252
+ }
4253
+ }
4254
+ break;
4255
+ case ChoreType.QRL_RESOLVE: {
4256
+ {
4257
+ const target = chore.$target$;
4258
+ returnValue = !target.resolved ? target.resolve() : null;
3910
4259
  }
3911
- }, (err) => container.handleError(err, host));
3912
- break;
3913
- case ChoreType.RESOURCE:
3914
- // Don't await the return value of the resource, because async resources should not be awaited.
3915
- // The reason for this is that we should be able to update for example a node with loading
3916
- // text. If we await the resource, the loading text will not be displayed until the resource
3917
- // is loaded.
3918
- const result = runResource(chore.$payload$, container, host);
3919
- returnValue = isDomContainer(container) ? null : result;
3920
- break;
3921
- case ChoreType.TASK:
3922
- returnValue = runTask(chore.$payload$, container, host);
3923
- break;
3924
- case ChoreType.VISIBLE:
3925
- returnValue = runTask(chore.$payload$, container, host);
3926
- break;
3927
- case ChoreType.CLEANUP_VISIBLE:
3928
- const task = chore.$payload$;
3929
- cleanupTask(task);
3930
- break;
3931
- case ChoreType.NODE_DIFF:
3932
- const parentVirtualNode = chore.$target$;
3933
- let jsx = chore.$payload$;
3934
- if (isSignal(jsx)) {
3935
- jsx = jsx.value;
3936
- }
3937
- returnValue = retryOnPromise(() => vnode_diff(container, jsx, parentVirtualNode, null));
3938
- break;
3939
- case ChoreType.NODE_PROP:
3940
- const virtualNode = chore.$host$;
3941
- const payload = chore.$payload$;
3942
- let value = payload.$value$;
3943
- if (isSignal(value)) {
3944
- value = value.value;
3945
- }
3946
- const isConst = payload.$isConst$;
3947
- const journal = container.$journal$;
3948
- const property = chore.$idx$;
3949
- const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
3950
- if (isConst) {
3951
- const element = virtualNode[ElementVNodeProps.element];
3952
- journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue);
3953
- }
3954
- else {
3955
- vnode_setAttr(journal, virtualNode, property, serializedValue);
3956
- }
3957
- break;
3958
- case ChoreType.QRL_RESOLVE: {
3959
- const target = chore.$target$;
3960
- returnValue = !target.resolved ? target.resolve() : null;
3961
- break;
3962
- }
3963
- case ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS: {
3964
- const target = chore.$target$;
3965
- const forceRunEffects = target.$forceRunEffects$;
3966
- target.$forceRunEffects$ = false;
3967
- if (!target.$effects$?.length) {
3968
4260
  break;
3969
4261
  }
3970
- returnValue = retryOnPromise(() => {
3971
- if (target.$computeIfNeeded$() || forceRunEffects) {
3972
- triggerEffects(container, target, target.$effects$);
4262
+ case ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS: {
4263
+ {
4264
+ const target = chore.$target$;
4265
+ const forceRunEffects = target.$forceRunEffects$;
4266
+ target.$forceRunEffects$ = false;
4267
+ if (!target.$effects$?.size) {
4268
+ break;
4269
+ }
4270
+ returnValue = retryOnPromise(() => {
4271
+ if (target.$computeIfNeeded$() || forceRunEffects) {
4272
+ triggerEffects(container, target, target.$effects$);
4273
+ }
4274
+ });
3973
4275
  }
3974
- });
3975
- break;
4276
+ break;
4277
+ }
3976
4278
  }
3977
4279
  }
3978
- return maybeThenPassError(returnValue, (value) => {
3979
- if (currentChore) {
3980
- currentChore.$executed$ = true;
3981
- currentChore.$resolve$?.(value);
3982
- }
4280
+ catch (e) {
4281
+ returnValue = Promise.reject(e);
4282
+ }
4283
+ const after = (value, error) => {
3983
4284
  currentChore = null;
3984
- return (chore.$returnValue$ = value);
3985
- });
3986
- }
3987
- };
3988
- const toNumber = (value) => {
3989
- return typeof value === 'number' ? value : -1;
3990
- };
3991
- /**
3992
- * When a derived signal is update we need to run vnode_diff. However the signal can update multiple
3993
- * times during component execution. For this reason it is necessary for us to update the schedule
3994
- * work with the latest result of the signal.
3995
- */
3996
- const choreUpdate = (existing, newChore) => {
3997
- if (existing.$type$ === ChoreType.NODE_DIFF) {
3998
- existing.$payload$ = newChore.$payload$;
3999
- }
4000
- };
4001
- function vNodeAlreadyDeleted(chore) {
4002
- return !!(chore.$host$ &&
4003
- vnode_isVNode(chore.$host$) &&
4004
- chore.$host$[VNodeProps.flags] & VNodeFlags.Deleted);
4005
- }
4006
- /**
4007
- * Compares two chores to determine their execution order in the scheduler's queue.
4008
- *
4009
- * @param a - The first chore to compare
4010
- * @param b - The second chore to compare
4011
- * @returns A number indicating the relative order of the chores. A negative number means `a` runs
4012
- * before `b`.
4013
- */
4014
- function choreComparator(a, b, rootVNode) {
4015
- const macroTypeDiff = (a.$type$ & ChoreType.MACRO) - (b.$type$ & ChoreType.MACRO);
4016
- if (macroTypeDiff !== 0) {
4017
- return macroTypeDiff;
4285
+ chore.$executed$ = true;
4286
+ if (error) {
4287
+ container.handleError(error, host);
4288
+ }
4289
+ else {
4290
+ chore.$returnValue$ = value;
4291
+ chore.$resolve$?.(value);
4292
+ }
4293
+ };
4294
+ if (isPromise(returnValue)) {
4295
+ chore.$promise$ = returnValue.then(after, (error) => after(undefined, error));
4296
+ chore.$resolve$?.(chore.$promise$);
4297
+ chore.$resolve$ = undefined;
4298
+ }
4299
+ else {
4300
+ after(returnValue);
4301
+ }
4018
4302
  }
4019
- // JOURNAL_FLUSH does not have a host or $idx$, so we can't compare it.
4020
- if (a.$type$ !== ChoreType.JOURNAL_FLUSH) {
4303
+ /**
4304
+ * Compares two chores to determine their execution order in the scheduler's queue.
4305
+ *
4306
+ * @param a - The first chore to compare
4307
+ * @param b - The second chore to compare
4308
+ * @returns A number indicating the relative order of the chores. A negative number means `a` runs
4309
+ * before `b`.
4310
+ */
4311
+ function choreComparator(a, b, rootVNode) {
4312
+ const macroTypeDiff = (a.$type$ & ChoreType.MACRO) - (b.$type$ & ChoreType.MACRO);
4313
+ if (macroTypeDiff !== 0) {
4314
+ return macroTypeDiff;
4315
+ }
4021
4316
  const aHost = a.$host$;
4022
4317
  const bHost = b.$host$;
4023
- // QRL_RESOLVE does not have a host.
4024
4318
  if (aHost !== bHost && aHost !== null && bHost !== null) {
4025
4319
  if (vnode_isVNode(aHost) && vnode_isVNode(bHost)) {
4026
4320
  // we are running on the client.
@@ -4030,6 +4324,8 @@ function choreComparator(a, b, rootVNode) {
4030
4324
  }
4031
4325
  }
4032
4326
  else {
4327
+ assertFalse(vnode_isVNode(aHost), 'expected aHost to be SSRNode but it is a VNode');
4328
+ assertFalse(vnode_isVNode(bHost), 'expected bHost to be SSRNode but it is a VNode');
4033
4329
  // we are running on the server.
4034
4330
  // On server we can't schedule task for a different host!
4035
4331
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -4049,247 +4345,114 @@ function choreComparator(a, b, rootVNode) {
4049
4345
  if (microTypeDiff !== 0) {
4050
4346
  return microTypeDiff;
4051
4347
  }
4348
+ // types are the same
4052
4349
  const idxDiff = toNumber(a.$idx$) - toNumber(b.$idx$);
4053
- if (idxDiff !== 0) {
4054
- return idxDiff;
4055
- }
4056
- // If the host is the same, we need to compare the target.
4057
- if (a.$target$ !== b.$target$ &&
4058
- ((a.$type$ === ChoreType.QRL_RESOLVE && b.$type$ === ChoreType.QRL_RESOLVE) ||
4059
- (a.$type$ === ChoreType.NODE_PROP && b.$type$ === ChoreType.NODE_PROP) ||
4060
- (a.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS &&
4061
- b.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS))) {
4062
- // 1 means that we are going to process chores as FIFO
4063
- return 1;
4064
- }
4065
- }
4066
- return 0;
4067
- }
4068
- function sortedFindIndex(sortedArray, value, rootVNode) {
4069
- /// We need to ensure that the `queue` is sorted by priority.
4070
- /// 1. Find a place where to insert into.
4071
- let bottom = 0;
4072
- let top = sortedArray.length;
4073
- while (bottom < top) {
4074
- const middle = bottom + ((top - bottom) >> 1);
4075
- const midChore = sortedArray[middle];
4076
- const comp = choreComparator(value, midChore, rootVNode);
4077
- if (comp < 0) {
4078
- top = middle;
4079
- }
4080
- else if (comp > 0) {
4081
- bottom = middle + 1;
4082
- }
4083
- else {
4084
- // We already have the host in the queue.
4085
- return middle;
4086
- }
4087
- }
4088
- return ~bottom;
4089
- }
4090
- function sortedInsert(sortedArray, value, rootVNode) {
4091
- /// We need to ensure that the `queue` is sorted by priority.
4092
- /// 1. Find a place where to insert into.
4093
- const idx = sortedFindIndex(sortedArray, value, rootVNode);
4094
- if (idx < 0) {
4095
- /// 2. Insert the chore into the queue.
4096
- sortedArray.splice(~idx, 0, value);
4097
- return value;
4098
- }
4099
- const existing = sortedArray[idx];
4100
- choreUpdate(existing, value);
4101
- return existing;
4102
- }
4103
-
4104
- // <docs markdown="../readme.md#useLexicalScope">
4105
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
4106
- // (edit ../readme.md#useLexicalScope instead and run `pnpm docs.sync`)
4107
- /**
4108
- * Used by the Qwik Optimizer to restore the lexically scoped variables.
4109
- *
4110
- * This method should not be present in the application source code.
4111
- *
4112
- * NOTE: `useLexicalScope` method can only be used in the synchronous portion of the callback
4113
- * (before any `await` statements.)
4114
- *
4115
- * @internal
4116
- */
4117
- // </docs>
4118
- const useLexicalScope = () => {
4119
- const context = getInvokeContext();
4120
- let qrl = context.$qrl$;
4121
- if (!qrl) {
4122
- const el = context.$element$;
4123
- assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
4124
- const containerElement = _getQContainerElement(el);
4125
- assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
4126
- const container = getDomContainer(containerElement);
4127
- qrl = container.parseQRL(decodeURIComponent(String(context.$url$)));
4128
- }
4129
- else {
4130
- assertQrl(qrl);
4131
- assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
4132
- }
4133
- return qrl.$captureRef$;
4134
- };
4135
-
4136
- var TaskFlags;
4137
- (function (TaskFlags) {
4138
- TaskFlags[TaskFlags["VISIBLE_TASK"] = 1] = "VISIBLE_TASK";
4139
- TaskFlags[TaskFlags["TASK"] = 2] = "TASK";
4140
- TaskFlags[TaskFlags["RESOURCE"] = 4] = "RESOURCE";
4141
- TaskFlags[TaskFlags["DIRTY"] = 8] = "DIRTY";
4142
- })(TaskFlags || (TaskFlags = {}));
4143
- /** @internal */
4144
- const useTaskQrl = (qrl, opts) => {
4145
- const { val, set, iCtx, i } = useSequentialScope();
4146
- if (val) {
4147
- return;
4148
- }
4149
- assertQrl(qrl);
4150
- set(1);
4151
- const host = iCtx.$hostElement$;
4152
- const task = new Task(TaskFlags.DIRTY | TaskFlags.TASK, i, iCtx.$hostElement$, qrl, undefined, null);
4153
- // In V2 we add the task to the sequential scope. We need to do this
4154
- // in order to be able to retrieve it later when the parent element is
4155
- // deleted and we need to be able to release the task subscriptions.
4156
- set(task);
4157
- const result = runTask(task, iCtx.$container$, host);
4158
- if (isPromise(result)) {
4159
- throw result;
4160
- }
4161
- qrl.$resolveLazy$(iCtx.$element$);
4162
- if (isServerPlatform()) {
4163
- useRunTask(task, opts?.eagerness);
4164
- }
4165
- };
4166
- const runTask = (task, container, host) => {
4167
- task.$flags$ &= ~TaskFlags.DIRTY;
4168
- cleanupTask(task);
4169
- const iCtx = newInvokeContext(container.$locale$, host, undefined, TaskEvent);
4170
- iCtx.$container$ = container;
4171
- const taskFn = task.$qrl$.getFn(iCtx, () => clearSubscriberEffectDependencies(container, task));
4172
- const track = (obj, prop) => {
4173
- const ctx = newInvokeContext();
4174
- ctx.$effectSubscriber$ = [task, EffectProperty.COMPONENT];
4175
- ctx.$container$ = container;
4176
- return invoke(ctx, () => {
4177
- if (isFunction(obj)) {
4178
- return obj();
4179
- }
4180
- if (prop) {
4181
- return obj[prop];
4182
- }
4183
- else if (isSignal(obj)) {
4184
- return obj.value;
4185
- }
4186
- else {
4187
- return obj;
4188
- }
4189
- });
4190
- };
4191
- const handleError = (reason) => container.handleError(reason, host);
4192
- let cleanupFns = null;
4193
- const cleanup = (fn) => {
4194
- if (typeof fn == 'function') {
4195
- if (!cleanupFns) {
4196
- cleanupFns = [];
4197
- task.$destroy$ = noSerialize(() => {
4198
- task.$destroy$ = null;
4199
- cleanupFns.forEach((fn) => {
4200
- try {
4201
- fn();
4202
- }
4203
- catch (err) {
4204
- handleError(err);
4205
- }
4206
- });
4207
- });
4208
- }
4209
- cleanupFns.push(fn);
4210
- }
4211
- };
4212
- const taskApi = { track, cleanup };
4213
- const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
4214
- if (isPromise(err)) {
4215
- return err.then(() => runTask(task, container, host));
4216
- }
4217
- else {
4218
- return handleError(err);
4350
+ if (idxDiff !== 0) {
4351
+ return idxDiff;
4219
4352
  }
4220
- });
4221
- return result;
4222
- };
4223
- const cleanupTask = (task) => {
4224
- const destroy = task.$destroy$;
4225
- if (destroy) {
4226
- task.$destroy$ = null;
4227
- try {
4228
- destroy();
4353
+ // If the host is the same (or missing), and the type is the same, we need to compare the target.
4354
+ if (a.$target$ !== b.$target$ || a.$payload$ !== b.$payload$) {
4355
+ // 1 means that we are going to process chores as FIFO
4356
+ return 1;
4229
4357
  }
4230
- catch (err) {
4231
- logError(err);
4358
+ // If the chore is the same as the current chore, we will run it again
4359
+ if (b === currentChore) {
4360
+ return 1;
4232
4361
  }
4362
+ // The chores are the same and will run only once
4363
+ return 0;
4233
4364
  }
4234
- };
4235
- const useRunTask = (task, eagerness) => {
4236
- if (eagerness === 'visible' || eagerness === 'intersection-observer') {
4237
- useOn('qvisible', getTaskHandlerQrl(task));
4238
- }
4239
- else if (eagerness === 'load' || eagerness === 'document-ready') {
4240
- useOnDocument('qinit', getTaskHandlerQrl(task));
4365
+ function sortedFindIndex(sortedArray, value, rootVNode) {
4366
+ /// We need to ensure that the `queue` is sorted by priority.
4367
+ /// 1. Find a place where to insert into.
4368
+ let bottom = 0;
4369
+ let top = sortedArray.length;
4370
+ while (bottom < top) {
4371
+ const middle = bottom + ((top - bottom) >> 1);
4372
+ const midChore = sortedArray[middle];
4373
+ const comp = choreComparator(value, midChore, rootVNode);
4374
+ if (comp < 0) {
4375
+ top = middle;
4376
+ }
4377
+ else if (comp > 0) {
4378
+ bottom = middle + 1;
4379
+ }
4380
+ else {
4381
+ // We already have the host in the queue.
4382
+ return middle;
4383
+ }
4384
+ }
4385
+ return ~bottom;
4241
4386
  }
4242
- else if (eagerness === 'idle' || eagerness === 'document-idle') {
4243
- useOnDocument('qidle', getTaskHandlerQrl(task));
4387
+ function sortedInsert(sortedArray, value, rootVNode) {
4388
+ /// We need to ensure that the `queue` is sorted by priority.
4389
+ /// 1. Find a place where to insert into.
4390
+ const idx = sortedFindIndex(sortedArray, value, rootVNode);
4391
+ if (idx < 0) {
4392
+ /// 2. Insert the chore into the queue.
4393
+ sortedArray.splice(~idx, 0, value);
4394
+ return value;
4395
+ }
4396
+ const existing = sortedArray[idx];
4397
+ /**
4398
+ * When a derived signal is updated we need to run vnode_diff. However the signal can update
4399
+ * multiple times during component execution. For this reason it is necessary for us to update
4400
+ * the chore with the latest result of the signal.
4401
+ */
4402
+ if (existing.$type$ === ChoreType.NODE_DIFF) {
4403
+ existing.$payload$ = value.$payload$;
4404
+ }
4405
+ if (existing.$executed$) {
4406
+ existing.$executed$ = false;
4407
+ }
4408
+ return existing;
4244
4409
  }
4245
4410
  };
4246
- const getTaskHandlerQrl = (task) => {
4247
- const taskQrl = task.$qrl$;
4248
- const taskHandler = createQRL(taskQrl.$chunk$, '_hW', _hW, null, null, [task], taskQrl.$symbol$);
4249
- // Needed for chunk lookup in dev mode
4250
- if (taskQrl.dev) {
4251
- taskHandler.dev = taskQrl.dev;
4252
- }
4253
- return taskHandler;
4411
+ const toNumber = (value) => {
4412
+ return typeof value === 'number' ? value : -1;
4254
4413
  };
4255
- class Task extends Subscriber {
4256
- $flags$;
4257
- $index$;
4258
- $el$;
4259
- $qrl$;
4260
- $state$;
4261
- $destroy$;
4262
- constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
4263
- super();
4264
- this.$flags$ = $flags$;
4265
- this.$index$ = $index$;
4266
- this.$el$ = $el$;
4267
- this.$qrl$ = $qrl$;
4268
- this.$state$ = $state$;
4269
- this.$destroy$ = $destroy$;
4414
+ function vNodeAlreadyDeleted(chore) {
4415
+ return !!(chore.$host$ &&
4416
+ vnode_isVNode(chore.$host$) &&
4417
+ chore.$host$[VNodeProps.flags] & VNodeFlags.Deleted);
4418
+ }
4419
+ function debugChoreTypeToString(type) {
4420
+ return ({
4421
+ [ChoreType.QRL_RESOLVE]: 'QRL_RESOLVE',
4422
+ [ChoreType.RUN_QRL]: 'RUN_QRL',
4423
+ [ChoreType.TASK]: 'TASK',
4424
+ [ChoreType.NODE_DIFF]: 'NODE_DIFF',
4425
+ [ChoreType.NODE_PROP]: 'NODE_PROP',
4426
+ [ChoreType.COMPONENT]: 'COMPONENT',
4427
+ [ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS]: 'RECOMPUTE_SIGNAL',
4428
+ [ChoreType.JOURNAL_FLUSH]: 'JOURNAL_FLUSH',
4429
+ [ChoreType.VISIBLE]: 'VISIBLE',
4430
+ [ChoreType.CLEANUP_VISIBLE]: 'CLEANUP_VISIBLE',
4431
+ [ChoreType.WAIT_FOR_ALL]: 'WAIT_FOR_ALL',
4432
+ }[type] || 'UNKNOWN: ' + type);
4433
+ }
4434
+ function debugChoreToString(chore) {
4435
+ const type = debugChoreTypeToString(chore.$type$);
4436
+ const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
4437
+ const qrlTarget = chore.$target$?.$symbol$;
4438
+ return `Chore(${type} ${chore.$type$ === ChoreType.QRL_RESOLVE || chore.$type$ === ChoreType.RUN_QRL ? qrlTarget : host} ${chore.$idx$})`;
4439
+ }
4440
+ function debugTrace(action, arg, currentChore, queue) {
4441
+ const lines = ['===========================\nScheduler: ' + action];
4442
+ if (arg && !('$type$' in arg)) {
4443
+ lines.push(' arg: ' + String(arg).replaceAll(/\n.*/gim, ''));
4444
+ }
4445
+ if (queue) {
4446
+ queue.forEach((chore) => {
4447
+ const active = chore === arg ? '>>>' : ' ';
4448
+ lines.push(` ${active} > ` +
4449
+ (chore === currentChore ? '[running] ' : '') +
4450
+ debugChoreToString(chore));
4451
+ });
4270
4452
  }
4453
+ // eslint-disable-next-line no-console
4454
+ console.log(lines.join('\n') + '\n');
4271
4455
  }
4272
- const isTask = (value) => {
4273
- return value instanceof Task;
4274
- };
4275
- /**
4276
- * Low-level API used by the Optimizer to process `useTask$()` API. This method is not intended to
4277
- * be used by developers.
4278
- *
4279
- * @internal
4280
- */
4281
- const _hW = () => {
4282
- const [task] = useLexicalScope();
4283
- const container = getDomContainer(task.$el$);
4284
- const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK;
4285
- container.$scheduler$(type, task);
4286
- };
4287
-
4288
- /**
4289
- * Special value used to mark that a given signal needs to be computed. This is essentially a
4290
- * "marked as dirty" flag.
4291
- */
4292
- const NEEDS_COMPUTATION = Symbol('invalid');
4293
4456
 
4294
4457
  /**
4295
4458
  * @file
@@ -4323,18 +4486,19 @@ const isSignal = (value) => {
4323
4486
  return value instanceof Signal;
4324
4487
  };
4325
4488
  /** @internal */
4326
- class EffectPropData {
4489
+ class SubscriptionData {
4327
4490
  data;
4328
4491
  constructor(data) {
4329
4492
  this.data = data;
4330
4493
  }
4331
4494
  }
4332
- var EffectSubscriptionsProp;
4333
- (function (EffectSubscriptionsProp) {
4334
- EffectSubscriptionsProp[EffectSubscriptionsProp["EFFECT"] = 0] = "EFFECT";
4335
- EffectSubscriptionsProp[EffectSubscriptionsProp["PROPERTY"] = 1] = "PROPERTY";
4336
- EffectSubscriptionsProp[EffectSubscriptionsProp["FIRST_BACK_REF_OR_DATA"] = 2] = "FIRST_BACK_REF_OR_DATA";
4337
- })(EffectSubscriptionsProp || (EffectSubscriptionsProp = {}));
4495
+ var EffectSubscriptionProp;
4496
+ (function (EffectSubscriptionProp) {
4497
+ EffectSubscriptionProp[EffectSubscriptionProp["CONSUMER"] = 0] = "CONSUMER";
4498
+ EffectSubscriptionProp[EffectSubscriptionProp["PROPERTY"] = 1] = "PROPERTY";
4499
+ EffectSubscriptionProp[EffectSubscriptionProp["BACK_REF"] = 2] = "BACK_REF";
4500
+ EffectSubscriptionProp[EffectSubscriptionProp["DATA"] = 3] = "DATA";
4501
+ })(EffectSubscriptionProp || (EffectSubscriptionProp = {}));
4338
4502
  var EffectProperty;
4339
4503
  (function (EffectProperty) {
4340
4504
  EffectProperty["COMPONENT"] = ":";
@@ -4371,19 +4535,16 @@ class Signal {
4371
4535
  }
4372
4536
  const effectSubscriber = ctx.$effectSubscriber$;
4373
4537
  if (effectSubscriber) {
4374
- const effects = (this.$effects$ ||= []);
4538
+ const effects = (this.$effects$ ||= new Set());
4375
4539
  // Let's make sure that we have a reference to this effect.
4376
4540
  // Adding reference is essentially adding a subscription, so if the signal
4377
4541
  // changes we know who to notify.
4378
- ensureContainsEffect(effects, effectSubscriber);
4542
+ ensureContainsSubscription(effects, effectSubscriber);
4379
4543
  // But when effect is scheduled in needs to be able to know which signals
4380
4544
  // to unsubscribe from. So we need to store the reference from the effect back
4381
4545
  // to this signal.
4382
- ensureContains(effectSubscriber, this);
4383
- if (isSubscriber(this)) {
4384
- // We need to add the subscriber to the effect so that we can clean it up later
4385
- ensureEffectContainsSubscriber(effectSubscriber[EffectSubscriptionsProp.EFFECT], this, this.$container$);
4386
- }
4546
+ ensureContainsBackRef(effectSubscriber, this);
4547
+ addQrlToSerializationCtx(effectSubscriber, this.$container$);
4387
4548
  }
4388
4549
  }
4389
4550
  return this.untrackedValue;
@@ -4402,122 +4563,97 @@ class Signal {
4402
4563
  }
4403
4564
  toString() {
4404
4565
  return (`[${this.constructor.name}${this.$invalid$ ? ' INVALID' : ''} ${String(this.$untrackedValue$)}]` +
4405
- (this.$effects$?.map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' ')).join('\n') || ''));
4566
+ (Array.from(this.$effects$ || [])
4567
+ .map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' '))
4568
+ .join('\n') || ''));
4406
4569
  }
4407
4570
  toJSON() {
4408
4571
  return { value: this.$untrackedValue$ };
4409
4572
  }
4410
4573
  }
4411
- /** Ensure the item is in array (do nothing if already there) */
4412
- const ensureContains = (array, value) => {
4413
- const isMissing = array.indexOf(value) === -1;
4414
- if (isMissing) {
4415
- array.push(value);
4416
- }
4417
- };
4418
- const ensureContainsEffect = (array, effectSubscriptions) => {
4419
- for (let i = 0; i < array.length; i++) {
4420
- const existingEffect = array[i];
4421
- if (existingEffect[0] === effectSubscriptions[0] &&
4422
- existingEffect[1] === effectSubscriptions[1]) {
4423
- return;
4424
- }
4425
- }
4426
- array.push(effectSubscriptions);
4574
+ const ensureContainsSubscription = (array, effectSubscription) => {
4575
+ array.add(effectSubscription);
4427
4576
  };
4428
- const ensureEffectContainsSubscriber = (effect, subscriber, container) => {
4429
- if (isSubscriber(effect)) {
4430
- effect.$effectDependencies$ ||= [];
4431
- if (subscriberExistInSubscribers(effect.$effectDependencies$, subscriber)) {
4432
- return;
4577
+ /** Ensure the item is in back refs set */
4578
+ const ensureContainsBackRef = (array, value) => {
4579
+ array[EffectSubscriptionProp.BACK_REF] ||= new Set();
4580
+ array[EffectSubscriptionProp.BACK_REF].add(value);
4581
+ };
4582
+ const addQrlToSerializationCtx = (effectSubscriber, container) => {
4583
+ if (!!container && !isDomContainer(container)) {
4584
+ const effect = effectSubscriber[EffectSubscriptionProp.CONSUMER];
4585
+ const property = effectSubscriber[EffectSubscriptionProp.PROPERTY];
4586
+ let qrl = null;
4587
+ if (isTask(effect)) {
4588
+ qrl = effect.$qrl$;
4433
4589
  }
4434
- effect.$effectDependencies$.push(subscriber);
4435
- }
4436
- else if (vnode_isVNode(effect) && !vnode_isTextVNode(effect)) {
4437
- let subscribers = vnode_getProp(effect, QSubscribers, container ? container.$getObjectById$ : null);
4438
- subscribers ||= [];
4439
- if (subscriberExistInSubscribers(subscribers, subscriber)) {
4440
- return;
4590
+ else if (effect instanceof ComputedSignal) {
4591
+ qrl = effect.$computeQrl$;
4441
4592
  }
4442
- subscribers.push(subscriber);
4443
- vnode_setProp(effect, QSubscribers, subscribers);
4444
- }
4445
- else if (isSSRNode(effect)) {
4446
- let subscribers = effect.getProp(QSubscribers);
4447
- subscribers ||= [];
4448
- if (subscriberExistInSubscribers(subscribers, subscriber)) {
4449
- return;
4593
+ else if (property === EffectProperty.COMPONENT) {
4594
+ qrl = container.getHostProp(effect, OnRenderProp);
4450
4595
  }
4451
- subscribers.push(subscriber);
4452
- effect.setProp(QSubscribers, subscribers);
4453
- }
4454
- };
4455
- const isSSRNode = (effect) => {
4456
- return 'setProp' in effect && 'getProp' in effect && 'removeProp' in effect && 'id' in effect;
4457
- };
4458
- const subscriberExistInSubscribers = (subscribers, subscriber) => {
4459
- for (let i = 0; i < subscribers.length; i++) {
4460
- if (subscribers[i] === subscriber) {
4461
- return true;
4596
+ if (qrl) {
4597
+ container.serializationCtx.$eventQrls$.add(qrl);
4462
4598
  }
4463
4599
  }
4464
- return false;
4465
4600
  };
4466
4601
  const triggerEffects = (container, signal, effects) => {
4602
+ const isBrowser = isDomContainer(container);
4467
4603
  if (effects) {
4468
- const scheduleEffect = (effectSubscriptions) => {
4469
- const effect = effectSubscriptions[EffectSubscriptionsProp.EFFECT];
4470
- const property = effectSubscriptions[EffectSubscriptionsProp.PROPERTY];
4604
+ const scheduleEffect = (effectSubscription) => {
4605
+ const consumer = effectSubscription[EffectSubscriptionProp.CONSUMER];
4606
+ const property = effectSubscription[EffectSubscriptionProp.PROPERTY];
4471
4607
  assertDefined(container, 'Container must be defined.');
4472
- if (isTask(effect)) {
4473
- effect.$flags$ |= TaskFlags.DIRTY;
4608
+ if (isTask(consumer)) {
4609
+ consumer.$flags$ |= TaskFlags.DIRTY;
4474
4610
  let choreType = ChoreType.TASK;
4475
- if (effect.$flags$ & TaskFlags.VISIBLE_TASK) {
4611
+ if (consumer.$flags$ & TaskFlags.VISIBLE_TASK) {
4476
4612
  choreType = ChoreType.VISIBLE;
4477
4613
  }
4478
- else if (effect.$flags$ & TaskFlags.RESOURCE) {
4479
- choreType = ChoreType.RESOURCE;
4480
- }
4481
- container.$scheduler$(choreType, effect);
4614
+ container.$scheduler$(choreType, consumer);
4482
4615
  }
4483
- else if (effect instanceof Signal) {
4616
+ else if (consumer instanceof Signal) {
4484
4617
  // we don't schedule ComputedSignal/DerivedSignal directly, instead we invalidate it and
4485
4618
  // and schedule the signals effects (recursively)
4486
- if (effect instanceof ComputedSignal) {
4619
+ if (consumer instanceof ComputedSignal) {
4487
4620
  // Ensure that the computed signal's QRL is resolved.
4488
4621
  // If not resolved schedule it to be resolved.
4489
- if (!effect.$computeQrl$.resolved) {
4490
- container.$scheduler$(ChoreType.QRL_RESOLVE, null, effect.$computeQrl$);
4622
+ if (!consumer.$computeQrl$.resolved) {
4623
+ container.$scheduler$(ChoreType.QRL_RESOLVE, null, consumer.$computeQrl$);
4491
4624
  }
4492
4625
  }
4493
- effect.$invalidate$();
4626
+ consumer.$invalidate$();
4494
4627
  }
4495
4628
  else if (property === EffectProperty.COMPONENT) {
4496
- const host = effect;
4629
+ const host = consumer;
4497
4630
  const qrl = container.getHostProp(host, OnRenderProp);
4498
4631
  assertDefined(qrl, 'Component must have QRL');
4499
4632
  const props = container.getHostProp(host, ELEMENT_PROPS);
4500
4633
  container.$scheduler$(ChoreType.COMPONENT, host, qrl, props);
4501
4634
  }
4502
- else if (property === EffectProperty.VNODE) {
4503
- const host = effect;
4504
- const target = host;
4505
- container.$scheduler$(ChoreType.NODE_DIFF, host, target, signal);
4506
- }
4507
- else {
4508
- const host = effect;
4509
- const effectData = effectSubscriptions[EffectSubscriptionsProp.FIRST_BACK_REF_OR_DATA];
4510
- if (effectData instanceof EffectPropData) {
4511
- const data = effectData.data;
4512
- const payload = {
4513
- ...data,
4514
- $value$: signal,
4515
- };
4516
- container.$scheduler$(ChoreType.NODE_PROP, host, property, payload);
4635
+ else if (isBrowser) {
4636
+ if (property === EffectProperty.VNODE) {
4637
+ const host = consumer;
4638
+ container.$scheduler$(ChoreType.NODE_DIFF, host, host, signal);
4639
+ }
4640
+ else {
4641
+ const host = consumer;
4642
+ const effectData = effectSubscription[EffectSubscriptionProp.DATA];
4643
+ if (effectData instanceof SubscriptionData) {
4644
+ const data = effectData.data;
4645
+ const payload = {
4646
+ ...data,
4647
+ $value$: signal,
4648
+ };
4649
+ container.$scheduler$(ChoreType.NODE_PROP, host, property, payload);
4650
+ }
4517
4651
  }
4518
4652
  }
4519
4653
  };
4520
- effects.forEach(scheduleEffect);
4654
+ for (const effect of effects) {
4655
+ scheduleEffect(effect);
4656
+ }
4521
4657
  }
4522
4658
  };
4523
4659
  /**
@@ -4575,7 +4711,7 @@ class ComputedSignal extends Signal {
4575
4711
  throwIfQRLNotResolved(computeQrl);
4576
4712
  const ctx = tryGetInvokeContext();
4577
4713
  const previousEffectSubscription = ctx?.$effectSubscriber$;
4578
- ctx && (ctx.$effectSubscriber$ = [this, EffectProperty.VNODE]);
4714
+ ctx && (ctx.$effectSubscriber$ = getSubscriber(this, EffectProperty.VNODE));
4579
4715
  try {
4580
4716
  const untrackedValue = computeQrl.getFn(ctx)();
4581
4717
  if (isPromise(untrackedValue)) {
@@ -4613,9 +4749,9 @@ class WrappedSignal extends Signal {
4613
4749
  // We need a separate flag to know when the computation needs running because
4614
4750
  // we need the old value to know if effects need running after computation
4615
4751
  $invalid$ = true;
4616
- $effectDependencies$ = null;
4617
4752
  $hostElement$ = null;
4618
4753
  $forceRunEffects$ = false;
4754
+ [_EFFECT_BACK_REF] = null;
4619
4755
  constructor(container, fn, args, fnStr) {
4620
4756
  super(container, NEEDS_COMPUTATION);
4621
4757
  this.$args$ = args;
@@ -4683,7 +4819,7 @@ const applyQwikComponentBody = (ssr, jsx, component) => {
4683
4819
  if (jsx.key !== null) {
4684
4820
  host.setProp(ELEMENT_KEY, jsx.key);
4685
4821
  }
4686
- return scheduler(ChoreType.COMPONENT_SSR, host, componentQrl, srcProps);
4822
+ return scheduler(ChoreType.COMPONENT, host, componentQrl, srcProps);
4687
4823
  };
4688
4824
 
4689
4825
  class ParentComponentData {
@@ -4695,21 +4831,10 @@ class ParentComponentData {
4695
4831
  }
4696
4832
  }
4697
4833
  /** @internal */
4698
- function _walkJSX(ssr, value, options) {
4834
+ async function _walkJSX(ssr, value, options) {
4699
4835
  const stack = [value];
4700
- let resolveDrain;
4701
- let rejectDrain;
4702
- const drained = options.allowPromises &&
4703
- new Promise((res, rej) => {
4704
- resolveDrain = res;
4705
- rejectDrain = rej;
4706
- });
4707
4836
  const enqueue = (value) => stack.push(value);
4708
- const resolveValue = (value) => {
4709
- stack.push(value);
4710
- drain();
4711
- };
4712
- const drain = () => {
4837
+ const drain = async () => {
4713
4838
  while (stack.length) {
4714
4839
  const value = stack.pop();
4715
4840
  if (value instanceof ParentComponentData) {
@@ -4719,20 +4844,10 @@ function _walkJSX(ssr, value, options) {
4719
4844
  }
4720
4845
  else if (typeof value === 'function') {
4721
4846
  if (value === Promise) {
4722
- if (!options.allowPromises) {
4723
- throw qError(QError.promisesNotExpected);
4724
- }
4725
- stack.pop().then(resolveValue, rejectDrain);
4726
- return;
4727
- }
4728
- const waitOn = value.apply(ssr);
4729
- if (waitOn) {
4730
- if (!options.allowPromises) {
4731
- throw qError(QError.promisesNotExpected);
4732
- }
4733
- waitOn.then(drain, rejectDrain);
4734
- return;
4847
+ stack.push(await stack.pop());
4848
+ continue;
4735
4849
  }
4850
+ await value.apply(ssr);
4736
4851
  continue;
4737
4852
  }
4738
4853
  processJSXNode(ssr, enqueue, value, {
@@ -4740,12 +4855,8 @@ function _walkJSX(ssr, value, options) {
4740
4855
  parentComponentFrame: options.parentComponentFrame,
4741
4856
  });
4742
4857
  }
4743
- if (stack.length === 0 && options.allowPromises) {
4744
- resolveDrain();
4745
- }
4746
4858
  };
4747
- drain();
4748
- return drained;
4859
+ await drain();
4749
4860
  }
4750
4861
  function processJSXNode(ssr, enqueue, value, options) {
4751
4862
  // console.log('processJSXNode', value);
@@ -4784,7 +4895,6 @@ function processJSXNode(ssr, enqueue, value, options) {
4784
4895
  enqueue(async () => {
4785
4896
  for await (const chunk of value) {
4786
4897
  await _walkJSX(ssr, chunk, {
4787
- allowPromises: true,
4788
4898
  currentStyleScoped: options.styleScoped,
4789
4899
  parentComponentFrame: options.parentComponentFrame,
4790
4900
  });
@@ -4871,7 +4981,6 @@ function processJSXNode(ssr, enqueue, value, options) {
4871
4981
  value = generator({
4872
4982
  async write(chunk) {
4873
4983
  await _walkJSX(ssr, chunk, {
4874
- allowPromises: true,
4875
4984
  currentStyleScoped: options.styleScoped,
4876
4985
  parentComponentFrame: options.parentComponentFrame,
4877
4986
  });
@@ -5028,11 +5137,23 @@ function setEvent(serializationCtx, key, rawValue) {
5028
5137
  const appendToValue = (valueToAppend) => {
5029
5138
  value = (value == null ? '' : value + '\n') + valueToAppend;
5030
5139
  };
5140
+ const getQrlString = (qrl) => {
5141
+ /**
5142
+ * If there are captures we need to schedule so everything is executed in the right order + qrls
5143
+ * are resolved.
5144
+ *
5145
+ * For internal qrls (starting with `_`) we assume that they do the right thing.
5146
+ */
5147
+ if (!qrl.$symbol$.startsWith('_') && (qrl.$captureRef$ || qrl.$capture$)) {
5148
+ qrl = createQRL(null, '_run', queueQRL, null, null, [qrl]);
5149
+ }
5150
+ return qrlToString(serializationCtx, qrl);
5151
+ };
5031
5152
  if (Array.isArray(qrls)) {
5032
5153
  for (let i = 0; i <= qrls.length; i++) {
5033
5154
  const qrl = qrls[i];
5034
- if (isQrl(qrl)) {
5035
- appendToValue(qrlToString(serializationCtx, qrl));
5155
+ if (isQrl$1(qrl)) {
5156
+ appendToValue(getQrlString(qrl));
5036
5157
  addQwikEventToSerializationContext(serializationCtx, key, qrl);
5037
5158
  }
5038
5159
  else if (qrl != null) {
@@ -5044,8 +5165,8 @@ function setEvent(serializationCtx, key, rawValue) {
5044
5165
  }
5045
5166
  }
5046
5167
  }
5047
- else if (isQrl(qrls)) {
5048
- value = qrlToString(serializationCtx, qrls);
5168
+ else if (isQrl$1(qrls)) {
5169
+ value = getQrlString(qrls);
5049
5170
  addQwikEventToSerializationContext(serializationCtx, key, qrls);
5050
5171
  }
5051
5172
  return value;
@@ -5095,7 +5216,7 @@ function appendClassIfScopedStyleExists(jsx, styleScoped) {
5095
5216
  *
5096
5217
  * @public
5097
5218
  */
5098
- const version = "2.0.0-alpha.6-dev+d848ba5";
5219
+ const version = "2.0.0-alpha.7-dev+a26598a";
5099
5220
 
5100
5221
  /** @internal */
5101
5222
  class _SharedContainer {
@@ -5127,13 +5248,6 @@ class _SharedContainer {
5127
5248
  }
5128
5249
  }
5129
5250
 
5130
- /** @internal */
5131
- const _CONST_PROPS = Symbol('CONST');
5132
- /** @internal */
5133
- const _VAR_PROPS = Symbol('VAR');
5134
- /** @internal @deprecated v1 compat */
5135
- const _IMMUTABLE = Symbol('IMMUTABLE');
5136
-
5137
5251
  // Keep these properties named like this so they're the same as from wrapSignal
5138
5252
  const getValueProp = (p0) => p0.value;
5139
5253
  const getProp = (p0, p1) => p0[p1];
@@ -5545,7 +5659,7 @@ function qwikDebugToString(value) {
5545
5659
  else if (isTask(value)) {
5546
5660
  return `Task(${qwikDebugToString(value.$qrl$)})`;
5547
5661
  }
5548
- else if (isQrl$1(value)) {
5662
+ else if (isQrl(value)) {
5549
5663
  return `Qrl(${value.$symbol$})`;
5550
5664
  }
5551
5665
  else if (typeof value === 'object' || typeof value === 'function') {
@@ -5699,14 +5813,21 @@ const VNodeDataChar = {
5699
5813
  CONTEXT_CHAR: /* **** */ ']',
5700
5814
  SEQ_IDX: /* ************ */ 94, // `^` - `q:seqIdx' - Sequential scope id
5701
5815
  SEQ_IDX_CHAR: /* **** */ '^',
5702
- SUBS: /* *************** */ 96, // '`' - `q:subs' - Effect dependencies/subscriptions
5703
- SUBS_CHAR: /* ******* */ '`',
5816
+ BACK_REFS: /* ********** */ 96, // '`' - `q:brefs' - Effect dependencies/subscriptions
5817
+ BACK_REFS_CHAR: /* ** */ '`',
5704
5818
  SEPARATOR: /* ********* */ 124, // `|` - Separator char to encode any key/value pairs.
5705
5819
  SEPARATOR_CHAR: /* ** */ '|',
5706
5820
  SLOT: /* ************** */ 126, // `~` - `q:slot' - Slot name
5707
5821
  SLOT_CHAR: /* ******* */ '~',
5708
5822
  };
5709
5823
 
5824
+ const mergeMaps = (map1, map2) => {
5825
+ for (const [k, v] of map2) {
5826
+ map1.set(k, v);
5827
+ }
5828
+ return map1;
5829
+ };
5830
+
5710
5831
  /**
5711
5832
  * @file
5712
5833
  *
@@ -5962,6 +6083,7 @@ const vnode_ensureElementInflated = (vnode) => {
5962
6083
  elementVNode[VNodeProps.flags] ^= VNodeFlags.Inflated;
5963
6084
  const element = elementVNode[ElementVNodeProps.element];
5964
6085
  const attributes = element.attributes;
6086
+ const props = vnode_getProps(elementVNode);
5965
6087
  for (let idx = 0; idx < attributes.length; idx++) {
5966
6088
  const attr = attributes[idx];
5967
6089
  const key = attr.name;
@@ -5972,15 +6094,15 @@ const vnode_ensureElementInflated = (vnode) => {
5972
6094
  }
5973
6095
  else if (key.startsWith(QContainerAttr)) {
5974
6096
  if (attr.value === QContainerValue.HTML) {
5975
- mapArray_set(elementVNode, dangerouslySetInnerHTML, element.innerHTML, ElementVNodeProps.PROPS_OFFSET);
6097
+ mapArray_set(props, dangerouslySetInnerHTML, element.innerHTML, 0);
5976
6098
  }
5977
6099
  else if (attr.value === QContainerValue.TEXT && 'value' in element) {
5978
- mapArray_set(elementVNode, 'value', element.value, ElementVNodeProps.PROPS_OFFSET);
6100
+ mapArray_set(props, 'value', element.value, 0);
5979
6101
  }
5980
6102
  }
5981
6103
  else if (!key.startsWith('on:')) {
5982
6104
  const value = attr.value;
5983
- mapArray_set(elementVNode, key, value, ElementVNodeProps.PROPS_OFFSET);
6105
+ mapArray_set(props, key, value, 0);
5984
6106
  }
5985
6107
  }
5986
6108
  }
@@ -6416,54 +6538,16 @@ const vnode_applyJournal = (journal) => {
6416
6538
  journal.length = 0;
6417
6539
  };
6418
6540
  //////////////////////////////////////////////////////////////////////////////////////////////////////
6419
- const mapApp_findIndx = (elementVNode, key, start) => {
6420
- assertTrue(start % 2 === 0, 'Expecting even number.');
6421
- let bottom = start >> 1;
6422
- let top = (elementVNode.length - 2) >> 1;
6423
- while (bottom <= top) {
6424
- const mid = bottom + ((top - bottom) >> 1);
6425
- const midKey = elementVNode[mid << 1];
6426
- if (midKey === key) {
6427
- return mid << 1;
6428
- }
6429
- if (midKey < key) {
6430
- bottom = mid + 1;
6431
- }
6432
- else {
6433
- top = mid - 1;
6434
- }
6435
- }
6436
- return (bottom << 1) ^ -1;
6437
- };
6438
- const mapArray_set = (elementVNode, key, value, start) => {
6439
- const indx = mapApp_findIndx(elementVNode, key, start);
6440
- if (indx >= 0) {
6441
- if (value == null) {
6442
- elementVNode.splice(indx, 2);
6443
- }
6444
- else {
6445
- elementVNode[indx + 1] = value;
6446
- }
6447
- }
6448
- else if (value != null) {
6449
- elementVNode.splice(indx ^ -1, 0, key, value);
6450
- }
6451
- };
6452
- const mapArray_get = (elementVNode, key, start) => {
6453
- const indx = mapApp_findIndx(elementVNode, key, start);
6454
- if (indx >= 0) {
6455
- return elementVNode[indx + 1];
6456
- }
6457
- else {
6458
- return null;
6459
- }
6460
- };
6461
- //////////////////////////////////////////////////////////////////////////////////////////////////////
6462
6541
  const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
6463
6542
  ensureElementOrVirtualVNode(parent);
6464
6543
  if (vnode_isElementVNode(parent)) {
6465
6544
  ensureMaterialized(parent);
6466
6545
  }
6546
+ if (newChild === insertBefore) {
6547
+ // invalid insertBefore. We can't insert before self reference
6548
+ // prevent infinity loop and putting self reference to next sibling
6549
+ insertBefore = null;
6550
+ }
6467
6551
  let adjustedInsertBefore = null;
6468
6552
  if (insertBefore == null) {
6469
6553
  if (vnode_isVirtualVNode(parent)) {
@@ -6587,9 +6671,10 @@ const vnode_getElementName = (vnode) => {
6587
6671
  const elementVNode = ensureElementVNode(vnode);
6588
6672
  let elementName = elementVNode[ElementVNodeProps.elementName];
6589
6673
  if (elementName === undefined) {
6590
- elementName = elementVNode[ElementVNodeProps.elementName] =
6591
- elementVNode[ElementVNodeProps.element].nodeName.toLowerCase();
6592
- elementVNode[VNodeProps.flags] |= vnode_getElementNamespaceFlags(elementName);
6674
+ const element = elementVNode[ElementVNodeProps.element];
6675
+ const nodeName = isDev ? fastNodeName(element).toLowerCase() : fastNodeName(element);
6676
+ elementName = elementVNode[ElementVNodeProps.elementName] = nodeName;
6677
+ elementVNode[VNodeProps.flags] |= vnode_getElementNamespaceFlags(element);
6593
6678
  }
6594
6679
  return elementName;
6595
6680
  };
@@ -6771,6 +6856,20 @@ const fastFirstChild = (node) => {
6771
6856
  }
6772
6857
  return node;
6773
6858
  };
6859
+ let _fastNamespaceURI = null;
6860
+ const fastNamespaceURI = (element) => {
6861
+ if (!_fastNamespaceURI) {
6862
+ _fastNamespaceURI = fastGetter(element, 'namespaceURI');
6863
+ }
6864
+ return _fastNamespaceURI.call(element);
6865
+ };
6866
+ let _fastNodeName = null;
6867
+ const fastNodeName = (element) => {
6868
+ if (!_fastNodeName) {
6869
+ _fastNodeName = fastGetter(element, 'nodeName');
6870
+ }
6871
+ return _fastNodeName.call(element);
6872
+ };
6774
6873
  const fastGetter = (prototype, name) => {
6775
6874
  let getter;
6776
6875
  while (prototype && !(getter = Object.getOwnPropertyDescriptor(prototype, name)?.get)) {
@@ -6837,8 +6936,11 @@ const materializeFromDOM = (vParent, firstChild, vData) => {
6837
6936
  container.$setRawState$(parseInt(id), vParent);
6838
6937
  isDev && vnode_setAttr(null, vParent, ELEMENT_ID, id);
6839
6938
  }
6840
- else if (peek() === VNodeDataChar.SUBS) {
6841
- vnode_setProp(vParent, QSubscribers, consumeValue());
6939
+ else if (peek() === VNodeDataChar.BACK_REFS) {
6940
+ if (!container) {
6941
+ container = getDomContainer(vParent[ElementVNodeProps.element]);
6942
+ }
6943
+ setEffectBackRefFromVNodeData(vParent, consumeValue(), container);
6842
6944
  }
6843
6945
  else {
6844
6946
  // prevent infinity loop if there are some characters outside the range
@@ -6848,6 +6950,18 @@ const materializeFromDOM = (vParent, firstChild, vData) => {
6848
6950
  }
6849
6951
  return vFirstChild;
6850
6952
  };
6953
+ function setEffectBackRefFromVNodeData(vParent, value, container) {
6954
+ const deserializedSubMap = container.$getObjectById$(value);
6955
+ if (!vParent[_EFFECT_BACK_REF]) {
6956
+ Object.defineProperty(vParent, _EFFECT_BACK_REF, {
6957
+ value: deserializedSubMap,
6958
+ });
6959
+ }
6960
+ else {
6961
+ const subMap = vParent[_EFFECT_BACK_REF];
6962
+ mergeMaps(subMap, deserializedSubMap);
6963
+ }
6964
+ }
6851
6965
  const processVNodeData$1 = (vData, callback) => {
6852
6966
  let nextToConsumeIdx = 0;
6853
6967
  let ch = 0;
@@ -6892,8 +7006,9 @@ const vnode_getAttrKeys = (vnode) => {
6892
7006
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6893
7007
  vnode_ensureElementInflated(vnode);
6894
7008
  const keys = [];
6895
- for (let i = vnode_getPropStartIndex(vnode); i < vnode.length; i = i + 2) {
6896
- const key = vnode[i];
7009
+ const props = vnode_getProps(vnode);
7010
+ for (let i = 0; i < props.length; i = i + 2) {
7011
+ const key = props[i];
6897
7012
  if (!key.startsWith(Q_PROPS_SEPARATOR)) {
6898
7013
  keys.push(key);
6899
7014
  }
@@ -6906,22 +7021,23 @@ const vnode_setAttr = (journal, vnode, key, value) => {
6906
7021
  const type = vnode[VNodeProps.flags];
6907
7022
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6908
7023
  vnode_ensureElementInflated(vnode);
6909
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7024
+ const props = vnode_getProps(vnode);
7025
+ const idx = mapApp_findIndx(props, key, 0);
6910
7026
  if (idx >= 0) {
6911
- if (vnode[idx + 1] != value && (type & VNodeFlags.Element) !== 0) {
7027
+ if (props[idx + 1] != value && (type & VNodeFlags.Element) !== 0) {
6912
7028
  // Values are different, update DOM
6913
7029
  const element = vnode[ElementVNodeProps.element];
6914
7030
  journal && journal.push(VNodeJournalOpCode.SetAttribute, element, key, value);
6915
7031
  }
6916
7032
  if (value == null) {
6917
- vnode.splice(idx, 2);
7033
+ props.splice(idx, 2);
6918
7034
  }
6919
7035
  else {
6920
- vnode[idx + 1] = value;
7036
+ props[idx + 1] = value;
6921
7037
  }
6922
7038
  }
6923
7039
  else if (value != null) {
6924
- vnode.splice(idx ^ -1, 0, key, value);
7040
+ props.splice(idx ^ -1, 0, key, value);
6925
7041
  if ((type & VNodeFlags.Element) !== 0) {
6926
7042
  // New value, update DOM
6927
7043
  const element = vnode[ElementVNodeProps.element];
@@ -6934,7 +7050,8 @@ const vnode_getAttr = (vnode, key) => {
6934
7050
  const type = vnode[VNodeProps.flags];
6935
7051
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6936
7052
  vnode_ensureElementInflated(vnode);
6937
- return mapArray_get(vnode, key, vnode_getPropStartIndex(vnode));
7053
+ const props = vnode_getProps(vnode);
7054
+ return mapArray_get(props, key, 0);
6938
7055
  }
6939
7056
  return null;
6940
7057
  };
@@ -6942,11 +7059,12 @@ const vnode_getProp = (vnode, key, getObject) => {
6942
7059
  const type = vnode[VNodeProps.flags];
6943
7060
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6944
7061
  type & VNodeFlags.Element && vnode_ensureElementInflated(vnode);
6945
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7062
+ const props = vnode_getProps(vnode);
7063
+ const idx = mapApp_findIndx(props, key, 0);
6946
7064
  if (idx >= 0) {
6947
- let value = vnode[idx + 1];
7065
+ let value = props[idx + 1];
6948
7066
  if (typeof value === 'string' && getObject) {
6949
- vnode[idx + 1] = value = getObject(value);
7067
+ props[idx + 1] = value = getObject(value);
6950
7068
  }
6951
7069
  return value;
6952
7070
  }
@@ -6955,12 +7073,13 @@ const vnode_getProp = (vnode, key, getObject) => {
6955
7073
  };
6956
7074
  const vnode_setProp = (vnode, key, value) => {
6957
7075
  ensureElementOrVirtualVNode(vnode);
6958
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7076
+ const props = vnode_getProps(vnode);
7077
+ const idx = mapApp_findIndx(props, key, 0);
6959
7078
  if (idx >= 0) {
6960
- vnode[idx + 1] = value;
7079
+ props[idx + 1] = value;
6961
7080
  }
6962
7081
  else if (value != null) {
6963
- vnode.splice(idx ^ -1, 0, key, value);
7082
+ props.splice(idx ^ -1, 0, key, value);
6964
7083
  }
6965
7084
  };
6966
7085
  const vnode_getPropStartIndex = (vnode) => {
@@ -6973,6 +7092,9 @@ const vnode_getPropStartIndex = (vnode) => {
6973
7092
  }
6974
7093
  throw qError(QError.invalidVNodeType, [type]);
6975
7094
  };
7095
+ const vnode_getProps = (vnode) => {
7096
+ return vnode[vnode_getPropStartIndex(vnode)];
7097
+ };
6976
7098
  const vnode_getParent = (vnode) => {
6977
7099
  return vnode[VNodeProps.parent] || null;
6978
7100
  };
@@ -7136,8 +7258,11 @@ function materializeFromVNodeData(vParent, vData, element, child) {
7136
7258
  else if (peek() === VNodeDataChar.SEQ_IDX) {
7137
7259
  vnode_setAttr(null, vParent, ELEMENT_SEQ_IDX, consumeValue());
7138
7260
  }
7139
- else if (peek() === VNodeDataChar.SUBS) {
7140
- vnode_setProp(vParent, QSubscribers, consumeValue());
7261
+ else if (peek() === VNodeDataChar.BACK_REFS) {
7262
+ if (!container) {
7263
+ container = getDomContainer(element);
7264
+ }
7265
+ setEffectBackRefFromVNodeData(vParent, consumeValue(), container);
7141
7266
  }
7142
7267
  else if (peek() === VNodeDataChar.CONTEXT) {
7143
7268
  vnode_setAttr(null, vParent, QCtxAttr, consumeValue());
@@ -7245,23 +7370,20 @@ const vnode_getProjectionParentComponent = (vHost, rootVNode) => {
7245
7370
  };
7246
7371
  const VNodeArray = class VNode extends Array {
7247
7372
  static createElement(flags, parent, previousSibling, nextSibling, firstChild, lastChild, element, elementName) {
7248
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7249
- vnode.push(firstChild, lastChild, element, elementName);
7373
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, firstChild, lastChild, element, elementName, []);
7250
7374
  return vnode;
7251
7375
  }
7252
7376
  static createText(flags, parent, previousSibling, nextSibling, textNode, text) {
7253
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7254
- vnode.push(textNode, text);
7377
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, textNode, text);
7255
7378
  return vnode;
7256
7379
  }
7257
7380
  static createVirtual(flags, parent, previousSibling, nextSibling, firstChild, lastChild) {
7258
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7259
- vnode.push(firstChild, lastChild);
7381
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, firstChild, lastChild, []);
7260
7382
  return vnode;
7261
7383
  }
7262
- constructor(flags, parent, previousSibling, nextSibling) {
7263
- super();
7264
- this.push(flags, parent, previousSibling, nextSibling);
7384
+ constructor(flags, parent, previousSibling, nextSibling, ...rest) {
7385
+ // @ts-expect-error
7386
+ super(flags, parent, previousSibling, nextSibling, ...rest);
7265
7387
  if (isDev) {
7266
7388
  this.toString = vnode_toString;
7267
7389
  }
@@ -7326,10 +7448,12 @@ function invokeApply(context, fn, args) {
7326
7448
  return returnValue;
7327
7449
  }
7328
7450
  const newInvokeContextFromTuple = ([element, event, url]) => {
7329
- const container = element.closest(QContainerSelector);
7451
+ const domContainer = getDomContainer(element);
7452
+ const container = domContainer.element;
7453
+ const vNode = container ? vnode_locate(domContainer.rootVNode, element) : undefined;
7330
7454
  const locale = container?.getAttribute(QLocaleAttr) || undefined;
7331
7455
  locale && setLocale(locale);
7332
- return newInvokeContext(locale, undefined, element, event, url);
7456
+ return newInvokeContext(locale, vNode, element, event, url);
7333
7457
  };
7334
7458
  // TODO how about putting url and locale (and event/custom?) in to a "static" object
7335
7459
  const newInvokeContext = (locale, hostElement, element, event, url) => {
@@ -7364,16 +7488,14 @@ const trackInvocation = /*#__PURE__*/ newInvokeContext(undefined, undefined, und
7364
7488
  * @param property `true` - subscriber is component `false` - subscriber is VNode `string` -
7365
7489
  * subscriber is property
7366
7490
  * @param container
7491
+ * @param data - Additional subscription data
7367
7492
  * @returns
7368
7493
  */
7369
7494
  const trackSignal = (fn, subscriber, property, container, data) => {
7370
7495
  const previousSubscriber = trackInvocation.$effectSubscriber$;
7371
7496
  const previousContainer = trackInvocation.$container$;
7372
7497
  try {
7373
- trackInvocation.$effectSubscriber$ = [subscriber, property];
7374
- if (data) {
7375
- trackInvocation.$effectSubscriber$.push(data);
7376
- }
7498
+ trackInvocation.$effectSubscriber$ = getSubscriber(subscriber, property, data);
7377
7499
  trackInvocation.$container$ = container;
7378
7500
  return invoke(trackInvocation, fn);
7379
7501
  }
@@ -7394,8 +7516,16 @@ const _getContextElement = () => {
7394
7516
  if (iCtx) {
7395
7517
  const hostElement = iCtx.$hostElement$;
7396
7518
  let element = null;
7397
- if (vnode_isVNode(hostElement) && vnode_isElementVNode(hostElement)) {
7398
- element = vnode_getNode(hostElement);
7519
+ if (hostElement != null) {
7520
+ if (vnode_isVNode(hostElement)) {
7521
+ if (vnode_isElementVNode(hostElement)) {
7522
+ element = vnode_getNode(hostElement);
7523
+ }
7524
+ }
7525
+ else {
7526
+ // isSSRnode
7527
+ element = hostElement;
7528
+ }
7399
7529
  }
7400
7530
  return element ?? iCtx.$qrl$?.$setContainer$(undefined);
7401
7531
  }
@@ -8031,9 +8161,9 @@ class DomContainer extends _SharedContainer {
8031
8161
  $storeProxyMap$ = new WeakMap();
8032
8162
  $qFuncs$;
8033
8163
  $instanceHash$;
8034
- stateData;
8164
+ vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
8165
+ $stateData$;
8035
8166
  $styleIds$ = null;
8036
- $vnodeLocate$ = (id) => vnode_locate(this.rootVNode, id);
8037
8167
  $renderCount$ = 0;
8038
8168
  constructor(element) {
8039
8169
  super(() => this.scheduleRender(), () => vnode_applyJournal(this.$journal$), {}, element.getAttribute('q:locale'));
@@ -8059,29 +8189,29 @@ class DomContainer extends _SharedContainer {
8059
8189
  this.rootVNode = vnode_newUnMaterializedElement(this.element);
8060
8190
  // These are here to initialize all properties at once for single class transition
8061
8191
  this.$rawStateData$ = null;
8062
- this.stateData = null;
8192
+ this.$stateData$ = null;
8063
8193
  const document = this.element.ownerDocument;
8064
8194
  if (!document.qVNodeData) {
8065
8195
  processVNodeData(document);
8066
8196
  }
8067
8197
  this.$rawStateData$ = [];
8068
- this.stateData = [];
8198
+ this.$stateData$ = [];
8069
8199
  const qwikStates = element.querySelectorAll('script[type="qwik/state"]');
8070
8200
  if (qwikStates.length !== 0) {
8071
8201
  const lastState = qwikStates[qwikStates.length - 1];
8072
8202
  this.$rawStateData$ = JSON.parse(lastState.textContent);
8073
- this.stateData = wrapDeserializerProxy(this, this.$rawStateData$);
8203
+ this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
8074
8204
  }
8075
8205
  this.$qFuncs$ = getQFuncs(document, this.$instanceHash$) || EMPTY_ARRAY;
8076
8206
  }
8077
8207
  $setRawState$(id, vParent) {
8078
- this.stateData[id] = vParent;
8208
+ this.$stateData$[id] = vParent;
8079
8209
  }
8080
8210
  parseQRL(qrl) {
8081
8211
  return inflateQRL(this, parseQRL(qrl));
8082
8212
  }
8083
8213
  handleError(err, host) {
8084
- if (qDev) {
8214
+ if (qDev && host) {
8085
8215
  // Clean vdom
8086
8216
  if (typeof document !== 'undefined') {
8087
8217
  const vHost = host;
@@ -8105,7 +8235,7 @@ class DomContainer extends _SharedContainer {
8105
8235
  throw err;
8106
8236
  }
8107
8237
  }
8108
- const errorStore = this.resolveContext(host, ERROR_CONTEXT);
8238
+ const errorStore = host && this.resolveContext(host, ERROR_CONTEXT);
8109
8239
  if (!errorStore) {
8110
8240
  throw err;
8111
8241
  }
@@ -8141,7 +8271,7 @@ class DomContainer extends _SharedContainer {
8141
8271
  vNode =
8142
8272
  vnode_getParent(vNode) ||
8143
8273
  // If virtual node, than it could be a slot so we need to read its parent.
8144
- vnode_getProp(vNode, QSlotParent, this.$vnodeLocate$);
8274
+ vnode_getProp(vNode, QSlotParent, this.vNodeLocate);
8145
8275
  }
8146
8276
  else {
8147
8277
  vNode = vnode_getParent(vNode);
@@ -8161,7 +8291,7 @@ class DomContainer extends _SharedContainer {
8161
8291
  case ELEMENT_PROPS:
8162
8292
  case OnRenderProp:
8163
8293
  case QCtxAttr:
8164
- case QSubscribers:
8294
+ case QBackRefs:
8165
8295
  getObjectById = this.$getObjectById$;
8166
8296
  break;
8167
8297
  case ELEMENT_SEQ_IDX:
@@ -8174,7 +8304,7 @@ class DomContainer extends _SharedContainer {
8174
8304
  scheduleRender() {
8175
8305
  this.$renderCount$++;
8176
8306
  this.renderDone ||= getPlatform().nextTick(() => this.processChores());
8177
- return this.renderDone;
8307
+ return this.renderDone.finally(() => emitEvent('qrender', { instanceHash: this.$instanceHash$, renderCount: this.$renderCount$ }));
8178
8308
  }
8179
8309
  processChores() {
8180
8310
  let renderCount = this.$renderCount$;
@@ -8197,12 +8327,13 @@ class DomContainer extends _SharedContainer {
8197
8327
  ensureProjectionResolved(vNode) {
8198
8328
  if ((vNode[VNodeProps.flags] & VNodeFlags.Resolved) === 0) {
8199
8329
  vNode[VNodeProps.flags] |= VNodeFlags.Resolved;
8200
- for (let i = vnode_getPropStartIndex(vNode); i < vNode.length; i = i + 2) {
8201
- const prop = vNode[i];
8330
+ const props = vnode_getProps(vNode);
8331
+ for (let i = 0; i < props.length; i = i + 2) {
8332
+ const prop = props[i];
8202
8333
  if (isSlotProp(prop)) {
8203
- const value = vNode[i + 1];
8334
+ const value = props[i + 1];
8204
8335
  if (typeof value == 'string') {
8205
- vNode[i + 1] = this.$vnodeLocate$(value);
8336
+ props[i + 1] = this.vNodeLocate(value);
8206
8337
  }
8207
8338
  }
8208
8339
  }
@@ -8213,7 +8344,7 @@ class DomContainer extends _SharedContainer {
8213
8344
  id = parseFloat(id);
8214
8345
  }
8215
8346
  assertTrue(id < this.$rawStateData$.length / 2, `Invalid reference: ${id} >= ${this.$rawStateData$.length / 2}`);
8216
- return this.stateData[id];
8347
+ return this.$stateData$[id];
8217
8348
  };
8218
8349
  getSyncFn(id) {
8219
8350
  const fn = this.$qFuncs$[id];
@@ -8285,8 +8416,7 @@ class DeserializationHandler {
8285
8416
  ? parseInt(property, 10)
8286
8417
  : NaN;
8287
8418
  if (Number.isNaN(i) || i < 0 || i >= this.$length$) {
8288
- const out = Reflect.get(target, property, receiver);
8289
- return out;
8419
+ return Reflect.get(target, property, receiver);
8290
8420
  }
8291
8421
  // The serialized data is an array with 2 values for each item
8292
8422
  const idx = i * 2;
@@ -8352,6 +8482,7 @@ const inflate = (container, target, typeId, data) => {
8352
8482
  switch (typeId) {
8353
8483
  case TypeIds.Object:
8354
8484
  // We use getters for making complex values lazy
8485
+ // TODO scan the data for computeQRLs and schedule resolve chores
8355
8486
  for (let i = 0; i < data.length; i += 4) {
8356
8487
  const key = deserializeData(container, data[i], data[i + 1]);
8357
8488
  const valType = data[i + 2];
@@ -8391,7 +8522,7 @@ const inflate = (container, target, typeId, data) => {
8391
8522
  task.$flags$ = v[1];
8392
8523
  task.$index$ = v[2];
8393
8524
  task.$el$ = v[3];
8394
- task.$effectDependencies$ = v[4];
8525
+ task[_EFFECT_BACK_REF] = v[4];
8395
8526
  task.$state$ = v[5];
8396
8527
  break;
8397
8528
  case TypeIds.Resource:
@@ -8414,12 +8545,9 @@ const inflate = (container, target, typeId, data) => {
8414
8545
  break;
8415
8546
  case TypeIds.Store:
8416
8547
  case TypeIds.StoreArray: {
8417
- const [value, flags, effects, storeEffect] = data;
8548
+ const [value, flags, effects] = data;
8418
8549
  const store = getOrCreateStore(value, flags, container);
8419
8550
  const storeHandler = getStoreHandler(store);
8420
- if (storeEffect) {
8421
- effects[STORE_ARRAY_PROP] = storeEffect;
8422
- }
8423
8551
  storeHandler.$effects$ = effects;
8424
8552
  target = store;
8425
8553
  break;
@@ -8428,7 +8556,7 @@ const inflate = (container, target, typeId, data) => {
8428
8556
  const signal = target;
8429
8557
  const d = data;
8430
8558
  signal.$untrackedValue$ = d[0];
8431
- signal.$effects$ = d.slice(1);
8559
+ signal.$effects$ = new Set(d.slice(1));
8432
8560
  break;
8433
8561
  }
8434
8562
  case TypeIds.WrappedSignal: {
@@ -8436,10 +8564,10 @@ const inflate = (container, target, typeId, data) => {
8436
8564
  const d = data;
8437
8565
  signal.$func$ = container.getSyncFn(d[0]);
8438
8566
  signal.$args$ = d[1];
8439
- signal.$effectDependencies$ = d[2];
8567
+ signal[_EFFECT_BACK_REF] = d[2];
8440
8568
  signal.$untrackedValue$ = d[3];
8441
8569
  signal.$hostElement$ = d[4];
8442
- signal.$effects$ = d.slice(5);
8570
+ signal.$effects$ = new Set(d.slice(5));
8443
8571
  break;
8444
8572
  }
8445
8573
  case TypeIds.ComputedSignal: {
@@ -8559,6 +8687,7 @@ const _constants = [
8559
8687
  EMPTY_ARRAY,
8560
8688
  EMPTY_OBJ,
8561
8689
  NEEDS_COMPUTATION,
8690
+ STORE_ARRAY_PROP,
8562
8691
  Slot,
8563
8692
  Fragment,
8564
8693
  NaN,
@@ -8639,6 +8768,8 @@ const allocate = (container, typeId, value) => {
8639
8768
  reject = rej;
8640
8769
  });
8641
8770
  resolvers.set(promise, [resolve, reject]);
8771
+ // Don't leave unhandled promise rejections
8772
+ promise.catch(() => { });
8642
8773
  return promise;
8643
8774
  case TypeIds.Uint8Array:
8644
8775
  const encodedLength = value.length;
@@ -8648,12 +8779,9 @@ const allocate = (container, typeId, value) => {
8648
8779
  return new Uint8Array(decodedLength);
8649
8780
  case TypeIds.PropsProxy:
8650
8781
  return createPropsProxy(null, null);
8651
- case TypeIds.RefVNode:
8652
8782
  case TypeIds.VNode:
8653
- const vnodeOrDocument = retrieveVNodeOrDocument(container, value);
8654
- if (typeId === TypeIds.VNode) {
8655
- return vnodeOrDocument;
8656
- }
8783
+ return retrieveVNodeOrDocument(container, value);
8784
+ case TypeIds.RefVNode:
8657
8785
  const vNode = retrieveVNodeOrDocument(container, value);
8658
8786
  if (vnode_isVNode(vNode)) {
8659
8787
  return vnode_getNode(vNode);
@@ -8662,7 +8790,7 @@ const allocate = (container, typeId, value) => {
8662
8790
  throw qError(QError.serializeErrorExpectedVNode, [typeof vNode]);
8663
8791
  }
8664
8792
  case TypeIds.EffectData:
8665
- return new EffectPropData({});
8793
+ return new SubscriptionData({});
8666
8794
  default:
8667
8795
  throw qError(QError.serializeErrorCannotAllocate, [typeId]);
8668
8796
  }
@@ -8694,7 +8822,7 @@ function parseQRL(qrl) {
8694
8822
  assertDefined(backChannel, 'Missing QRL_RUNTIME_CHUNK');
8695
8823
  qrlRef = backChannel.get(symbol);
8696
8824
  }
8697
- return createQRL(chunk, symbol, qrlRef, null, captureIds, null, null);
8825
+ return createQRL(chunk, symbol, qrlRef, null, captureIds, null);
8698
8826
  }
8699
8827
  function inflateQRL(container, qrl) {
8700
8828
  const captureIds = qrl.$capture$;
@@ -8812,7 +8940,7 @@ prepVNodeData) => {
8812
8940
  /** Visit an object, adding anything that will be serialized as to scan */
8813
8941
  const visit = (obj) => {
8814
8942
  if (typeof obj === 'function') {
8815
- if (isQrl(obj)) {
8943
+ if (isQrl$1(obj)) {
8816
8944
  if (obj.$captureRef$) {
8817
8945
  discoveredValues.push(...obj.$captureRef$);
8818
8946
  }
@@ -8831,6 +8959,7 @@ prepVNodeData) => {
8831
8959
  obj instanceof RegExp ||
8832
8960
  obj instanceof Uint8Array ||
8833
8961
  obj instanceof URLSearchParams ||
8962
+ vnode_isVNode(obj) ||
8834
8963
  (typeof FormData !== 'undefined' && obj instanceof FormData) ||
8835
8964
  // Ignore the no serialize objects
8836
8965
  fastSkipSerialize(obj)) ;
@@ -8840,8 +8969,7 @@ prepVNodeData) => {
8840
8969
  else if (isStore(obj)) {
8841
8970
  const target = getStoreTarget(obj);
8842
8971
  const effects = getStoreHandler(obj).$effects$;
8843
- const storeEffect = effects?.[STORE_ARRAY_PROP] ?? null;
8844
- discoveredValues.push(target, effects, storeEffect);
8972
+ discoveredValues.push(target, effects);
8845
8973
  for (const prop in target) {
8846
8974
  const propValue = target[prop];
8847
8975
  if (storeProxyMap.has(propValue)) {
@@ -8875,9 +9003,7 @@ prepVNodeData) => {
8875
9003
  }
8876
9004
  // WrappedSignal uses syncQrl which has no captured refs
8877
9005
  if (obj instanceof WrappedSignal) {
8878
- if (obj.$effectDependencies$) {
8879
- discoveredValues.push(...obj.$effectDependencies$);
8880
- }
9006
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8881
9007
  if (obj.$args$) {
8882
9008
  discoveredValues.push(...obj.$args$);
8883
9009
  }
@@ -8890,7 +9016,8 @@ prepVNodeData) => {
8890
9016
  }
8891
9017
  }
8892
9018
  else if (obj instanceof Task) {
8893
- discoveredValues.push(obj.$el$, obj.$qrl$, obj.$state$, obj.$effectDependencies$);
9019
+ discoveredValues.push(obj.$el$, obj.$qrl$, obj.$state$);
9020
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8894
9021
  }
8895
9022
  else if (isSsrNode(obj)) {
8896
9023
  discoverValuesForVNodeData(obj.vnodeData, discoveredValues);
@@ -8909,7 +9036,7 @@ prepVNodeData) => {
8909
9036
  else if (Array.isArray(obj)) {
8910
9037
  discoveredValues.push(...obj);
8911
9038
  }
8912
- else if (isQrl(obj)) {
9039
+ else if (isQrl$1(obj)) {
8913
9040
  obj.$captureRef$ && obj.$captureRef$.length && discoveredValues.push(...obj.$captureRef$);
8914
9041
  }
8915
9042
  else if (isPropsProxy(obj)) {
@@ -8925,7 +9052,7 @@ prepVNodeData) => {
8925
9052
  });
8926
9053
  promises.push(obj);
8927
9054
  }
8928
- else if (obj instanceof EffectPropData) {
9055
+ else if (obj instanceof SubscriptionData) {
8929
9056
  discoveredValues.push(obj.data);
8930
9057
  }
8931
9058
  else if (isObjectLiteral(obj)) {
@@ -8970,15 +9097,31 @@ const discoverValuesForVNodeData = (vnodeData, discoveredValues) => {
8970
9097
  for (const value of vnodeData) {
8971
9098
  if (isSsrAttrs(value)) {
8972
9099
  for (let i = 1; i < value.length; i += 2) {
8973
- if (value[i - 1] === ELEMENT_KEY) {
9100
+ const attrValue = value[i];
9101
+ if (typeof attrValue === 'string') {
8974
9102
  continue;
8975
9103
  }
8976
- const attrValue = value[i];
8977
9104
  discoveredValues.push(attrValue);
8978
9105
  }
8979
9106
  }
8980
9107
  }
8981
9108
  };
9109
+ const discoverEffectBackRefs = (effectsBackRefs, discoveredValues) => {
9110
+ if (effectsBackRefs) {
9111
+ // We need serialize effect subscriptions with back refs
9112
+ let hasBackRefs = false;
9113
+ for (const [, effect] of effectsBackRefs) {
9114
+ const backRefs = effect[EffectSubscriptionProp.BACK_REF];
9115
+ if (backRefs) {
9116
+ hasBackRefs = true;
9117
+ break;
9118
+ }
9119
+ }
9120
+ if (hasBackRefs) {
9121
+ discoveredValues.push(effectsBackRefs);
9122
+ }
9123
+ }
9124
+ };
8982
9125
  const promiseResults = new WeakMap();
8983
9126
  /**
8984
9127
  * Format:
@@ -9050,7 +9193,7 @@ function serialize(serializationContext) {
9050
9193
  else if (value === Fragment) {
9051
9194
  output(TypeIds.Constant, Constants.Fragment);
9052
9195
  }
9053
- else if (isQrl(value)) {
9196
+ else if (isQrl$1(value)) {
9054
9197
  const qrl = qrlToString(serializationContext, value);
9055
9198
  const id = serializationContext.$addRoot$(qrl);
9056
9199
  output(TypeIds.QRL, id);
@@ -9125,6 +9268,9 @@ function serialize(serializationContext) {
9125
9268
  else if (value === NEEDS_COMPUTATION) {
9126
9269
  output(TypeIds.Constant, Constants.NEEDS_COMPUTATION);
9127
9270
  }
9271
+ else if (value === STORE_ARRAY_PROP) {
9272
+ output(TypeIds.Constant, Constants.STORE_ARRAY_PROP);
9273
+ }
9128
9274
  else {
9129
9275
  throw qError(QError.serializeErrorUnknownType, [typeof value]);
9130
9276
  }
@@ -9161,7 +9307,7 @@ function serialize(serializationContext) {
9161
9307
  : 0;
9162
9308
  output(TypeIds.PropsProxy, out);
9163
9309
  }
9164
- else if (value instanceof EffectPropData) {
9310
+ else if (value instanceof SubscriptionData) {
9165
9311
  output(TypeIds.EffectData, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
9166
9312
  }
9167
9313
  else if (isStore(value)) {
@@ -9172,6 +9318,7 @@ function serialize(serializationContext) {
9172
9318
  if (!res) {
9173
9319
  throw qError(QError.serializeErrorUnvisited, ['resource']);
9174
9320
  }
9321
+ // TODO the effects include the resourcereturn which has duplicate data
9175
9322
  output(TypeIds.Resource, [...res, getStoreHandler(value).$effects$]);
9176
9323
  }
9177
9324
  else {
@@ -9179,7 +9326,6 @@ function serialize(serializationContext) {
9179
9326
  const storeTarget = getStoreTarget(value);
9180
9327
  const flags = storeHandler.$flags$;
9181
9328
  const effects = storeHandler.$effects$;
9182
- const storeEffect = effects?.[STORE_ARRAY_PROP] ?? null;
9183
9329
  const innerStores = [];
9184
9330
  for (const prop in storeTarget) {
9185
9331
  const propValue = storeTarget[prop];
@@ -9189,7 +9335,7 @@ function serialize(serializationContext) {
9189
9335
  serializationContext.$addRoot$(innerStore);
9190
9336
  }
9191
9337
  }
9192
- const out = [storeTarget, flags, effects, storeEffect, ...innerStores];
9338
+ const out = [storeTarget, flags, effects, ...innerStores];
9193
9339
  while (out[out.length - 1] == null) {
9194
9340
  out.pop();
9195
9341
  }
@@ -9228,7 +9374,7 @@ function serialize(serializationContext) {
9228
9374
  if (value instanceof WrappedSignal) {
9229
9375
  output(TypeIds.WrappedSignal, [
9230
9376
  ...serializeWrappingFn(serializationContext, value),
9231
- value.$effectDependencies$,
9377
+ filterEffectBackRefs(value[_EFFECT_BACK_REF]),
9232
9378
  v,
9233
9379
  value.$hostElement$,
9234
9380
  ...(value.$effects$ || []),
@@ -9335,7 +9481,7 @@ function serialize(serializationContext) {
9335
9481
  value.$flags$,
9336
9482
  value.$index$,
9337
9483
  value.$el$,
9338
- value.$effectDependencies$,
9484
+ value[_EFFECT_BACK_REF],
9339
9485
  value.$state$,
9340
9486
  ];
9341
9487
  while (out[out.length - 1] == null) {
@@ -9358,12 +9504,27 @@ function serialize(serializationContext) {
9358
9504
  const out = btoa(buf).replace(/=+$/, '');
9359
9505
  output(TypeIds.Uint8Array, out);
9360
9506
  }
9507
+ else if (vnode_isVNode(value)) {
9508
+ output(TypeIds.Constant, Constants.Undefined);
9509
+ }
9361
9510
  else {
9362
9511
  throw qError(QError.serializeErrorUnknownType, [typeof value]);
9363
9512
  }
9364
9513
  };
9365
9514
  writeValue(serializationContext.$roots$, -1);
9366
9515
  }
9516
+ function filterEffectBackRefs(effectBackRef) {
9517
+ let effectBackRefToSerialize = null;
9518
+ if (effectBackRef) {
9519
+ for (const [effectProp, effect] of effectBackRef) {
9520
+ if (effect[EffectSubscriptionProp.BACK_REF]) {
9521
+ effectBackRefToSerialize ||= new Map();
9522
+ effectBackRefToSerialize.set(effectProp, effect);
9523
+ }
9524
+ }
9525
+ }
9526
+ return effectBackRefToSerialize;
9527
+ }
9367
9528
  function serializeWrappingFn(serializationContext, value) {
9368
9529
  // if value is an object then we need to wrap this in ()
9369
9530
  if (value.$funcStr$ && value.$funcStr$[0] === '{') {
@@ -9376,15 +9537,12 @@ function serializeWrappingFn(serializationContext, value) {
9376
9537
  function qrlToString(serializationContext, value) {
9377
9538
  let symbol = value.$symbol$;
9378
9539
  let chunk = value.$chunk$;
9379
- const refSymbol = value.$refSymbol$ ?? symbol;
9380
9540
  const platform = getPlatform();
9381
9541
  if (platform) {
9382
- const result = platform.chunkForSymbol(refSymbol, chunk, value.dev?.file);
9542
+ const result = platform.chunkForSymbol(symbol, chunk, value.dev?.file);
9383
9543
  if (result) {
9384
9544
  chunk = result[1];
9385
- if (!value.$refSymbol$) {
9386
- symbol = result[0];
9387
- }
9545
+ symbol = result[0];
9388
9546
  }
9389
9547
  }
9390
9548
  const isSync = isSyncQrl(value);
@@ -9561,9 +9719,9 @@ const frameworkType = (obj) => {
9561
9719
  return ((typeof obj === 'object' &&
9562
9720
  obj !== null &&
9563
9721
  (obj instanceof Signal || obj instanceof Task || isJSXNode(obj))) ||
9564
- isQrl(obj));
9722
+ isQrl$1(obj));
9565
9723
  };
9566
- const canSerialize = (value) => {
9724
+ const canSerialize = (value, seen = new WeakSet()) => {
9567
9725
  if (value == null ||
9568
9726
  typeof value === 'string' ||
9569
9727
  typeof value === 'number' ||
@@ -9572,6 +9730,10 @@ const canSerialize = (value) => {
9572
9730
  return true;
9573
9731
  }
9574
9732
  else if (typeof value === 'object') {
9733
+ if (seen.has(value)) {
9734
+ return true;
9735
+ }
9736
+ seen.add(value);
9575
9737
  const proto = Object.getPrototypeOf(value);
9576
9738
  if (isStore(value)) {
9577
9739
  value = getStoreTarget(value);
@@ -9580,7 +9742,7 @@ const canSerialize = (value) => {
9580
9742
  for (const key in value) {
9581
9743
  // if the value is a props proxy, then sometimes we could create a component-level subscription,
9582
9744
  // so we should call untrack here to avoid tracking the value
9583
- if (!canSerialize(untrack(() => value[key]))) {
9745
+ if (!canSerialize(untrack(() => value[key]), seen)) {
9584
9746
  return false;
9585
9747
  }
9586
9748
  }
@@ -9588,7 +9750,7 @@ const canSerialize = (value) => {
9588
9750
  }
9589
9751
  else if (proto == Array.prototype) {
9590
9752
  for (let i = 0; i < value.length; i++) {
9591
- if (!canSerialize(value[i])) {
9753
+ if (!canSerialize(value[i], seen)) {
9592
9754
  return false;
9593
9755
  }
9594
9756
  }
@@ -9638,7 +9800,7 @@ const canSerialize = (value) => {
9638
9800
  }
9639
9801
  }
9640
9802
  else if (typeof value === 'function') {
9641
- if (isQrl(value) || isQwikComponent(value)) {
9803
+ if (isQrl$1(value) || isQwikComponent(value)) {
9642
9804
  return true;
9643
9805
  }
9644
9806
  }
@@ -9691,20 +9853,21 @@ var Constants;
9691
9853
  Constants[Constants["EMPTY_ARRAY"] = 5] = "EMPTY_ARRAY";
9692
9854
  Constants[Constants["EMPTY_OBJ"] = 6] = "EMPTY_OBJ";
9693
9855
  Constants[Constants["NEEDS_COMPUTATION"] = 7] = "NEEDS_COMPUTATION";
9694
- Constants[Constants["Slot"] = 8] = "Slot";
9695
- Constants[Constants["Fragment"] = 9] = "Fragment";
9696
- Constants[Constants["NaN"] = 10] = "NaN";
9697
- Constants[Constants["PositiveInfinity"] = 11] = "PositiveInfinity";
9698
- Constants[Constants["NegativeInfinity"] = 12] = "NegativeInfinity";
9699
- Constants[Constants["MaxSafeInt"] = 13] = "MaxSafeInt";
9856
+ Constants[Constants["STORE_ARRAY_PROP"] = 8] = "STORE_ARRAY_PROP";
9857
+ Constants[Constants["Slot"] = 9] = "Slot";
9858
+ Constants[Constants["Fragment"] = 10] = "Fragment";
9859
+ Constants[Constants["NaN"] = 11] = "NaN";
9860
+ Constants[Constants["PositiveInfinity"] = 12] = "PositiveInfinity";
9861
+ Constants[Constants["NegativeInfinity"] = 13] = "NegativeInfinity";
9862
+ Constants[Constants["MaxSafeInt"] = 14] = "MaxSafeInt";
9700
9863
  // used for close fragment
9701
- Constants[Constants["AlmostMaxSafeInt"] = 14] = "AlmostMaxSafeInt";
9702
- Constants[Constants["MinSafeInt"] = 15] = "MinSafeInt";
9864
+ Constants[Constants["AlmostMaxSafeInt"] = 15] = "AlmostMaxSafeInt";
9865
+ Constants[Constants["MinSafeInt"] = 16] = "MinSafeInt";
9703
9866
  })(Constants || (Constants = {}));
9704
9867
 
9705
9868
  /** @internal */
9706
9869
  const verifySerializable = (value, preMessage) => {
9707
- const seen = new Set();
9870
+ const seen = new WeakSet();
9708
9871
  return _verifySerializable(value, seen, '_', preMessage);
9709
9872
  };
9710
9873
  const _verifySerializable = (value, seen, ctx, preMessage) => {
@@ -9713,10 +9876,12 @@ const _verifySerializable = (value, seen, ctx, preMessage) => {
9713
9876
  return value;
9714
9877
  }
9715
9878
  if (shouldSerialize(unwrapped)) {
9716
- if (seen.has(unwrapped)) {
9717
- return value;
9879
+ if (typeof unwrapped === 'object') {
9880
+ if (seen.has(unwrapped)) {
9881
+ return value;
9882
+ }
9883
+ seen.add(unwrapped);
9718
9884
  }
9719
- seen.add(unwrapped);
9720
9885
  if (isSignal(unwrapped)) {
9721
9886
  return value;
9722
9887
  }
@@ -9820,16 +9985,7 @@ const _weakSerialize = (input) => {
9820
9985
  return input;
9821
9986
  };
9822
9987
 
9823
- const isQrl = (value) => {
9824
- return typeof value === 'function' && typeof value.getSymbol === 'function';
9825
- };
9826
- // Make sure this value is same as value in `platform.ts`
9827
- const SYNC_QRL = '<sync>';
9828
- /** Sync QRL is a function which is serialized into `<script q:func="qwik/json">` tag. */
9829
- const isSyncQrl = (value) => {
9830
- return isQrl(value) && value.$symbol$ == SYNC_QRL;
9831
- };
9832
- const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refSymbol) => {
9988
+ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
9833
9989
  if (qDev && qSerialize) {
9834
9990
  if (captureRef) {
9835
9991
  for (const item of captureRef) {
@@ -9851,9 +10007,14 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9851
10007
  };
9852
10008
  function bindFnToContext(currentCtx, beforeFn) {
9853
10009
  // Note that we bind the current `this`
9854
- return (...args) => maybeThen(resolveLazy(), (fn) => {
9855
- if (!isFunction(fn)) {
9856
- throw qError(QError.qrlIsNotFunction);
10010
+ const bound = (...args) => {
10011
+ if (!qrl.resolved) {
10012
+ return qrl.resolve().then((fn) => {
10013
+ if (!isFunction(fn)) {
10014
+ throw qError(QError.qrlIsNotFunction);
10015
+ }
10016
+ return bound(...args);
10017
+ });
9857
10018
  }
9858
10019
  if (beforeFn && beforeFn() === false) {
9859
10020
  return;
@@ -9866,13 +10027,14 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9866
10027
  context.$qrl$ = qrl;
9867
10028
  context.$event$ ||= this;
9868
10029
  try {
9869
- return invoke.call(this, context, fn, ...args);
10030
+ return invoke.call(this, context, symbolRef, ...args);
9870
10031
  }
9871
10032
  finally {
9872
10033
  context.$qrl$ = prevQrl;
9873
10034
  context.$event$ = prevEvent;
9874
10035
  }
9875
- });
10036
+ };
10037
+ return bound;
9876
10038
  }
9877
10039
  const resolveLazy = (containerEl) => {
9878
10040
  return symbolRef !== null ? symbolRef : resolve(containerEl);
@@ -9884,8 +10046,20 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9884
10046
  }
9885
10047
  return function (...args) {
9886
10048
  let context = tryGetInvokeContext();
10049
+ // use the given qrl if it is the right one
9887
10050
  if (context) {
9888
- return fn.apply(this, args);
10051
+ // TODO check if this is necessary in production
10052
+ if (context.$qrl$?.$symbol$ === qrl.$symbol$) {
10053
+ return fn.apply(this, args);
10054
+ }
10055
+ const prevQrl = context.$qrl$;
10056
+ context.$qrl$ = qrl;
10057
+ try {
10058
+ return fn.apply(this, args);
10059
+ }
10060
+ finally {
10061
+ context.$qrl$ = prevQrl;
10062
+ }
9889
10063
  }
9890
10064
  context = newInvokeContext();
9891
10065
  context.$qrl$ = qrl;
@@ -9913,11 +10087,11 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9913
10087
  const start = now();
9914
10088
  const ctx = tryGetInvokeContext();
9915
10089
  if (symbolFn !== null) {
9916
- symbolRef = symbolFn().then((module) => (qrl.resolved = symbolRef = wrapFn(module[symbol])));
10090
+ symbolRef = symbolFn().then((module) => (qrl.resolved = wrapFn((symbolRef = module[symbol]))));
9917
10091
  }
9918
10092
  else {
9919
10093
  const imported = getPlatform().importSymbol(_containerEl, chunk, symbol);
9920
- symbolRef = maybeThen(imported, (ref) => (qrl.resolved = symbolRef = wrapFn(ref)));
10094
+ symbolRef = maybeThen(imported, (ref) => (qrl.resolved = wrapFn((symbolRef = ref))));
9921
10095
  }
9922
10096
  if (typeof symbolRef === 'object' && isPromise(symbolRef)) {
9923
10097
  symbolRef.then(() => emitUsedSymbol(symbol, ctx?.$element$, start), (err) => {
@@ -9940,10 +10114,9 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9940
10114
  return invoke;
9941
10115
  }
9942
10116
  };
9943
- const resolvedSymbol = refSymbol ?? symbol;
9944
- const hash = getSymbolHash(resolvedSymbol);
10117
+ const hash = getSymbolHash(symbol);
9945
10118
  Object.assign(qrl, {
9946
- getSymbol: () => resolvedSymbol,
10119
+ getSymbol: () => symbol,
9947
10120
  getHash: () => hash,
9948
10121
  getCaptured: () => captureRef,
9949
10122
  resolve,
@@ -9951,7 +10124,6 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9951
10124
  $setContainer$: setContainer,
9952
10125
  $chunk$: chunk,
9953
10126
  $symbol$: symbol,
9954
- $refSymbol$: refSymbol,
9955
10127
  $hash$: hash,
9956
10128
  getFn: bindFnToContext,
9957
10129
  $capture$: capture,
@@ -9960,8 +10132,8 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9960
10132
  resolved: undefined,
9961
10133
  });
9962
10134
  if (symbolRef) {
9963
- // Replace symbolRef with (a promise for) the value or wrapped function
9964
- symbolRef = maybeThen(symbolRef, (resolved) => (qrl.resolved = symbolRef = wrapFn(resolved)));
10135
+ // Unwrap any promises
10136
+ symbolRef = maybeThen(symbolRef, (resolved) => (qrl.resolved = wrapFn((symbolRef = resolved))));
9965
10137
  }
9966
10138
  if (isDev) {
9967
10139
  Object.defineProperty(qrl, '_devOnlySymbolRef', {
@@ -9975,20 +10147,6 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9975
10147
  }
9976
10148
  return qrl;
9977
10149
  };
9978
- const getSymbolHash = (symbolName) => {
9979
- const index = symbolName.lastIndexOf('_');
9980
- if (index > -1) {
9981
- return symbolName.slice(index + 1);
9982
- }
9983
- return symbolName;
9984
- };
9985
- function assertQrl(qrl) {
9986
- if (qDev) {
9987
- if (!isQrl(qrl)) {
9988
- throw new Error('Not a QRL');
9989
- }
9990
- }
9991
- }
9992
10150
  const EMITTED = /*#__PURE__*/ new Set();
9993
10151
  const emitUsedSymbol = (symbol, element, reqTime) => {
9994
10152
  if (!EMITTED.has(symbol)) {
@@ -10101,7 +10259,7 @@ const $ = (expression) => {
10101
10259
  if (!qRuntimeQrl && qDev) {
10102
10260
  throw new Error('Optimizer should replace all usages of $() with some special syntax. If you need to create a QRL manually, use inlinedQrl() instead.');
10103
10261
  }
10104
- return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null, null);
10262
+ return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null);
10105
10263
  };
10106
10264
  /** @private Use To avoid optimizer replacement */
10107
10265
  const dollar = $;
@@ -10127,7 +10285,7 @@ const sync$ = (fn) => {
10127
10285
  // eslint-disable-next-line no-new-func
10128
10286
  fn = new Function('return ' + fn.toString())();
10129
10287
  }
10130
- return createQRL('', SYNC_QRL, fn, null, null, null, null);
10288
+ return createQRL('', SYNC_QRL, fn, null, null, null);
10131
10289
  };
10132
10290
  /**
10133
10291
  * Extract function into a synchronously loadable QRL.
@@ -10144,7 +10302,7 @@ const _qrlSync = function (fn, serializedFn) {
10144
10302
  serializedFn = fn.toString();
10145
10303
  }
10146
10304
  fn.serialized = serializedFn;
10147
- return createQRL('', SYNC_QRL, fn, null, null, null, null);
10305
+ return createQRL('', SYNC_QRL, fn, null, null, null);
10148
10306
  };
10149
10307
 
10150
10308
  /** @internal */
@@ -10863,6 +11021,21 @@ const useVisibleTaskQrl = (qrl, opts) => {
10863
11021
  iCtx.$container$.$scheduler$(ChoreType.VISIBLE, task);
10864
11022
  }
10865
11023
  };
11024
+ const useRunTask = (task, eagerness) => {
11025
+ if (eagerness === 'intersection-observer') {
11026
+ useOn('qvisible', getTaskHandlerQrl(task));
11027
+ }
11028
+ else if (eagerness === 'document-ready') {
11029
+ useOnDocument('qinit', getTaskHandlerQrl(task));
11030
+ }
11031
+ else if (eagerness === 'document-idle') {
11032
+ useOnDocument('qidle', getTaskHandlerQrl(task));
11033
+ }
11034
+ };
11035
+ const getTaskHandlerQrl = (task) => {
11036
+ const taskHandler = createQRL(null, '_task', scheduleTask, null, null, [task]);
11037
+ return taskHandler;
11038
+ };
10866
11039
 
10867
11040
  // <docs markdown="../readme.md#useResource">
10868
11041
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
@@ -11173,5 +11346,5 @@ const PrefetchGraph = (opts = {}) => {
11173
11346
  return _jsxSorted('script', null, props, null, 0, 'prefetch-graph');
11174
11347
  };
11175
11348
 
11176
- export { $, Fragment, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, EMPTY_ARRAY as _EMPTY_ARRAY, EffectPropData as _EffectData, _IMMUTABLE, _SharedContainer, _VAR_PROPS, _deserialize, _fnSignal, _getContextElement, _getContextEvent, getDomContainer as _getDomContainer, _getQContainerElement, _hW, isJSXNode as _isJSXNode, isStringifiable as _isStringifiable, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, _noopQrl, _noopQrlDEV, _qrlSync, _regSymbol, _restProps, _serialize, verifySerializable as _verifySerializable, _waitUntilRendered, _walkJSX, _weakSerialize, _wrapProp, _wrapSignal, component$, componentQrl, createComputed$, createComputedQrl, createContextId, h as createElement, createSignal, event$, eventQrl, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsx as jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
11349
+ export { $, Fragment, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, SubscriptionData as _EffectData, _IMMUTABLE, _SharedContainer, _VAR_PROPS, _deserialize, _fnSignal, _getContextElement, _getContextEvent, getDomContainer as _getDomContainer, _getQContainerElement, isJSXNode as _isJSXNode, isStringifiable as _isStringifiable, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, _noopQrl, _noopQrlDEV, _qrlSync, _regSymbol, _restProps, queueQRL as _run, _serialize, scheduleTask as _task, verifySerializable as _verifySerializable, _waitUntilRendered, _walkJSX, _weakSerialize, _wrapProp, _wrapSignal, component$, componentQrl, createComputed$, createComputedQrl, createContextId, h as createElement, createSignal, event$, eventQrl, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsx as jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
11177
11350
  //# sourceMappingURL=core.mjs.map