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

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 +92 -78
  9. package/dist/core.cjs +1603 -1374
  10. package/dist/core.cjs.map +1 -1
  11. package/dist/core.min.mjs +1 -1
  12. package/dist/core.mjs +1600 -1374
  13. package/dist/core.mjs.map +1 -1
  14. package/dist/core.prod.cjs +963 -834
  15. package/dist/core.prod.mjs +1093 -927
  16. package/dist/insights/index.qwik.cjs +3679 -167
  17. package/dist/insights/index.qwik.mjs +3679 -167
  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 +228 -5715
  22. package/dist/optimizer.mjs +208 -6038
  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 +787 -7152
  27. package/dist/server.mjs +805 -7148
  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 +1341 -1180
  45. package/dist/testing/index.mjs +1354 -1186
  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.8-dev+66037b5
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,11 +310,10 @@ 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
- const QSlotParent = ':';
295
- const QSlotRef = 'q:sref';
316
+ const QSlotParent = 'q:sparent';
296
317
  const QSlotS = 'q:s';
297
318
  const QStyle = 'q:style';
298
319
  const QStyleSelector = 'style[q\\:style]';
@@ -300,7 +321,7 @@ const QStyleSSelector = 'style[q\\:sstyle]';
300
321
  const QStylesAllSelector = QStyleSelector + ',' + QStyleSSelector;
301
322
  const QScopedStyle = 'q:sstyle';
302
323
  const QCtxAttr = 'q:ctx';
303
- const QSubscribers = 'q:subs';
324
+ const QBackRefs = 'q:brefs';
304
325
  const QFuncsPrefix = 'qFuncs_';
305
326
  const getQFuncs = (document, hash) => {
306
327
  return document[QFuncsPrefix + hash] || [];
@@ -329,6 +350,7 @@ const MATH_NS = 'http://www.w3.org/1998/Math/MathML';
329
350
  const ResourceEvent = 'qResource';
330
351
  const RenderEvent = 'qRender';
331
352
  const TaskEvent = 'qTask';
353
+ /** `<q:slot name="...">` */
332
354
  const QDefaultSlot = '';
333
355
  /**
334
356
  * Attribute to mark that this VNode has a pointer to itself from the `qwik/json` state.
@@ -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,40 +1147,10 @@ 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');
1051
- const STORE_ARRAY_PROP = Symbol('store.array');
1153
+ const STORE_ALL_PROPS = Symbol('store.all');
1052
1154
  var StoreFlags;
1053
1155
  (function (StoreFlags) {
1054
1156
  StoreFlags[StoreFlags["NONE"] = 0] = "NONE";
@@ -1123,7 +1225,7 @@ class StoreHandler {
1123
1225
  }
1124
1226
  const effectSubscriber = ctx.$effectSubscriber$;
1125
1227
  if (effectSubscriber) {
1126
- addEffect(target, Array.isArray(target) ? STORE_ARRAY_PROP : prop, this, effectSubscriber);
1228
+ addStoreEffect(target, Array.isArray(target) ? STORE_ALL_PROPS : prop, this, effectSubscriber);
1127
1229
  }
1128
1230
  }
1129
1231
  if (prop === 'toString' && value === Object.prototype.toString) {
@@ -1174,7 +1276,7 @@ class StoreHandler {
1174
1276
  if (ctx) {
1175
1277
  const effectSubscriber = ctx.$effectSubscriber$;
1176
1278
  if (effectSubscriber) {
1177
- addEffect(target, Array.isArray(target) ? STORE_ARRAY_PROP : prop, this, effectSubscriber);
1279
+ addStoreEffect(target, Array.isArray(target) ? STORE_ALL_PROPS : prop, this, effectSubscriber);
1178
1280
  }
1179
1281
  }
1180
1282
  }
@@ -1184,13 +1286,17 @@ class StoreHandler {
1184
1286
  const ctx = tryGetInvokeContext();
1185
1287
  const effectSubscriber = ctx?.$effectSubscriber$;
1186
1288
  if (effectSubscriber) {
1187
- addEffect(target, STORE_ARRAY_PROP, this, effectSubscriber);
1289
+ addStoreEffect(target, STORE_ALL_PROPS, this, effectSubscriber);
1188
1290
  }
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 addStoreEffect(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_ALL_PROPS);
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)) {
@@ -1558,116 +1589,91 @@ const runResource = (task, container, host) => {
1558
1589
  else if (isSignal(obj)) {
1559
1590
  return obj.value;
1560
1591
  }
1561
- else {
1592
+ else if (isStore(obj)) {
1593
+ // track whole store
1594
+ addStoreEffect(getStoreTarget(obj), STORE_ALL_PROPS, getStoreHandler(obj), ctx.$effectSubscriber$);
1562
1595
  return obj;
1563
1596
  }
1597
+ else {
1598
+ throw qError(QError.trackObjectWithoutProp);
1599
+ }
1564
1600
  });
1565
1601
  };
1566
1602
  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);
1603
+ let cleanupFns = null;
1604
+ const cleanup = (fn) => {
1605
+ if (typeof fn == 'function') {
1606
+ if (!cleanupFns) {
1607
+ cleanupFns = [];
1608
+ task.$destroy$ = noSerialize(() => {
1609
+ task.$destroy$ = null;
1610
+ cleanupFns.forEach((fn) => {
1611
+ try {
1612
+ fn();
1613
+ }
1614
+ catch (err) {
1615
+ handleError(err);
1616
+ }
1617
+ });
1618
+ });
1575
1619
  }
1576
- });
1577
- done = true;
1620
+ cleanupFns.push(fn);
1621
+ }
1622
+ };
1623
+ const taskApi = { track, cleanup };
1624
+ const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
1625
+ // If a Promise is thrown, that means we need to re-run the task.
1626
+ if (isPromise(err)) {
1627
+ return err.then(() => runTask(task, container, host));
1628
+ }
1629
+ else {
1630
+ throw err;
1631
+ }
1578
1632
  });
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));
1633
+ return result;
1634
+ };
1635
+ const cleanupTask = (task) => {
1636
+ const destroy = task.$destroy$;
1637
+ if (destroy) {
1638
+ task.$destroy$ = null;
1639
+ try {
1640
+ destroy();
1650
1641
  }
1651
- else {
1652
- setState(false, err);
1642
+ catch (err) {
1643
+ logError(err);
1653
1644
  }
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
1645
  }
1666
- return promise;
1667
1646
  };
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.
1647
+ class Task extends BackRef {
1648
+ $flags$;
1649
+ $index$;
1650
+ $el$;
1651
+ $qrl$;
1652
+ $state$;
1653
+ $destroy$;
1654
+ constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
1655
+ super();
1656
+ this.$flags$ = $flags$;
1657
+ this.$index$ = $index$;
1658
+ this.$el$ = $el$;
1659
+ this.$qrl$ = $qrl$;
1660
+ this.$state$ = $state$;
1661
+ this.$destroy$ = $destroy$;
1662
+ }
1663
+ }
1664
+ const isTask = (value) => {
1665
+ return value instanceof Task;
1666
+ };
1667
+ /**
1668
+ * Used internally as a qrl event handler to schedule a task.
1669
+ *
1670
+ * @internal
1671
+ */
1672
+ const scheduleTask = (_event, element) => {
1673
+ const [task] = useLexicalScope();
1674
+ const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK;
1675
+ const container = getDomContainer(element);
1676
+ container.$scheduler$(type, task);
1671
1677
  };
1672
1678
 
1673
1679
  /** @file Public types for the client deserialization */
@@ -1745,22 +1751,67 @@ var VirtualVNodeProps;
1745
1751
  VirtualVNodeProps[VirtualVNodeProps["PROPS_OFFSET"] = 6] = "PROPS_OFFSET";
1746
1752
  })(VirtualVNodeProps || (VirtualVNodeProps = {}));
1747
1753
 
1748
- const isForeignObjectElement = (elementName) => elementName.toLowerCase() === 'foreignobject';
1754
+ const mapApp_findIndx = (array, key, start) => {
1755
+ assertTrue(start % 2 === 0, 'Expecting even number.');
1756
+ let bottom = start >> 1;
1757
+ let top = (array.length - 2) >> 1;
1758
+ while (bottom <= top) {
1759
+ const mid = bottom + ((top - bottom) >> 1);
1760
+ const midKey = array[mid << 1];
1761
+ if (midKey === key) {
1762
+ return mid << 1;
1763
+ }
1764
+ if (midKey < key) {
1765
+ bottom = mid + 1;
1766
+ }
1767
+ else {
1768
+ top = mid - 1;
1769
+ }
1770
+ }
1771
+ return (bottom << 1) ^ -1;
1772
+ };
1773
+ const mapArray_set = (array, key, value, start) => {
1774
+ const indx = mapApp_findIndx(array, key, start);
1775
+ if (indx >= 0) {
1776
+ if (value == null) {
1777
+ array.splice(indx, 2);
1778
+ }
1779
+ else {
1780
+ array[indx + 1] = value;
1781
+ }
1782
+ }
1783
+ else if (value != null) {
1784
+ array.splice(indx ^ -1, 0, key, value);
1785
+ }
1786
+ };
1787
+ const mapArray_get = (array, key, start) => {
1788
+ const indx = mapApp_findIndx(array, key, start);
1789
+ if (indx >= 0) {
1790
+ return array[indx + 1];
1791
+ }
1792
+ else {
1793
+ return null;
1794
+ }
1795
+ };
1796
+
1797
+ const isForeignObjectElement = (elementName) => {
1798
+ return isDev ? elementName.toLowerCase() === 'foreignobject' : elementName === 'foreignObject';
1799
+ };
1749
1800
  const isSvgElement = (elementName) => elementName === 'svg' || isForeignObjectElement(elementName);
1750
1801
  const isMathElement = (elementName) => elementName === 'math';
1751
1802
  const vnode_isDefaultNamespace = (vnode) => {
1752
1803
  const flags = vnode[VNodeProps.flags];
1753
1804
  return (flags & VNodeFlags.NAMESPACE_MASK) === 0;
1754
1805
  };
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;
1806
+ const vnode_getElementNamespaceFlags = (element) => {
1807
+ const namespace = fastNamespaceURI(element);
1808
+ switch (namespace) {
1809
+ case SVG_NS:
1810
+ return VNodeFlags.NS_svg;
1811
+ case MATH_NS:
1812
+ return VNodeFlags.NS_math;
1813
+ default:
1814
+ return VNodeFlags.NS_html;
1764
1815
  }
1765
1816
  };
1766
1817
  function vnode_getDomChildrenWithCorrectNamespacesToInsert(journal, domParentVNode, newChild) {
@@ -2114,19 +2165,21 @@ const useOnEventsSequentialScope = () => {
2114
2165
  * @returns
2115
2166
  */
2116
2167
  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;
2168
+ const iCtx = newInvokeContext(container.$locale$, subscriptionHost || undefined, undefined, RenderEvent);
2169
+ if (subscriptionHost) {
2170
+ iCtx.$effectSubscriber$ = getSubscriber(subscriptionHost, EffectProperty.COMPONENT);
2171
+ iCtx.$container$ = container;
2172
+ }
2120
2173
  let componentFn;
2121
2174
  container.ensureProjectionResolved(renderHost);
2122
2175
  let isInlineComponent = false;
2123
2176
  if (componentQRL === null) {
2124
- componentQRL = componentQRL || container.getHostProp(renderHost, OnRenderProp);
2177
+ componentQRL = container.getHostProp(renderHost, OnRenderProp);
2125
2178
  assertDefined(componentQRL, 'No Component found at this location');
2126
2179
  }
2127
- if (isQrl(componentQRL)) {
2180
+ if (isQrl$1(componentQRL)) {
2128
2181
  props = props || container.getHostProp(renderHost, ELEMENT_PROPS) || EMPTY_OBJ;
2129
- if (props && props.children) {
2182
+ if (props.children) {
2130
2183
  delete props.children;
2131
2184
  }
2132
2185
  componentFn = componentQRL.getFn(iCtx);
@@ -2144,18 +2197,16 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2144
2197
  if (!isInlineComponent) {
2145
2198
  container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
2146
2199
  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
- }
2200
+ container.setHostProp(renderHost, ELEMENT_PROPS, props);
2150
2201
  }
2151
2202
  if (vnode_isVNode(renderHost)) {
2152
- clearVNodeEffectDependencies(container, renderHost);
2203
+ clearAllEffects(container, renderHost);
2153
2204
  }
2154
2205
  return componentFn(props);
2155
2206
  }, (jsx) => {
2156
2207
  const useOnEvents = container.getHostProp(renderHost, USE_ON_LOCAL);
2157
2208
  if (useOnEvents) {
2158
- return maybeThen(addUseOnEvents(jsx, useOnEvents), () => jsx);
2209
+ return addUseOnEvents(jsx, useOnEvents);
2159
2210
  }
2160
2211
  return jsx;
2161
2212
  }, (err) => {
@@ -2181,6 +2232,7 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2181
2232
  */
2182
2233
  function addUseOnEvents(jsx, useOnEvents) {
2183
2234
  const jsxElement = findFirstStringJSX(jsx);
2235
+ let jsxResult = jsx;
2184
2236
  return maybeThen(jsxElement, (jsxElement) => {
2185
2237
  let isInvisibleComponent = false;
2186
2238
  if (!jsxElement) {
@@ -2199,13 +2251,15 @@ function addUseOnEvents(jsx, useOnEvents) {
2199
2251
  if (Object.prototype.hasOwnProperty.call(useOnEvents, key)) {
2200
2252
  if (isInvisibleComponent) {
2201
2253
  if (key === 'onQvisible$') {
2202
- jsxElement = addScriptNodeForInvisibleComponents(jsx);
2254
+ const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2255
+ jsxResult = jsx;
2203
2256
  if (jsxElement) {
2204
2257
  addUseOnEvent(jsxElement, 'document:onQinit$', useOnEvents[key]);
2205
2258
  }
2206
2259
  }
2207
2260
  else if (key.startsWith('document:') || key.startsWith('window:')) {
2208
- jsxElement = addScriptNodeForInvisibleComponents(jsx);
2261
+ const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2262
+ jsxResult = jsx;
2209
2263
  if (jsxElement) {
2210
2264
  addUseOnEvent(jsxElement, key, useOnEvents[key]);
2211
2265
  }
@@ -2223,7 +2277,7 @@ function addUseOnEvents(jsx, useOnEvents) {
2223
2277
  }
2224
2278
  }
2225
2279
  }
2226
- return jsxElement;
2280
+ return jsxResult;
2227
2281
  });
2228
2282
  }
2229
2283
  function addUseOnEvent(jsxElement, key, value) {
@@ -2269,6 +2323,9 @@ function addScriptNodeForInvisibleComponents(jsx) {
2269
2323
  type: 'placeholder',
2270
2324
  hidden: '',
2271
2325
  }, null, 3);
2326
+ if (jsx.type === Slot) {
2327
+ return [jsxElement, _jsxSorted(Fragment, null, null, [jsx, jsxElement], 0, null)];
2328
+ }
2272
2329
  if (jsx.children == null) {
2273
2330
  jsx.children = jsxElement;
2274
2331
  }
@@ -2278,29 +2335,46 @@ function addScriptNodeForInvisibleComponents(jsx) {
2278
2335
  else {
2279
2336
  jsx.children = [jsx.children, jsxElement];
2280
2337
  }
2281
- return jsxElement;
2338
+ return [jsxElement, jsx];
2282
2339
  }
2283
2340
  else if (Array.isArray(jsx) && jsx.length) {
2284
2341
  // get first element
2285
- return addScriptNodeForInvisibleComponents(jsx[0]);
2342
+ const [jsxElement, _] = addScriptNodeForInvisibleComponents(jsx[0]);
2343
+ return [jsxElement, jsx];
2286
2344
  }
2287
- return null;
2345
+ return [null, null];
2288
2346
  }
2289
2347
 
2348
+ /** @internal */
2349
+ const _CONST_PROPS = Symbol('CONST');
2350
+ /** @internal */
2351
+ const _VAR_PROPS = Symbol('VAR');
2352
+ /** @internal @deprecated v1 compat */
2353
+ const _IMMUTABLE = Symbol('IMMUTABLE');
2354
+
2290
2355
  function isSlotProp(prop) {
2291
2356
  return !prop.startsWith('q:') && !prop.startsWith(NON_SERIALIZABLE_MARKER_PREFIX);
2292
2357
  }
2293
- function isParentSlotProp(prop) {
2294
- return prop.startsWith(QSlotParent);
2295
- }
2296
2358
  /** @internal */
2297
2359
  const _restProps = (props, omit, target = {}) => {
2298
- for (const key in props) {
2360
+ let constPropsTarget = null;
2361
+ const constProps = props[_CONST_PROPS];
2362
+ if (constProps) {
2363
+ for (const key in constProps) {
2364
+ if (!omit.includes(key)) {
2365
+ constPropsTarget ||= {};
2366
+ constPropsTarget[key] = constProps[key];
2367
+ }
2368
+ }
2369
+ }
2370
+ const varPropsTarget = target;
2371
+ const varProps = props[_VAR_PROPS];
2372
+ for (const key in varProps) {
2299
2373
  if (!omit.includes(key)) {
2300
- target[key] = props[key];
2374
+ varPropsTarget[key] = varProps[key];
2301
2375
  }
2302
2376
  }
2303
- return target;
2377
+ return createPropsProxy(varPropsTarget, constPropsTarget);
2304
2378
  };
2305
2379
 
2306
2380
  function escapeHTML(html) {
@@ -2342,17 +2416,6 @@ function escapeHTML(html) {
2342
2416
  }
2343
2417
  }
2344
2418
 
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
2419
  const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2357
2420
  let journal = container.$journal$;
2358
2421
  /**
@@ -2371,9 +2434,9 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2371
2434
  /// (Node can be null, if we are at the end of the list.)
2372
2435
  let vCurrent = null;
2373
2436
  /// 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
2437
+ /// NOTE: it can't be stored in `vCurrent` because `vNewNode` is in journal
2375
2438
  /// and is not connected to the tree.
2376
- let vNewNode = null; // TODO: delete, because journal is on vNode, the above comment no longer applies
2439
+ let vNewNode = null;
2377
2440
  /// When elements have keys they can be consumed out of order and therefore we can't use nextSibling.
2378
2441
  /// In such a case this array will contain the elements after the current location.
2379
2442
  /// The array even indices will contains keys and odd indices the vNode.
@@ -2426,7 +2489,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2426
2489
  }
2427
2490
  else if (isSignal(jsxValue)) {
2428
2491
  if (vCurrent) {
2429
- clearVNodeEffectDependencies(container, vCurrent);
2492
+ clearAllEffects(container, vCurrent);
2430
2493
  }
2431
2494
  expectVirtual(VirtualType.WrappedSignal, null);
2432
2495
  descend(trackSignalAndAssignHost(jsxValue, (vNewNode || vCurrent), EffectProperty.VNODE, container), true);
@@ -2641,9 +2704,10 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2641
2704
  };
2642
2705
  const projections = [];
2643
2706
  if (host) {
2707
+ const props = vnode_getProps(host);
2644
2708
  // 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];
2709
+ for (let i = 0; i < props.length; i = i + 2) {
2710
+ const prop = props[i];
2647
2711
  if (isSlotProp(prop)) {
2648
2712
  const slotName = prop;
2649
2713
  projections.push(slotName);
@@ -2684,6 +2748,8 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2684
2748
  // console.log('expectProjection', JSON.stringify(slotName));
2685
2749
  vCurrent = vnode_getProp(vParent, // The parent is the component and it should have our portal.
2686
2750
  slotName, (id) => vnode_locate(container.rootVNode, id));
2751
+ // if projection is marked as deleted then we need to create a new one
2752
+ vCurrent = vCurrent && vCurrent[VNodeProps.flags] & VNodeFlags.Deleted ? null : vCurrent;
2687
2753
  if (vCurrent == null) {
2688
2754
  vNewNode = vnode_newVirtual();
2689
2755
  // you may be tempted to add the projection into the current parent, but
@@ -2720,6 +2786,19 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2720
2786
  }
2721
2787
  else if (vProjectedNode === vCurrent) ;
2722
2788
  else {
2789
+ const parent = vnode_getParent(vProjectedNode);
2790
+ const isAlreadyProjected = !!parent && !(vnode_isElementVNode(parent) && vnode_getElementName(parent) === QTemplate);
2791
+ if (isAlreadyProjected && vParent !== parent) {
2792
+ /**
2793
+ * The node is already projected, but structure has been changed. In next steps we will
2794
+ * insert the vProjectedNode at the end. However we will find existing projection elements
2795
+ * (from already projected THE SAME projection as vProjectedNode!) during
2796
+ * vnode_insertBefore. We need to remove vnode from the vnode tree to avoid referencing it
2797
+ * to self and cause infinite loop. Don't remove it from DOM to avoid additional operations
2798
+ * and flickering.
2799
+ */
2800
+ vnode_remove(journal, parent, vProjectedNode, false);
2801
+ }
2723
2802
  // move from q:template to the target node
2724
2803
  vnode_insertBefore(journal, vParent, (vNewNode = vProjectedNode), vCurrent && getInsertBefore());
2725
2804
  vnode_setProp(vNewNode, QSlot, slotNameKey);
@@ -2773,8 +2852,8 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2773
2852
  while (vCurrent) {
2774
2853
  const toRemove = vCurrent;
2775
2854
  advanceToNextSibling();
2776
- cleanup(container, toRemove);
2777
2855
  if (vParent === vnode_getParent(toRemove)) {
2856
+ cleanup(container, toRemove);
2778
2857
  // If we are diffing projection than the parent is not the parent of the node.
2779
2858
  // If that is the case we don't want to remove the node from the parent.
2780
2859
  vnode_remove(journal, vParent, toRemove, true);
@@ -2811,10 +2890,19 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2811
2890
  // But we need to mark them so that they don't get pulled into the diff.
2812
2891
  const eventName = getEventNameFromJsxProp(key);
2813
2892
  const scope = getEventNameScopeFromJsxProp(key);
2814
- vnode_setProp(vNewNode, HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
2815
2893
  if (eventName) {
2894
+ vnode_setProp(vNewNode, HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
2816
2895
  registerQwikLoaderEvent(eventName);
2817
2896
  }
2897
+ if (scope) {
2898
+ // add an event attr with empty value for qwikloader element selector.
2899
+ // We don't need value here. For ssr this value is a QRL,
2900
+ // but for CSR value should be just empty
2901
+ const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
2902
+ if (htmlEvent) {
2903
+ vnode_setAttr(journal, vNewNode, htmlEvent, '');
2904
+ }
2905
+ }
2818
2906
  needsQDispatchEventPatch = true;
2819
2907
  continue;
2820
2908
  }
@@ -2827,12 +2915,15 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2827
2915
  value(element);
2828
2916
  continue;
2829
2917
  }
2918
+ else if (value == null) {
2919
+ continue;
2920
+ }
2830
2921
  else {
2831
2922
  throw qError(QError.invalidRefValue, [currentFile]);
2832
2923
  }
2833
2924
  }
2834
2925
  if (isSignal(value)) {
2835
- const signalData = new EffectPropData({
2926
+ const signalData = new SubscriptionData({
2836
2927
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
2837
2928
  $isConst$: true,
2838
2929
  });
@@ -2936,7 +3027,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2936
3027
  let returnValue = false;
2937
3028
  qrls.flat(2).forEach((qrl) => {
2938
3029
  if (qrl) {
2939
- const value = qrl(event, element);
3030
+ const value = container.$scheduler$(ChoreType.RUN_QRL, vNode, qrl, [event, element]);
2940
3031
  returnValue = returnValue || value === true;
2941
3032
  }
2942
3033
  });
@@ -2948,10 +3039,10 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2948
3039
  /** @param tag Returns true if `qDispatchEvent` needs patching */
2949
3040
  function setBulkProps(vnode, srcAttrs, currentFile) {
2950
3041
  vnode_ensureElementInflated(vnode);
2951
- const dstAttrs = vnode;
3042
+ const dstAttrs = vnode_getProps(vnode);
2952
3043
  let srcIdx = 0;
2953
3044
  const srcLength = srcAttrs.length;
2954
- let dstIdx = ElementVNodeProps.PROPS_OFFSET;
3045
+ let dstIdx = 0;
2955
3046
  let dstLength = dstAttrs.length;
2956
3047
  let srcKey = srcIdx < srcLength ? srcAttrs[srcIdx++] : null;
2957
3048
  let dstKey = dstIdx < dstLength ? dstAttrs[dstIdx++] : null;
@@ -2971,12 +3062,15 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2971
3062
  value(element);
2972
3063
  return;
2973
3064
  }
3065
+ else if (value == null) {
3066
+ return;
3067
+ }
2974
3068
  else {
2975
3069
  throw qError(QError.invalidRefValue, [currentFile]);
2976
3070
  }
2977
3071
  }
2978
3072
  if (isSignal(value)) {
2979
- const signalData = new EffectPropData({
3073
+ const signalData = new SubscriptionData({
2980
3074
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
2981
3075
  $isConst$: false,
2982
3076
  });
@@ -2990,21 +3084,21 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2990
3084
  };
2991
3085
  const recordJsxEvent = (key, value) => {
2992
3086
  const eventName = getEventNameFromJsxProp(key);
3087
+ const scope = getEventNameScopeFromJsxProp(key);
2993
3088
  if (eventName) {
2994
- const scope = getEventNameScopeFromJsxProp(key);
2995
3089
  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) {
3090
+ // register an event for qwik loader
3006
3091
  registerQwikLoaderEvent(eventName);
3007
3092
  }
3093
+ if (scope) {
3094
+ // add an event attr with empty value for qwikloader element selector.
3095
+ // We don't need value here. For ssr this value is a QRL,
3096
+ // but for CSR value should be just empty
3097
+ const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
3098
+ if (htmlEvent) {
3099
+ record(htmlEvent, '');
3100
+ }
3101
+ }
3008
3102
  };
3009
3103
  while (srcKey !== null || dstKey !== null) {
3010
3104
  if (dstKey?.startsWith(HANDLER_PREFIX) || dstKey?.startsWith(Q_PREFIX)) {
@@ -3199,14 +3293,8 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
3199
3293
  }
3200
3294
  else if (!hashesAreEqual) {
3201
3295
  insertNewComponent(host, componentQRL, jsxProps);
3202
- 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
- host = vNewNode;
3208
- shouldRender = true;
3209
- }
3296
+ host = vNewNode;
3297
+ shouldRender = true;
3210
3298
  }
3211
3299
  if (host) {
3212
3300
  const vNodeProps = vnode_getProp(host, ELEMENT_PROPS, container.$getObjectById$);
@@ -3227,6 +3315,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
3227
3315
  const lookupKey = jsxNode.key;
3228
3316
  const vNodeLookupKey = getKey(host);
3229
3317
  const lookupKeysAreEqual = lookupKey === vNodeLookupKey;
3318
+ const vNodeComponentHash = getComponentHash(host, container.$getObjectById$);
3230
3319
  if (!lookupKeysAreEqual) {
3231
3320
  // See if we already have this inline component later on.
3232
3321
  vNewNode = retrieveChildWithKey(null, lookupKey);
@@ -3240,6 +3329,11 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
3240
3329
  }
3241
3330
  host = vNewNode;
3242
3331
  }
3332
+ // inline components don't have component hash - q:renderFn prop, so it should be null
3333
+ else if (vNodeComponentHash != null) {
3334
+ insertNewInlineComponent();
3335
+ host = vNewNode;
3336
+ }
3243
3337
  if (host) {
3244
3338
  let componentHost = host;
3245
3339
  // Find the closest component host which has `OnRender` prop. This is need for subscriptions context.
@@ -3256,7 +3350,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
3256
3350
  }
3257
3351
  function insertNewComponent(host, componentQRL, jsxProps) {
3258
3352
  if (host) {
3259
- clearVNodeEffectDependencies(container, host);
3353
+ clearAllEffects(container, host);
3260
3354
  }
3261
3355
  vnode_insertBefore(journal, vParent, (vNewNode = vnode_newVirtual()), vCurrent && getInsertBefore());
3262
3356
  const jsxNode = jsxValue;
@@ -3344,8 +3438,8 @@ function propsDiffer(src, dst) {
3344
3438
  if (!src || !dst) {
3345
3439
  return true;
3346
3440
  }
3347
- let srcKeys = removePropsKeys(Object.keys(src), ['children', QSubscribers]);
3348
- let dstKeys = removePropsKeys(Object.keys(dst), ['children', QSubscribers]);
3441
+ let srcKeys = removePropsKeys(Object.keys(src), ['children', QBackRefs]);
3442
+ let dstKeys = removePropsKeys(Object.keys(dst), ['children', QBackRefs]);
3349
3443
  if (srcKeys.length !== dstKeys.length) {
3350
3444
  return true;
3351
3445
  }
@@ -3384,6 +3478,7 @@ function cleanup(container, vNode) {
3384
3478
  let vCursor = vNode;
3385
3479
  // Depth first traversal
3386
3480
  if (vnode_isTextVNode(vNode)) {
3481
+ markVNodeAsDeleted(vCursor);
3387
3482
  // Text nodes don't have subscriptions or children;
3388
3483
  return;
3389
3484
  }
@@ -3391,7 +3486,7 @@ function cleanup(container, vNode) {
3391
3486
  do {
3392
3487
  const type = vCursor[VNodeProps.flags];
3393
3488
  if (type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) {
3394
- clearVNodeEffectDependencies(container, vCursor);
3489
+ clearAllEffects(container, vCursor);
3395
3490
  markVNodeAsDeleted(vCursor);
3396
3491
  // Only elements and virtual nodes need to be traversed for children
3397
3492
  if (type & VNodeFlags.Virtual) {
@@ -3401,7 +3496,7 @@ function cleanup(container, vNode) {
3401
3496
  const obj = seq[i];
3402
3497
  if (isTask(obj)) {
3403
3498
  const task = obj;
3404
- clearSubscriberEffectDependencies(container, task);
3499
+ clearAllEffects(container, task);
3405
3500
  if (task.$flags$ & TaskFlags.VISIBLE_TASK) {
3406
3501
  container.$scheduler$(ChoreType.CLEANUP_VISIBLE, task);
3407
3502
  }
@@ -3416,10 +3511,10 @@ function cleanup(container, vNode) {
3416
3511
  vnode_getProp(vCursor, OnRenderProp, null) !== null;
3417
3512
  if (isComponent) {
3418
3513
  // 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) {
3514
+ const attrs = vnode_getProps(vCursor);
3515
+ for (let i = 0; i < attrs.length; i = i + 2) {
3421
3516
  const key = attrs[i];
3422
- if (!isParentSlotProp(key) && isSlotProp(key)) {
3517
+ if (isSlotProp(key)) {
3423
3518
  const value = attrs[i + 1];
3424
3519
  if (value) {
3425
3520
  attrs[i + 1] = null; // prevent infinite loop
@@ -3436,7 +3531,7 @@ function cleanup(container, vNode) {
3436
3531
  }
3437
3532
  }
3438
3533
  }
3439
- const isProjection = type & VNodeFlags.Virtual && vnode_getProp(vCursor, QSlot, null) !== null;
3534
+ const isProjection = vnode_isProjection(vCursor);
3440
3535
  // Descend into children
3441
3536
  if (!isProjection) {
3442
3537
  // Only if it is not a projection
@@ -3459,6 +3554,9 @@ function cleanup(container, vNode) {
3459
3554
  }
3460
3555
  }
3461
3556
  }
3557
+ else if (type & VNodeFlags.Text) {
3558
+ markVNodeAsDeleted(vCursor);
3559
+ }
3462
3560
  // Out of children
3463
3561
  if (vCursor === vNode) {
3464
3562
  // we are where we started, this means that vNode has no children, so we are done.
@@ -3525,82 +3623,270 @@ var SiblingsArray;
3525
3623
  SiblingsArray[SiblingsArray["NextVNode"] = 5] = "NextVNode";
3526
3624
  })(SiblingsArray || (SiblingsArray = {}));
3527
3625
 
3528
- // <docs markdown="../../readme.md#implicit$FirstArg">
3626
+ /** @internal */
3627
+ const useResourceQrl = (qrl, opts) => {
3628
+ const { val, set, i, iCtx } = useSequentialScope();
3629
+ if (val != null) {
3630
+ return val;
3631
+ }
3632
+ assertQrl(qrl);
3633
+ const container = iCtx.$container$;
3634
+ const resource = createResourceReturn(container, opts);
3635
+ const el = iCtx.$hostElement$;
3636
+ const task = new Task(TaskFlags.DIRTY | TaskFlags.RESOURCE, i, el, qrl, resource, null);
3637
+ container.$scheduler$(ChoreType.TASK, task);
3638
+ set(resource);
3639
+ return resource;
3640
+ };
3641
+ // <docs markdown="../readme.md#useResource">
3529
3642
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
3530
- // (edit ../../readme.md#implicit$FirstArg instead and run `pnpm docs.sync`)
3643
+ // (edit ../readme.md#useResource instead and run `pnpm docs.sync`)
3531
3644
  /**
3532
- * Create a `____$(...)` convenience method from `___(...)`.
3645
+ * This method works like an async memoized function that runs whenever some tracked value changes
3646
+ * and returns some data.
3533
3647
  *
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 `$`.
3648
+ * `useResource` however returns immediate a `ResourceReturn` object that contains the data and a
3649
+ * state that indicates if the data is available or not.
3537
3650
  *
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.
3651
+ * The status can be one of the following:
3540
3652
  *
3541
- * For example, these function calls are equivalent:
3653
+ * - `pending` - the data is not yet available.
3654
+ * - `resolved` - the data is available.
3655
+ * - `rejected` - the data is not available due to an error or timeout.
3542
3656
  *
3543
- * - `component$(() => {...})` is same as `component($(() => {...}))`
3657
+ * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
3658
+ * re-throw it (or a new Error), the resource status will never be `rejected`.
3544
3659
  *
3545
- * ```tsx
3546
- * export function myApi(callback: QRL<() => void>): void {
3547
- * // ...
3548
- * }
3660
+ * ### Example
3549
3661
  *
3550
- * export const myApi$ = implicit$FirstArg(myApi);
3551
- * // type of myApi$: (callback: () => void): void
3662
+ * Example showing how `useResource` to perform a fetch to request the weather, whenever the input
3663
+ * city name changes.
3552
3664
  *
3553
- * // can be used as:
3554
- * myApi$(() => console.log('callback'));
3665
+ * ```tsx
3666
+ * const Cmp = component$(() => {
3667
+ * const cityS = useSignal('');
3555
3668
  *
3556
- * // will be transpiled to:
3557
- * // FILE: <current file>
3558
- * myApi(qrl('./chunk-abc.js', 'callback'));
3669
+ * const weatherResource = useResource$(async ({ track, cleanup }) => {
3670
+ * const cityName = track(cityS);
3671
+ * const abortController = new AbortController();
3672
+ * cleanup(() => abortController.abort('cleanup'));
3673
+ * const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
3674
+ * signal: abortController.signal,
3675
+ * });
3676
+ * const data = await res.json();
3677
+ * return data as { temp: number };
3678
+ * });
3559
3679
  *
3560
- * // FILE: chunk-abc.js
3561
- * export const callback = () => console.log('callback');
3680
+ * return (
3681
+ * <div>
3682
+ * <input name="city" bind:value={cityS} />
3683
+ * <Resource
3684
+ * value={weatherResource}
3685
+ * onResolved={(weather) => {
3686
+ * return <div>Temperature: {weather.temp}</div>;
3687
+ * }}
3688
+ * />
3689
+ * </div>
3690
+ * );
3691
+ * });
3562
3692
  * ```
3563
3693
  *
3564
- * @param fn - A function that should have its first argument automatically `$`.
3565
3694
  * @public
3695
+ * @see Resource
3696
+ * @see ResourceReturn
3566
3697
  */
3567
3698
  // </docs>
3568
- const implicit$FirstArg = (fn) => {
3569
- return function (first, ...rest) {
3570
- return fn.call(null, dollar(first), ...rest);
3699
+ const Resource = (props) => {
3700
+ // Resource path
3701
+ return _jsxSorted(Fragment, null, null, getResourceValueAsPromise(props), 0, null);
3702
+ };
3703
+ function getResourceValueAsPromise(props) {
3704
+ const resource = props.value;
3705
+ if (isResourceReturn(resource) && resource.value) {
3706
+ const isBrowser = !isServerPlatform();
3707
+ if (isBrowser) {
3708
+ // create a subscription for the resource._state changes
3709
+ const state = resource._state;
3710
+ if (state === 'pending' && props.onPending) {
3711
+ return Promise.resolve().then(useBindInvokeContext(props.onPending));
3712
+ }
3713
+ else if (state === 'rejected' && props.onRejected) {
3714
+ return Promise.resolve(resource._error).then(useBindInvokeContext(props.onRejected));
3715
+ }
3716
+ else {
3717
+ const resolvedValue = untrack(() => resource._resolved);
3718
+ if (resolvedValue !== undefined) {
3719
+ // resolved, pending without onPending prop or rejected without onRejected prop
3720
+ return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved));
3721
+ }
3722
+ }
3723
+ }
3724
+ return resource.value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3725
+ }
3726
+ else if (isPromise(resource)) {
3727
+ return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3728
+ }
3729
+ else if (isSignal(resource)) {
3730
+ return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3731
+ }
3732
+ else {
3733
+ return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3734
+ }
3735
+ }
3736
+ const _createResourceReturn = (opts) => {
3737
+ const resource = {
3738
+ __brand: 'resource',
3739
+ value: undefined,
3740
+ loading: isServerPlatform() ? false : true,
3741
+ _resolved: undefined,
3742
+ _error: undefined,
3743
+ _state: 'pending',
3744
+ _timeout: opts?.timeout ?? -1,
3745
+ _cache: 0,
3571
3746
  };
3747
+ return resource;
3572
3748
  };
3573
-
3574
- const createSignal$1 = (value) => {
3575
- return new Signal(null, value);
3749
+ const createResourceReturn = (container, opts, initialPromise) => {
3750
+ const result = _createResourceReturn(opts);
3751
+ result.value = initialPromise;
3752
+ return createStore(container, result, StoreFlags.RECURSIVE);
3576
3753
  };
3577
- const createComputedSignal = (qrl) => {
3578
- throwIfQRLNotResolved(qrl);
3579
- return new ComputedSignal(null, qrl);
3754
+ const isResourceReturn = (obj) => {
3755
+ return isObject(obj) && (getStoreTarget(obj) || obj).__brand === 'resource';
3756
+ };
3757
+ const runResource = (task, container, host) => {
3758
+ task.$flags$ &= ~TaskFlags.DIRTY;
3759
+ cleanupTask(task);
3760
+ const iCtx = newInvokeContext(container.$locale$, host, undefined, ResourceEvent);
3761
+ iCtx.$container$ = container;
3762
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
3763
+ const resource = task.$state$;
3764
+ assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
3765
+ const track = (obj, prop) => {
3766
+ const ctx = newInvokeContext();
3767
+ ctx.$effectSubscriber$ = getSubscriber(task, EffectProperty.COMPONENT);
3768
+ ctx.$container$ = container;
3769
+ return invoke(ctx, () => {
3770
+ if (isFunction(obj)) {
3771
+ return obj();
3772
+ }
3773
+ if (prop) {
3774
+ return obj[prop];
3775
+ }
3776
+ else if (isSignal(obj)) {
3777
+ return obj.value;
3778
+ }
3779
+ else {
3780
+ return obj;
3781
+ }
3782
+ });
3783
+ };
3784
+ const handleError = (reason) => container.handleError(reason, host);
3785
+ const cleanups = [];
3786
+ task.$destroy$ = noSerialize(() => {
3787
+ cleanups.forEach((fn) => {
3788
+ try {
3789
+ fn();
3790
+ }
3791
+ catch (err) {
3792
+ handleError(err);
3793
+ }
3794
+ });
3795
+ done = true;
3796
+ });
3797
+ const resourceTarget = unwrapStore(resource);
3798
+ const opts = {
3799
+ track,
3800
+ cleanup(fn) {
3801
+ if (typeof fn === 'function') {
3802
+ cleanups.push(fn);
3803
+ }
3804
+ },
3805
+ cache(policy) {
3806
+ let milliseconds = 0;
3807
+ if (policy === 'immutable') {
3808
+ milliseconds = Infinity;
3809
+ }
3810
+ else {
3811
+ milliseconds = policy;
3812
+ }
3813
+ resource._cache = milliseconds;
3814
+ },
3815
+ previous: resourceTarget._resolved,
3816
+ };
3817
+ let resolve;
3818
+ let reject;
3819
+ let done = false;
3820
+ const setState = (resolved, value) => {
3821
+ if (!done) {
3822
+ done = true;
3823
+ if (resolved) {
3824
+ done = true;
3825
+ resource.loading = false;
3826
+ resource._state = 'resolved';
3827
+ resource._resolved = value;
3828
+ resource._error = undefined;
3829
+ resolve(value);
3830
+ }
3831
+ else {
3832
+ done = true;
3833
+ resource.loading = false;
3834
+ resource._state = 'rejected';
3835
+ resource._error = value;
3836
+ reject(value);
3837
+ }
3838
+ return true;
3839
+ }
3840
+ return false;
3841
+ };
3842
+ /**
3843
+ * Add cleanup to resolve the resource if we are trying to run the same resource again while the
3844
+ * previous one is not resolved yet. The next `runResource` run will call this cleanup
3845
+ */
3846
+ cleanups.push(() => {
3847
+ if (untrack(() => resource.loading) === true) {
3848
+ const value = untrack(() => resource._resolved);
3849
+ setState(true, value);
3850
+ }
3851
+ });
3852
+ // Execute mutation inside empty invocation
3853
+ invoke(iCtx, () => {
3854
+ // console.log('RESOURCE.pending: ');
3855
+ resource._state = 'pending';
3856
+ resource.loading = !isServerPlatform();
3857
+ const promise = (resource.value = new Promise((r, re) => {
3858
+ resolve = r;
3859
+ reject = re;
3860
+ }));
3861
+ promise.catch(ignoreErrorToPreventNodeFromCrashing);
3862
+ });
3863
+ const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
3864
+ setState(true, value);
3865
+ }, (err) => {
3866
+ if (isPromise(err)) {
3867
+ return err.then(() => runResource(task, container, host));
3868
+ }
3869
+ else {
3870
+ setState(false, err);
3871
+ }
3872
+ });
3873
+ const timeout = resourceTarget._timeout;
3874
+ if (timeout > 0) {
3875
+ return Promise.race([
3876
+ promise,
3877
+ delay(timeout).then(() => {
3878
+ if (setState(false, new Error('timeout'))) {
3879
+ cleanupTask(task);
3880
+ }
3881
+ }),
3882
+ ]);
3883
+ }
3884
+ return promise;
3885
+ };
3886
+ const ignoreErrorToPreventNodeFromCrashing = (err) => {
3887
+ // ignore error to prevent node from crashing
3888
+ // node will crash in promise is rejected and no one is listening to the rejection.
3580
3889
  };
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
3890
 
3605
3891
  /// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
3606
3892
  const aVNodePath = [];
@@ -3785,38 +4071,29 @@ const ssrNodeDocumentPosition = (a, b) => {
3785
4071
  * - Visible Tasks are sorted afterJournalFlush, than depth first on component and finally in
3786
4072
  * declaration order within component.
3787
4073
  */
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 = {}));
4074
+ // Turn this on to get debug output of what the scheduler is doing.
4075
+ const DEBUG$1 = false;
4076
+ const getPromise = (chore) => (chore.$promise$ ||= new Promise((resolve) => {
4077
+ chore.$resolve$ = resolve;
4078
+ }));
3808
4079
  const createScheduler = (container, scheduleDrain, journalFlush) => {
3809
4080
  const choreQueue = [];
4081
+ const qrlRuns = [];
3810
4082
  let currentChore = null;
3811
- let journalFlushScheduled = false;
4083
+ let drainScheduled = false;
3812
4084
  return schedule;
3813
4085
  ///// IMPLEMENTATION /////
3814
4086
  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;
4087
+ const isServer = !isDomContainer(container);
4088
+ const isComponentSsr = isServer && type === ChoreType.COMPONENT;
4089
+ const runLater = type !== ChoreType.WAIT_FOR_ALL && !isComponentSsr && type !== ChoreType.RUN_QRL;
4090
+ const isTask = type === ChoreType.TASK || type === ChoreType.VISIBLE || type === ChoreType.CLEANUP_VISIBLE;
4091
+ const isClientOnly = type === ChoreType.JOURNAL_FLUSH ||
4092
+ type === ChoreType.NODE_DIFF ||
4093
+ type === ChoreType.NODE_PROP;
4094
+ if (isServer && isClientOnly) {
4095
+ return;
4096
+ }
3820
4097
  if (isTask) {
3821
4098
  hostOrTask.$flags$ |= TaskFlags.DIRTY;
3822
4099
  }
@@ -3835,192 +4112,234 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
3835
4112
  $returnValue$: null,
3836
4113
  $executed$: false,
3837
4114
  };
3838
- chore.$promise$ = new Promise((resolve) => (chore.$resolve$ = resolve));
3839
4115
  chore = sortedInsert(choreQueue, chore, container.rootVNode || null);
3840
- if (!journalFlushScheduled && runLater) {
4116
+ if (!drainScheduled && runLater) {
3841
4117
  // If we are not currently draining, we need to schedule a drain.
3842
- journalFlushScheduled = true;
4118
+ drainScheduled = true;
3843
4119
  schedule(ChoreType.JOURNAL_FLUSH);
3844
- scheduleDrain();
4120
+ // Catch here to avoid unhandled promise rejection
4121
+ scheduleDrain()?.catch?.(() => { });
3845
4122
  }
4123
+ // TODO figure out what to do with chore errors
3846
4124
  if (runLater) {
3847
- return chore.$promise$;
4125
+ return getPromise(chore);
3848
4126
  }
3849
4127
  else {
3850
- return drainUpTo(chore, container.rootVNode || null);
4128
+ return drainUpTo(chore, isServer);
3851
4129
  }
3852
4130
  }
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
- }
4131
+ /** Execute all of the chores up to and including the given chore. */
4132
+ function drainUpTo(runUptoChore, isServer) {
4133
+ let maxRetries = 5000;
3867
4134
  while (choreQueue.length) {
3868
- const nextChore = choreQueue.shift();
3869
- const order = choreComparator(nextChore, runUptoChore, rootVNode);
3870
- if (order === null) {
3871
- continue;
4135
+ if (maxRetries-- < 0) {
4136
+ throw new Error('drainUpTo: max retries reached');
3872
4137
  }
3873
- if (order > 0) {
3874
- // we have processed all of the chores up to and including the given chore.
3875
- break;
4138
+ if (currentChore) {
4139
+ // Already running chore, which means we're waiting for async completion
4140
+ return getPromise(currentChore)
4141
+ .then(() => drainUpTo(runUptoChore, isServer))
4142
+ .catch((e) => {
4143
+ container.handleError(e, currentChore?.$host$);
4144
+ });
4145
+ }
4146
+ const nextChore = choreQueue[0];
4147
+ if (nextChore.$executed$) {
4148
+ choreQueue.shift();
4149
+ if (nextChore === runUptoChore) {
4150
+ break;
4151
+ }
4152
+ continue;
3876
4153
  }
3877
- const isDeletedVNode = vNodeAlreadyDeleted(nextChore);
3878
- if (isDeletedVNode &&
4154
+ if (vNodeAlreadyDeleted(nextChore) &&
3879
4155
  // we need to process cleanup tasks for deleted nodes
3880
4156
  nextChore.$type$ !== ChoreType.CLEANUP_VISIBLE) {
4157
+ choreQueue.shift();
3881
4158
  continue;
3882
4159
  }
3883
- const returnValue = executeChore(nextChore);
3884
- if (isPromise(returnValue)) {
3885
- const promise = returnValue.then(() => drainUpTo(runUptoChore, rootVNode));
3886
- return promise;
3887
- }
4160
+ executeChore(nextChore, isServer);
3888
4161
  }
3889
4162
  return runUptoChore.$returnValue$;
3890
4163
  }
3891
- function executeChore(chore) {
4164
+ function executeChore(chore, isServer) {
3892
4165
  const host = chore.$host$;
3893
4166
  assertEqual(currentChore, null, 'Chore already running.');
3894
4167
  currentChore = chore;
3895
4168
  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)));
4169
+ try {
4170
+ switch (chore.$type$) {
4171
+ case ChoreType.WAIT_FOR_ALL:
4172
+ {
4173
+ if (isServer) {
4174
+ drainScheduled = false;
4175
+ }
3907
4176
  }
3908
- else {
3909
- return jsx;
4177
+ break;
4178
+ case ChoreType.JOURNAL_FLUSH:
4179
+ {
4180
+ returnValue = journalFlush();
4181
+ drainScheduled = false;
4182
+ }
4183
+ break;
4184
+ case ChoreType.COMPONENT:
4185
+ {
4186
+ returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
4187
+ if (isServer) {
4188
+ return jsx;
4189
+ }
4190
+ else {
4191
+ const styleScopedId = container.getHostProp(host, QScopedStyle);
4192
+ return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
4193
+ }
4194
+ }, (err) => container.handleError(err, host));
4195
+ }
4196
+ break;
4197
+ case ChoreType.RUN_QRL:
4198
+ {
4199
+ const fn = chore.$target$.getFn();
4200
+ const result = retryOnPromise(() => fn(...chore.$payload$));
4201
+ if (isPromise(result)) {
4202
+ const handled = result
4203
+ .finally(() => {
4204
+ qrlRuns.splice(qrlRuns.indexOf(handled), 1);
4205
+ })
4206
+ .catch((error) => {
4207
+ container.handleError(error, chore.$host$);
4208
+ });
4209
+ // Don't wait for the promise to resolve
4210
+ // TODO come up with a better solution, we also want concurrent signal handling with tasks but serial tasks
4211
+ qrlRuns.push(handled);
4212
+ DEBUG$1 &&
4213
+ debugTrace('execute.DONE (but still running)', chore, currentChore, choreQueue);
4214
+ chore.$returnValue$ = handled;
4215
+ chore.$resolve$?.(handled);
4216
+ currentChore = null;
4217
+ chore.$executed$ = true;
4218
+ // early out so we don't call after()
4219
+ return;
4220
+ }
4221
+ returnValue = null;
4222
+ }
4223
+ break;
4224
+ case ChoreType.TASK:
4225
+ case ChoreType.VISIBLE:
4226
+ {
4227
+ const payload = chore.$payload$;
4228
+ if (payload.$flags$ & TaskFlags.RESOURCE) {
4229
+ const result = runResource(payload, container, host);
4230
+ // Don't await the return value of the resource, because async resources should not be awaited.
4231
+ // The reason for this is that we should be able to update for example a node with loading
4232
+ // text. If we await the resource, the loading text will not be displayed until the resource
4233
+ // is loaded.
4234
+ // Awaiting on the client also causes a deadlock.
4235
+ // In any case, the resource will never throw.
4236
+ returnValue = isServer ? result : null;
4237
+ }
4238
+ else {
4239
+ returnValue = runTask(payload, container, host);
4240
+ }
4241
+ }
4242
+ break;
4243
+ case ChoreType.CLEANUP_VISIBLE:
4244
+ {
4245
+ const task = chore.$payload$;
4246
+ cleanupTask(task);
4247
+ }
4248
+ break;
4249
+ case ChoreType.NODE_DIFF:
4250
+ {
4251
+ const parentVirtualNode = chore.$target$;
4252
+ let jsx = chore.$payload$;
4253
+ if (isSignal(jsx)) {
4254
+ jsx = jsx.value;
4255
+ }
4256
+ returnValue = retryOnPromise(() => vnode_diff(container, jsx, parentVirtualNode, null));
4257
+ }
4258
+ break;
4259
+ case ChoreType.NODE_PROP:
4260
+ {
4261
+ const virtualNode = chore.$host$;
4262
+ const payload = chore.$payload$;
4263
+ let value = payload.$value$;
4264
+ if (isSignal(value)) {
4265
+ value = value.value;
4266
+ }
4267
+ const isConst = payload.$isConst$;
4268
+ const journal = container.$journal$;
4269
+ const property = chore.$idx$;
4270
+ const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
4271
+ if (isConst) {
4272
+ const element = virtualNode[ElementVNodeProps.element];
4273
+ journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue);
4274
+ }
4275
+ else {
4276
+ vnode_setAttr(journal, virtualNode, property, serializedValue);
4277
+ }
4278
+ }
4279
+ break;
4280
+ case ChoreType.QRL_RESOLVE: {
4281
+ {
4282
+ const target = chore.$target$;
4283
+ returnValue = !target.resolved ? target.resolve() : null;
3910
4284
  }
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
4285
  break;
3969
4286
  }
3970
- returnValue = retryOnPromise(() => {
3971
- if (target.$computeIfNeeded$() || forceRunEffects) {
3972
- triggerEffects(container, target, target.$effects$);
4287
+ case ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS: {
4288
+ {
4289
+ const target = chore.$target$;
4290
+ const forceRunEffects = target.$forceRunEffects$;
4291
+ target.$forceRunEffects$ = false;
4292
+ if (!target.$effects$?.size) {
4293
+ break;
4294
+ }
4295
+ returnValue = retryOnPromise(() => {
4296
+ if (target.$computeIfNeeded$() || forceRunEffects) {
4297
+ triggerEffects(container, target, target.$effects$);
4298
+ }
4299
+ });
3973
4300
  }
3974
- });
3975
- break;
4301
+ break;
4302
+ }
3976
4303
  }
3977
4304
  }
3978
- return maybeThenPassError(returnValue, (value) => {
3979
- if (currentChore) {
3980
- currentChore.$executed$ = true;
3981
- currentChore.$resolve$?.(value);
3982
- }
4305
+ catch (e) {
4306
+ returnValue = Promise.reject(e);
4307
+ }
4308
+ const after = (value, error) => {
3983
4309
  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;
4310
+ chore.$executed$ = true;
4311
+ if (error) {
4312
+ container.handleError(error, host);
4313
+ }
4314
+ else {
4315
+ chore.$returnValue$ = value;
4316
+ chore.$resolve$?.(value);
4317
+ }
4318
+ };
4319
+ if (isPromise(returnValue)) {
4320
+ chore.$promise$ = returnValue.then(after, (error) => after(undefined, error));
4321
+ chore.$resolve$?.(chore.$promise$);
4322
+ chore.$resolve$ = undefined;
4323
+ }
4324
+ else {
4325
+ after(returnValue);
4326
+ }
4018
4327
  }
4019
- // JOURNAL_FLUSH does not have a host or $idx$, so we can't compare it.
4020
- if (a.$type$ !== ChoreType.JOURNAL_FLUSH) {
4328
+ /**
4329
+ * Compares two chores to determine their execution order in the scheduler's queue.
4330
+ *
4331
+ * @param a - The first chore to compare
4332
+ * @param b - The second chore to compare
4333
+ * @returns A number indicating the relative order of the chores. A negative number means `a` runs
4334
+ * before `b`.
4335
+ */
4336
+ function choreComparator(a, b, rootVNode) {
4337
+ const macroTypeDiff = (a.$type$ & ChoreType.MACRO) - (b.$type$ & ChoreType.MACRO);
4338
+ if (macroTypeDiff !== 0) {
4339
+ return macroTypeDiff;
4340
+ }
4021
4341
  const aHost = a.$host$;
4022
4342
  const bHost = b.$host$;
4023
- // QRL_RESOLVE does not have a host.
4024
4343
  if (aHost !== bHost && aHost !== null && bHost !== null) {
4025
4344
  if (vnode_isVNode(aHost) && vnode_isVNode(bHost)) {
4026
4345
  // we are running on the client.
@@ -4030,6 +4349,8 @@ function choreComparator(a, b, rootVNode) {
4030
4349
  }
4031
4350
  }
4032
4351
  else {
4352
+ assertFalse(vnode_isVNode(aHost), 'expected aHost to be SSRNode but it is a VNode');
4353
+ assertFalse(vnode_isVNode(bHost), 'expected bHost to be SSRNode but it is a VNode');
4033
4354
  // we are running on the server.
4034
4355
  // On server we can't schedule task for a different host!
4035
4356
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -4046,250 +4367,117 @@ function choreComparator(a, b, rootVNode) {
4046
4367
  }
4047
4368
  }
4048
4369
  const microTypeDiff = (a.$type$ & ChoreType.MICRO) - (b.$type$ & ChoreType.MICRO);
4049
- if (microTypeDiff !== 0) {
4050
- return microTypeDiff;
4051
- }
4052
- 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);
4219
- }
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();
4370
+ if (microTypeDiff !== 0) {
4371
+ return microTypeDiff;
4229
4372
  }
4230
- catch (err) {
4231
- logError(err);
4373
+ // types are the same
4374
+ const idxDiff = toNumber(a.$idx$) - toNumber(b.$idx$);
4375
+ if (idxDiff !== 0) {
4376
+ return idxDiff;
4232
4377
  }
4378
+ // If the host is the same (or missing), and the type is the same, we need to compare the target.
4379
+ if (a.$target$ !== b.$target$ || a.$payload$ !== b.$payload$) {
4380
+ // 1 means that we are going to process chores as FIFO
4381
+ return 1;
4382
+ }
4383
+ // If the chore is the same as the current chore, we will run it again
4384
+ if (b === currentChore) {
4385
+ return 1;
4386
+ }
4387
+ // The chores are the same and will run only once
4388
+ return 0;
4233
4389
  }
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));
4390
+ function sortedFindIndex(sortedArray, value, rootVNode) {
4391
+ /// We need to ensure that the `queue` is sorted by priority.
4392
+ /// 1. Find a place where to insert into.
4393
+ let bottom = 0;
4394
+ let top = sortedArray.length;
4395
+ while (bottom < top) {
4396
+ const middle = bottom + ((top - bottom) >> 1);
4397
+ const midChore = sortedArray[middle];
4398
+ const comp = choreComparator(value, midChore, rootVNode);
4399
+ if (comp < 0) {
4400
+ top = middle;
4401
+ }
4402
+ else if (comp > 0) {
4403
+ bottom = middle + 1;
4404
+ }
4405
+ else {
4406
+ // We already have the host in the queue.
4407
+ return middle;
4408
+ }
4409
+ }
4410
+ return ~bottom;
4241
4411
  }
4242
- else if (eagerness === 'idle' || eagerness === 'document-idle') {
4243
- useOnDocument('qidle', getTaskHandlerQrl(task));
4412
+ function sortedInsert(sortedArray, value, rootVNode) {
4413
+ /// We need to ensure that the `queue` is sorted by priority.
4414
+ /// 1. Find a place where to insert into.
4415
+ const idx = sortedFindIndex(sortedArray, value, rootVNode);
4416
+ if (idx < 0) {
4417
+ /// 2. Insert the chore into the queue.
4418
+ sortedArray.splice(~idx, 0, value);
4419
+ return value;
4420
+ }
4421
+ const existing = sortedArray[idx];
4422
+ /**
4423
+ * When a derived signal is updated we need to run vnode_diff. However the signal can update
4424
+ * multiple times during component execution. For this reason it is necessary for us to update
4425
+ * the chore with the latest result of the signal.
4426
+ */
4427
+ if (existing.$type$ === ChoreType.NODE_DIFF) {
4428
+ existing.$payload$ = value.$payload$;
4429
+ }
4430
+ if (existing.$executed$) {
4431
+ existing.$executed$ = false;
4432
+ }
4433
+ return existing;
4244
4434
  }
4245
4435
  };
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;
4436
+ const toNumber = (value) => {
4437
+ return typeof value === 'number' ? value : -1;
4254
4438
  };
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$;
4439
+ function vNodeAlreadyDeleted(chore) {
4440
+ return !!(chore.$host$ &&
4441
+ vnode_isVNode(chore.$host$) &&
4442
+ chore.$host$[VNodeProps.flags] & VNodeFlags.Deleted);
4443
+ }
4444
+ function debugChoreTypeToString(type) {
4445
+ return ({
4446
+ [ChoreType.QRL_RESOLVE]: 'QRL_RESOLVE',
4447
+ [ChoreType.RUN_QRL]: 'RUN_QRL',
4448
+ [ChoreType.TASK]: 'TASK',
4449
+ [ChoreType.NODE_DIFF]: 'NODE_DIFF',
4450
+ [ChoreType.NODE_PROP]: 'NODE_PROP',
4451
+ [ChoreType.COMPONENT]: 'COMPONENT',
4452
+ [ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS]: 'RECOMPUTE_SIGNAL',
4453
+ [ChoreType.JOURNAL_FLUSH]: 'JOURNAL_FLUSH',
4454
+ [ChoreType.VISIBLE]: 'VISIBLE',
4455
+ [ChoreType.CLEANUP_VISIBLE]: 'CLEANUP_VISIBLE',
4456
+ [ChoreType.WAIT_FOR_ALL]: 'WAIT_FOR_ALL',
4457
+ }[type] || 'UNKNOWN: ' + type);
4458
+ }
4459
+ function debugChoreToString(chore) {
4460
+ const type = debugChoreTypeToString(chore.$type$);
4461
+ const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
4462
+ const qrlTarget = chore.$target$?.$symbol$;
4463
+ return `Chore(${type} ${chore.$type$ === ChoreType.QRL_RESOLVE || chore.$type$ === ChoreType.RUN_QRL ? qrlTarget : host} ${chore.$idx$})`;
4464
+ }
4465
+ function debugTrace(action, arg, currentChore, queue) {
4466
+ const lines = ['===========================\nScheduler: ' + action];
4467
+ if (arg && !('$type$' in arg)) {
4468
+ lines.push(' arg: ' + String(arg).replaceAll(/\n.*/gim, ''));
4469
+ }
4470
+ if (queue) {
4471
+ queue.forEach((chore) => {
4472
+ const active = chore === arg ? '>>>' : ' ';
4473
+ lines.push(` ${active} > ` +
4474
+ (chore === currentChore ? '[running] ' : '') +
4475
+ debugChoreToString(chore));
4476
+ });
4270
4477
  }
4478
+ // eslint-disable-next-line no-console
4479
+ console.log(lines.join('\n') + '\n');
4271
4480
  }
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
4481
 
4294
4482
  /**
4295
4483
  * @file
@@ -4307,6 +4495,15 @@ const NEEDS_COMPUTATION = Symbol('invalid');
4307
4495
  const DEBUG = false;
4308
4496
  // eslint-disable-next-line no-console
4309
4497
  const log = (...args) => console.log('SIGNAL', ...args.map(qwikDebugToString));
4498
+ var SignalFlags;
4499
+ (function (SignalFlags) {
4500
+ SignalFlags[SignalFlags["INVALID"] = 1] = "INVALID";
4501
+ })(SignalFlags || (SignalFlags = {}));
4502
+ var WrappedSignalFlags;
4503
+ (function (WrappedSignalFlags) {
4504
+ // should subscribe to value and be unwrapped for PropsProxy
4505
+ WrappedSignalFlags[WrappedSignalFlags["UNWRAP"] = 2] = "UNWRAP";
4506
+ })(WrappedSignalFlags || (WrappedSignalFlags = {}));
4310
4507
  const throwIfQRLNotResolved = (qrl) => {
4311
4508
  const resolved = qrl.resolved;
4312
4509
  if (!resolved) {
@@ -4323,18 +4520,19 @@ const isSignal = (value) => {
4323
4520
  return value instanceof Signal;
4324
4521
  };
4325
4522
  /** @internal */
4326
- class EffectPropData {
4523
+ class SubscriptionData {
4327
4524
  data;
4328
4525
  constructor(data) {
4329
4526
  this.data = data;
4330
4527
  }
4331
4528
  }
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 = {}));
4529
+ var EffectSubscriptionProp;
4530
+ (function (EffectSubscriptionProp) {
4531
+ EffectSubscriptionProp[EffectSubscriptionProp["CONSUMER"] = 0] = "CONSUMER";
4532
+ EffectSubscriptionProp[EffectSubscriptionProp["PROPERTY"] = 1] = "PROPERTY";
4533
+ EffectSubscriptionProp[EffectSubscriptionProp["BACK_REF"] = 2] = "BACK_REF";
4534
+ EffectSubscriptionProp[EffectSubscriptionProp["DATA"] = 3] = "DATA";
4535
+ })(EffectSubscriptionProp || (EffectSubscriptionProp = {}));
4338
4536
  var EffectProperty;
4339
4537
  (function (EffectProperty) {
4340
4538
  EffectProperty["COMPONENT"] = ":";
@@ -4371,19 +4569,16 @@ class Signal {
4371
4569
  }
4372
4570
  const effectSubscriber = ctx.$effectSubscriber$;
4373
4571
  if (effectSubscriber) {
4374
- const effects = (this.$effects$ ||= []);
4572
+ const effects = (this.$effects$ ||= new Set());
4375
4573
  // Let's make sure that we have a reference to this effect.
4376
4574
  // Adding reference is essentially adding a subscription, so if the signal
4377
4575
  // changes we know who to notify.
4378
- ensureContainsEffect(effects, effectSubscriber);
4576
+ ensureContainsSubscription(effects, effectSubscriber);
4379
4577
  // But when effect is scheduled in needs to be able to know which signals
4380
4578
  // to unsubscribe from. So we need to store the reference from the effect back
4381
4579
  // 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
- }
4580
+ ensureContainsBackRef(effectSubscriber, this);
4581
+ addQrlToSerializationCtx(effectSubscriber, this.$container$);
4387
4582
  }
4388
4583
  }
4389
4584
  return this.untrackedValue;
@@ -4401,123 +4596,98 @@ class Signal {
4401
4596
  }
4402
4597
  }
4403
4598
  toString() {
4404
- return (`[${this.constructor.name}${this.$invalid$ ? ' INVALID' : ''} ${String(this.$untrackedValue$)}]` +
4405
- (this.$effects$?.map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' ')).join('\n') || ''));
4599
+ return (`[${this.constructor.name}${this.$flags$ & SignalFlags.INVALID ? ' INVALID' : ''} ${String(this.$untrackedValue$)}]` +
4600
+ (Array.from(this.$effects$ || [])
4601
+ .map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' '))
4602
+ .join('\n') || ''));
4406
4603
  }
4407
4604
  toJSON() {
4408
4605
  return { value: this.$untrackedValue$ };
4409
4606
  }
4410
4607
  }
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);
4608
+ const ensureContainsSubscription = (array, effectSubscription) => {
4609
+ array.add(effectSubscription);
4427
4610
  };
4428
- const ensureEffectContainsSubscriber = (effect, subscriber, container) => {
4429
- if (isSubscriber(effect)) {
4430
- effect.$effectDependencies$ ||= [];
4431
- if (subscriberExistInSubscribers(effect.$effectDependencies$, subscriber)) {
4432
- return;
4611
+ /** Ensure the item is in back refs set */
4612
+ const ensureContainsBackRef = (array, value) => {
4613
+ array[EffectSubscriptionProp.BACK_REF] ||= new Set();
4614
+ array[EffectSubscriptionProp.BACK_REF].add(value);
4615
+ };
4616
+ const addQrlToSerializationCtx = (effectSubscriber, container) => {
4617
+ if (!!container && !isDomContainer(container)) {
4618
+ const effect = effectSubscriber[EffectSubscriptionProp.CONSUMER];
4619
+ const property = effectSubscriber[EffectSubscriptionProp.PROPERTY];
4620
+ let qrl = null;
4621
+ if (isTask(effect)) {
4622
+ qrl = effect.$qrl$;
4433
4623
  }
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;
4624
+ else if (effect instanceof ComputedSignal) {
4625
+ qrl = effect.$computeQrl$;
4441
4626
  }
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;
4627
+ else if (property === EffectProperty.COMPONENT) {
4628
+ qrl = container.getHostProp(effect, OnRenderProp);
4450
4629
  }
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;
4630
+ if (qrl) {
4631
+ container.serializationCtx.$eventQrls$.add(qrl);
4462
4632
  }
4463
4633
  }
4464
- return false;
4465
4634
  };
4466
4635
  const triggerEffects = (container, signal, effects) => {
4636
+ const isBrowser = isDomContainer(container);
4467
4637
  if (effects) {
4468
- const scheduleEffect = (effectSubscriptions) => {
4469
- const effect = effectSubscriptions[EffectSubscriptionsProp.EFFECT];
4470
- const property = effectSubscriptions[EffectSubscriptionsProp.PROPERTY];
4638
+ const scheduleEffect = (effectSubscription) => {
4639
+ const consumer = effectSubscription[EffectSubscriptionProp.CONSUMER];
4640
+ const property = effectSubscription[EffectSubscriptionProp.PROPERTY];
4471
4641
  assertDefined(container, 'Container must be defined.');
4472
- if (isTask(effect)) {
4473
- effect.$flags$ |= TaskFlags.DIRTY;
4642
+ if (isTask(consumer)) {
4643
+ consumer.$flags$ |= TaskFlags.DIRTY;
4474
4644
  let choreType = ChoreType.TASK;
4475
- if (effect.$flags$ & TaskFlags.VISIBLE_TASK) {
4645
+ if (consumer.$flags$ & TaskFlags.VISIBLE_TASK) {
4476
4646
  choreType = ChoreType.VISIBLE;
4477
4647
  }
4478
- else if (effect.$flags$ & TaskFlags.RESOURCE) {
4479
- choreType = ChoreType.RESOURCE;
4480
- }
4481
- container.$scheduler$(choreType, effect);
4648
+ container.$scheduler$(choreType, consumer);
4482
4649
  }
4483
- else if (effect instanceof Signal) {
4650
+ else if (consumer instanceof Signal) {
4484
4651
  // we don't schedule ComputedSignal/DerivedSignal directly, instead we invalidate it and
4485
4652
  // and schedule the signals effects (recursively)
4486
- if (effect instanceof ComputedSignal) {
4653
+ if (consumer instanceof ComputedSignal) {
4487
4654
  // Ensure that the computed signal's QRL is resolved.
4488
4655
  // If not resolved schedule it to be resolved.
4489
- if (!effect.$computeQrl$.resolved) {
4490
- container.$scheduler$(ChoreType.QRL_RESOLVE, null, effect.$computeQrl$);
4656
+ if (!consumer.$computeQrl$.resolved) {
4657
+ container.$scheduler$(ChoreType.QRL_RESOLVE, null, consumer.$computeQrl$);
4491
4658
  }
4492
4659
  }
4493
- effect.$invalidate$();
4660
+ consumer.$invalidate$();
4494
4661
  }
4495
4662
  else if (property === EffectProperty.COMPONENT) {
4496
- const host = effect;
4663
+ const host = consumer;
4497
4664
  const qrl = container.getHostProp(host, OnRenderProp);
4498
4665
  assertDefined(qrl, 'Component must have QRL');
4499
4666
  const props = container.getHostProp(host, ELEMENT_PROPS);
4500
4667
  container.$scheduler$(ChoreType.COMPONENT, host, qrl, props);
4501
4668
  }
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);
4669
+ else if (isBrowser) {
4670
+ if (property === EffectProperty.VNODE) {
4671
+ const host = consumer;
4672
+ container.$scheduler$(ChoreType.NODE_DIFF, host, host, signal);
4673
+ }
4674
+ else {
4675
+ const host = consumer;
4676
+ const effectData = effectSubscription[EffectSubscriptionProp.DATA];
4677
+ if (effectData instanceof SubscriptionData) {
4678
+ const data = effectData.data;
4679
+ const payload = {
4680
+ ...data,
4681
+ $value$: signal,
4682
+ };
4683
+ container.$scheduler$(ChoreType.NODE_PROP, host, property, payload);
4684
+ }
4517
4685
  }
4518
4686
  }
4519
4687
  };
4520
- effects.forEach(scheduleEffect);
4688
+ for (const effect of effects) {
4689
+ scheduleEffect(effect);
4690
+ }
4521
4691
  }
4522
4692
  };
4523
4693
  /**
@@ -4533,18 +4703,21 @@ class ComputedSignal extends Signal {
4533
4703
  * resolve the QRL during the mark dirty phase so that any call to it will be synchronous). )
4534
4704
  */
4535
4705
  $computeQrl$;
4706
+ $flags$;
4707
+ $forceRunEffects$ = false;
4708
+ [_EFFECT_BACK_REF] = null;
4709
+ constructor(container, fn,
4536
4710
  // We need a separate flag to know when the computation needs running because
4537
4711
  // we need the old value to know if effects need running after computation
4538
- $invalid$ = true;
4539
- $forceRunEffects$ = false;
4540
- constructor(container, fn) {
4712
+ flags = SignalFlags.INVALID) {
4541
4713
  // The value is used for comparison when signals trigger, which can only happen
4542
4714
  // when it was calculated before. Therefore we can pass whatever we like.
4543
4715
  super(container, NEEDS_COMPUTATION);
4544
4716
  this.$computeQrl$ = fn;
4717
+ this.$flags$ = flags;
4545
4718
  }
4546
4719
  $invalidate$() {
4547
- this.$invalid$ = true;
4720
+ this.$flags$ |= SignalFlags.INVALID;
4548
4721
  this.$forceRunEffects$ = false;
4549
4722
  // We should only call subscribers if the calculation actually changed.
4550
4723
  // Therefore, we need to calculate the value now.
@@ -4555,7 +4728,7 @@ class ComputedSignal extends Signal {
4555
4728
  * remained the same object
4556
4729
  */
4557
4730
  force() {
4558
- this.$invalid$ = true;
4731
+ this.$flags$ |= SignalFlags.INVALID;
4559
4732
  this.$forceRunEffects$ = false;
4560
4733
  triggerEffects(this.$container$, this, this.$effects$);
4561
4734
  }
@@ -4568,14 +4741,14 @@ class ComputedSignal extends Signal {
4568
4741
  return this.$untrackedValue$;
4569
4742
  }
4570
4743
  $computeIfNeeded$() {
4571
- if (!this.$invalid$) {
4744
+ if (!(this.$flags$ & SignalFlags.INVALID)) {
4572
4745
  return false;
4573
4746
  }
4574
4747
  const computeQrl = this.$computeQrl$;
4575
4748
  throwIfQRLNotResolved(computeQrl);
4576
4749
  const ctx = tryGetInvokeContext();
4577
4750
  const previousEffectSubscription = ctx?.$effectSubscriber$;
4578
- ctx && (ctx.$effectSubscriber$ = [this, EffectProperty.VNODE]);
4751
+ ctx && (ctx.$effectSubscriber$ = getSubscriber(this, EffectProperty.VNODE));
4579
4752
  try {
4580
4753
  const untrackedValue = computeQrl.getFn(ctx)();
4581
4754
  if (isPromise(untrackedValue)) {
@@ -4585,7 +4758,7 @@ class ComputedSignal extends Signal {
4585
4758
  ]);
4586
4759
  }
4587
4760
  DEBUG && log('Signal.$compute$', untrackedValue);
4588
- this.$invalid$ = false;
4761
+ this.$flags$ &= ~SignalFlags.INVALID;
4589
4762
  const didChange = untrackedValue !== this.$untrackedValue$;
4590
4763
  if (didChange) {
4591
4764
  this.$untrackedValue$ = untrackedValue;
@@ -4610,20 +4783,22 @@ class WrappedSignal extends Signal {
4610
4783
  $args$;
4611
4784
  $func$;
4612
4785
  $funcStr$;
4613
- // We need a separate flag to know when the computation needs running because
4614
- // we need the old value to know if effects need running after computation
4615
- $invalid$ = true;
4616
- $effectDependencies$ = null;
4786
+ $flags$;
4617
4787
  $hostElement$ = null;
4618
4788
  $forceRunEffects$ = false;
4619
- constructor(container, fn, args, fnStr) {
4789
+ [_EFFECT_BACK_REF] = null;
4790
+ constructor(container, fn, args, fnStr,
4791
+ // We need a separate flag to know when the computation needs running because
4792
+ // we need the old value to know if effects need running after computation
4793
+ flags = SignalFlags.INVALID | WrappedSignalFlags.UNWRAP) {
4620
4794
  super(container, NEEDS_COMPUTATION);
4621
4795
  this.$args$ = args;
4622
4796
  this.$func$ = fn;
4623
4797
  this.$funcStr$ = fnStr;
4798
+ this.$flags$ = flags;
4624
4799
  }
4625
4800
  $invalidate$() {
4626
- this.$invalid$ = true;
4801
+ this.$flags$ |= SignalFlags.INVALID;
4627
4802
  this.$forceRunEffects$ = false;
4628
4803
  // We should only call subscribers if the calculation actually changed.
4629
4804
  // Therefore, we need to calculate the value now.
@@ -4634,7 +4809,7 @@ class WrappedSignal extends Signal {
4634
4809
  * remained the same object
4635
4810
  */
4636
4811
  force() {
4637
- this.$invalid$ = true;
4812
+ this.$flags$ |= SignalFlags.INVALID;
4638
4813
  this.$forceRunEffects$ = false;
4639
4814
  triggerEffects(this.$container$, this, this.$effects$);
4640
4815
  }
@@ -4647,10 +4822,12 @@ class WrappedSignal extends Signal {
4647
4822
  return this.$untrackedValue$;
4648
4823
  }
4649
4824
  $computeIfNeeded$() {
4650
- if (!this.$invalid$) {
4825
+ if (!(this.$flags$ & SignalFlags.INVALID)) {
4651
4826
  return false;
4652
4827
  }
4653
4828
  const untrackedValue = trackSignal(() => this.$func$(...this.$args$), this, EffectProperty.VNODE, this.$container$);
4829
+ // TODO: we should remove invalid flag here
4830
+ // this.$flags$ &= ~SignalFlags.INVALID;
4654
4831
  const didChange = untrackedValue !== this.$untrackedValue$;
4655
4832
  if (didChange) {
4656
4833
  this.$untrackedValue$ = untrackedValue;
@@ -4683,7 +4860,7 @@ const applyQwikComponentBody = (ssr, jsx, component) => {
4683
4860
  if (jsx.key !== null) {
4684
4861
  host.setProp(ELEMENT_KEY, jsx.key);
4685
4862
  }
4686
- return scheduler(ChoreType.COMPONENT_SSR, host, componentQrl, srcProps);
4863
+ return scheduler(ChoreType.COMPONENT, host, componentQrl, srcProps);
4687
4864
  };
4688
4865
 
4689
4866
  class ParentComponentData {
@@ -4695,21 +4872,10 @@ class ParentComponentData {
4695
4872
  }
4696
4873
  }
4697
4874
  /** @internal */
4698
- function _walkJSX(ssr, value, options) {
4875
+ async function _walkJSX(ssr, value, options) {
4699
4876
  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
4877
  const enqueue = (value) => stack.push(value);
4708
- const resolveValue = (value) => {
4709
- stack.push(value);
4710
- drain();
4711
- };
4712
- const drain = () => {
4878
+ const drain = async () => {
4713
4879
  while (stack.length) {
4714
4880
  const value = stack.pop();
4715
4881
  if (value instanceof ParentComponentData) {
@@ -4719,20 +4885,10 @@ function _walkJSX(ssr, value, options) {
4719
4885
  }
4720
4886
  else if (typeof value === 'function') {
4721
4887
  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;
4888
+ stack.push(await stack.pop());
4889
+ continue;
4735
4890
  }
4891
+ await value.apply(ssr);
4736
4892
  continue;
4737
4893
  }
4738
4894
  processJSXNode(ssr, enqueue, value, {
@@ -4740,12 +4896,8 @@ function _walkJSX(ssr, value, options) {
4740
4896
  parentComponentFrame: options.parentComponentFrame,
4741
4897
  });
4742
4898
  }
4743
- if (stack.length === 0 && options.allowPromises) {
4744
- resolveDrain();
4745
- }
4746
4899
  };
4747
- drain();
4748
- return drained;
4900
+ await drain();
4749
4901
  }
4750
4902
  function processJSXNode(ssr, enqueue, value, options) {
4751
4903
  // console.log('processJSXNode', value);
@@ -4784,7 +4936,6 @@ function processJSXNode(ssr, enqueue, value, options) {
4784
4936
  enqueue(async () => {
4785
4937
  for await (const chunk of value) {
4786
4938
  await _walkJSX(ssr, chunk, {
4787
- allowPromises: true,
4788
4939
  currentStyleScoped: options.styleScoped,
4789
4940
  parentComponentFrame: options.parentComponentFrame,
4790
4941
  });
@@ -4838,7 +4989,7 @@ function processJSXNode(ssr, enqueue, value, options) {
4838
4989
  if (componentFrame) {
4839
4990
  const compId = componentFrame.componentNode.id || '';
4840
4991
  const projectionAttrs = isDev ? [DEBUG_TYPE, VirtualType.Projection] : [];
4841
- projectionAttrs.push(':', compId);
4992
+ projectionAttrs.push(QSlotParent, compId);
4842
4993
  ssr.openProjection(projectionAttrs);
4843
4994
  const host = componentFrame.componentNode;
4844
4995
  const node = ssr.getLastNode();
@@ -4871,7 +5022,6 @@ function processJSXNode(ssr, enqueue, value, options) {
4871
5022
  value = generator({
4872
5023
  async write(chunk) {
4873
5024
  await _walkJSX(ssr, chunk, {
4874
- allowPromises: true,
4875
5025
  currentStyleScoped: options.styleScoped,
4876
5026
  parentComponentFrame: options.parentComponentFrame,
4877
5027
  });
@@ -5028,11 +5178,23 @@ function setEvent(serializationCtx, key, rawValue) {
5028
5178
  const appendToValue = (valueToAppend) => {
5029
5179
  value = (value == null ? '' : value + '\n') + valueToAppend;
5030
5180
  };
5181
+ const getQrlString = (qrl) => {
5182
+ /**
5183
+ * If there are captures we need to schedule so everything is executed in the right order + qrls
5184
+ * are resolved.
5185
+ *
5186
+ * For internal qrls (starting with `_`) we assume that they do the right thing.
5187
+ */
5188
+ if (!qrl.$symbol$.startsWith('_') && (qrl.$captureRef$ || qrl.$capture$)) {
5189
+ qrl = createQRL(null, '_run', queueQRL, null, null, [qrl]);
5190
+ }
5191
+ return qrlToString(serializationCtx, qrl);
5192
+ };
5031
5193
  if (Array.isArray(qrls)) {
5032
5194
  for (let i = 0; i <= qrls.length; i++) {
5033
5195
  const qrl = qrls[i];
5034
- if (isQrl(qrl)) {
5035
- appendToValue(qrlToString(serializationCtx, qrl));
5196
+ if (isQrl$1(qrl)) {
5197
+ appendToValue(getQrlString(qrl));
5036
5198
  addQwikEventToSerializationContext(serializationCtx, key, qrl);
5037
5199
  }
5038
5200
  else if (qrl != null) {
@@ -5044,8 +5206,8 @@ function setEvent(serializationCtx, key, rawValue) {
5044
5206
  }
5045
5207
  }
5046
5208
  }
5047
- else if (isQrl(qrls)) {
5048
- value = qrlToString(serializationCtx, qrls);
5209
+ else if (isQrl$1(qrls)) {
5210
+ value = getQrlString(qrls);
5049
5211
  addQwikEventToSerializationContext(serializationCtx, key, qrls);
5050
5212
  }
5051
5213
  return value;
@@ -5095,7 +5257,7 @@ function appendClassIfScopedStyleExists(jsx, styleScoped) {
5095
5257
  *
5096
5258
  * @public
5097
5259
  */
5098
- const version = "2.0.0-alpha.6-dev+d848ba5";
5260
+ const version = "2.0.0-alpha.8-dev+66037b5";
5099
5261
 
5100
5262
  /** @internal */
5101
5263
  class _SharedContainer {
@@ -5127,13 +5289,6 @@ class _SharedContainer {
5127
5289
  }
5128
5290
  }
5129
5291
 
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
5292
  // Keep these properties named like this so they're the same as from wrapSignal
5138
5293
  const getValueProp = (p0) => p0.value;
5139
5294
  const getProp = (p0, p1) => p0[p1];
@@ -5157,7 +5312,7 @@ const _wrapProp = (...args) => {
5157
5312
  }
5158
5313
  if (isSignal(obj)) {
5159
5314
  assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
5160
- if (obj instanceof WrappedSignal) {
5315
+ if (obj instanceof WrappedSignal && obj.flags & WrappedSignalFlags.UNWRAP) {
5161
5316
  return obj;
5162
5317
  }
5163
5318
  return getWrapped(args);
@@ -5183,6 +5338,17 @@ const _wrapProp = (...args) => {
5183
5338
  // We need to forward the access to the original object
5184
5339
  return getWrapped(args);
5185
5340
  };
5341
+ /** @internal */
5342
+ const _wrapStore = (obj, prop) => {
5343
+ const target = getStoreTarget(obj);
5344
+ const value = target[prop];
5345
+ if (isSignal(value)) {
5346
+ return value;
5347
+ }
5348
+ else {
5349
+ return new WrappedSignal(null, getProp, [obj, prop], null, SignalFlags.INVALID);
5350
+ }
5351
+ };
5186
5352
  /** @internal @deprecated v1 compat */
5187
5353
  const _wrapSignal = (obj, prop) => {
5188
5354
  const r = _wrapProp(obj, prop);
@@ -5453,7 +5619,9 @@ class PropsProxyHandler {
5453
5619
  ? this.$constProps$[prop]
5454
5620
  : this.$varProps$[prop];
5455
5621
  // a proxied value that the optimizer made
5456
- return value instanceof WrappedSignal ? value.value : value;
5622
+ return value instanceof WrappedSignal && value.$flags$ & WrappedSignalFlags.UNWRAP
5623
+ ? value.value
5624
+ : value;
5457
5625
  }
5458
5626
  set(_, prop, value) {
5459
5627
  if (prop === _CONST_PROPS) {
@@ -5545,7 +5713,7 @@ function qwikDebugToString(value) {
5545
5713
  else if (isTask(value)) {
5546
5714
  return `Task(${qwikDebugToString(value.$qrl$)})`;
5547
5715
  }
5548
- else if (isQrl$1(value)) {
5716
+ else if (isQrl(value)) {
5549
5717
  return `Qrl(${value.$symbol$})`;
5550
5718
  }
5551
5719
  else if (typeof value === 'object' || typeof value === 'function') {
@@ -5687,8 +5855,8 @@ const VNodeDataChar = {
5687
5855
  ID_CHAR: /* ********* */ '=',
5688
5856
  PROPS: /* ************** */ 62, // `>` - `q:props' - Component Props
5689
5857
  PROPS_CHAR: /* ****** */ '>',
5690
- SLOT_REF: /* *********** */ 63, // `?` - `q:sref` - Slot reference.
5691
- SLOT_REF_CHAR: /* *** */ '?',
5858
+ SLOT_PARENT: /* ******** */ 63, // `?` - `q:sparent` - Slot parent.
5859
+ SLOT_PARENT_CHAR: /* */ '?',
5692
5860
  KEY: /* **************** */ 64, // `@` - `q:key` - Element key.
5693
5861
  KEY_CHAR: /* ******** */ '@',
5694
5862
  SEQ: /* **************** */ 91, // `[` - `q:seq' - Seq value from `useSequentialScope()`
@@ -5699,14 +5867,21 @@ const VNodeDataChar = {
5699
5867
  CONTEXT_CHAR: /* **** */ ']',
5700
5868
  SEQ_IDX: /* ************ */ 94, // `^` - `q:seqIdx' - Sequential scope id
5701
5869
  SEQ_IDX_CHAR: /* **** */ '^',
5702
- SUBS: /* *************** */ 96, // '`' - `q:subs' - Effect dependencies/subscriptions
5703
- SUBS_CHAR: /* ******* */ '`',
5870
+ BACK_REFS: /* ********** */ 96, // '`' - `q:brefs' - Effect dependencies/subscriptions
5871
+ BACK_REFS_CHAR: /* ** */ '`',
5704
5872
  SEPARATOR: /* ********* */ 124, // `|` - Separator char to encode any key/value pairs.
5705
5873
  SEPARATOR_CHAR: /* ** */ '|',
5706
5874
  SLOT: /* ************** */ 126, // `~` - `q:slot' - Slot name
5707
5875
  SLOT_CHAR: /* ******* */ '~',
5708
5876
  };
5709
5877
 
5878
+ const mergeMaps = (map1, map2) => {
5879
+ for (const [k, v] of map2) {
5880
+ map1.set(k, v);
5881
+ }
5882
+ return map1;
5883
+ };
5884
+
5710
5885
  /**
5711
5886
  * @file
5712
5887
  *
@@ -5929,6 +6104,12 @@ const vnode_isVirtualVNode = (vNode) => {
5929
6104
  const flag = vNode[VNodeProps.flags];
5930
6105
  return (flag & VNodeFlags.Virtual) === VNodeFlags.Virtual;
5931
6106
  };
6107
+ const vnode_isProjection = (vNode) => {
6108
+ assertDefined(vNode, 'Missing vNode');
6109
+ const flag = vNode[VNodeProps.flags];
6110
+ return ((flag & VNodeFlags.Virtual) === VNodeFlags.Virtual &&
6111
+ vnode_getProp(vNode, QSlot, null) !== null);
6112
+ };
5932
6113
  const ensureTextVNode = (vNode) => {
5933
6114
  assertTrue(vnode_isTextVNode(vNode), 'Expecting TextVNode was: ' + vnode_getNodeTypeName(vNode));
5934
6115
  return vNode;
@@ -5962,6 +6143,7 @@ const vnode_ensureElementInflated = (vnode) => {
5962
6143
  elementVNode[VNodeProps.flags] ^= VNodeFlags.Inflated;
5963
6144
  const element = elementVNode[ElementVNodeProps.element];
5964
6145
  const attributes = element.attributes;
6146
+ const props = vnode_getProps(elementVNode);
5965
6147
  for (let idx = 0; idx < attributes.length; idx++) {
5966
6148
  const attr = attributes[idx];
5967
6149
  const key = attr.name;
@@ -5972,15 +6154,15 @@ const vnode_ensureElementInflated = (vnode) => {
5972
6154
  }
5973
6155
  else if (key.startsWith(QContainerAttr)) {
5974
6156
  if (attr.value === QContainerValue.HTML) {
5975
- mapArray_set(elementVNode, dangerouslySetInnerHTML, element.innerHTML, ElementVNodeProps.PROPS_OFFSET);
6157
+ mapArray_set(props, dangerouslySetInnerHTML, element.innerHTML, 0);
5976
6158
  }
5977
6159
  else if (attr.value === QContainerValue.TEXT && 'value' in element) {
5978
- mapArray_set(elementVNode, 'value', element.value, ElementVNodeProps.PROPS_OFFSET);
6160
+ mapArray_set(props, 'value', element.value, 0);
5979
6161
  }
5980
6162
  }
5981
6163
  else if (!key.startsWith('on:')) {
5982
6164
  const value = attr.value;
5983
- mapArray_set(elementVNode, key, value, ElementVNodeProps.PROPS_OFFSET);
6165
+ mapArray_set(props, key, value, 0);
5984
6166
  }
5985
6167
  }
5986
6168
  }
@@ -6372,7 +6554,7 @@ const vnode_applyJournal = (journal) => {
6372
6554
  element[key] = parseBoolean(value);
6373
6555
  }
6374
6556
  else if (key === 'value' && key in element) {
6375
- element.value = escapeHTML(String(value));
6557
+ element.value = String(value);
6376
6558
  }
6377
6559
  else if (key === dangerouslySetInnerHTML) {
6378
6560
  element.innerHTML = value;
@@ -6416,54 +6598,16 @@ const vnode_applyJournal = (journal) => {
6416
6598
  journal.length = 0;
6417
6599
  };
6418
6600
  //////////////////////////////////////////////////////////////////////////////////////////////////////
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
6601
  const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
6463
6602
  ensureElementOrVirtualVNode(parent);
6464
6603
  if (vnode_isElementVNode(parent)) {
6465
6604
  ensureMaterialized(parent);
6466
6605
  }
6606
+ if (newChild === insertBefore) {
6607
+ // invalid insertBefore. We can't insert before self reference
6608
+ // prevent infinity loop and putting self reference to next sibling
6609
+ insertBefore = null;
6610
+ }
6467
6611
  let adjustedInsertBefore = null;
6468
6612
  if (insertBefore == null) {
6469
6613
  if (vnode_isVirtualVNode(parent)) {
@@ -6587,9 +6731,10 @@ const vnode_getElementName = (vnode) => {
6587
6731
  const elementVNode = ensureElementVNode(vnode);
6588
6732
  let elementName = elementVNode[ElementVNodeProps.elementName];
6589
6733
  if (elementName === undefined) {
6590
- elementName = elementVNode[ElementVNodeProps.elementName] =
6591
- elementVNode[ElementVNodeProps.element].nodeName.toLowerCase();
6592
- elementVNode[VNodeProps.flags] |= vnode_getElementNamespaceFlags(elementName);
6734
+ const element = elementVNode[ElementVNodeProps.element];
6735
+ const nodeName = isDev ? fastNodeName(element).toLowerCase() : fastNodeName(element);
6736
+ elementName = elementVNode[ElementVNodeProps.elementName] = nodeName;
6737
+ elementVNode[VNodeProps.flags] |= vnode_getElementNamespaceFlags(element);
6593
6738
  }
6594
6739
  return elementName;
6595
6740
  };
@@ -6771,6 +6916,20 @@ const fastFirstChild = (node) => {
6771
6916
  }
6772
6917
  return node;
6773
6918
  };
6919
+ let _fastNamespaceURI = null;
6920
+ const fastNamespaceURI = (element) => {
6921
+ if (!_fastNamespaceURI) {
6922
+ _fastNamespaceURI = fastGetter(element, 'namespaceURI');
6923
+ }
6924
+ return _fastNamespaceURI.call(element);
6925
+ };
6926
+ let _fastNodeName = null;
6927
+ const fastNodeName = (element) => {
6928
+ if (!_fastNodeName) {
6929
+ _fastNodeName = fastGetter(element, 'nodeName');
6930
+ }
6931
+ return _fastNodeName.call(element);
6932
+ };
6774
6933
  const fastGetter = (prototype, name) => {
6775
6934
  let getter;
6776
6935
  while (prototype && !(getter = Object.getOwnPropertyDescriptor(prototype, name)?.get)) {
@@ -6837,8 +6996,11 @@ const materializeFromDOM = (vParent, firstChild, vData) => {
6837
6996
  container.$setRawState$(parseInt(id), vParent);
6838
6997
  isDev && vnode_setAttr(null, vParent, ELEMENT_ID, id);
6839
6998
  }
6840
- else if (peek() === VNodeDataChar.SUBS) {
6841
- vnode_setProp(vParent, QSubscribers, consumeValue());
6999
+ else if (peek() === VNodeDataChar.BACK_REFS) {
7000
+ if (!container) {
7001
+ container = getDomContainer(vParent[ElementVNodeProps.element]);
7002
+ }
7003
+ setEffectBackRefFromVNodeData(vParent, consumeValue(), container);
6842
7004
  }
6843
7005
  else {
6844
7006
  // prevent infinity loop if there are some characters outside the range
@@ -6848,6 +7010,18 @@ const materializeFromDOM = (vParent, firstChild, vData) => {
6848
7010
  }
6849
7011
  return vFirstChild;
6850
7012
  };
7013
+ function setEffectBackRefFromVNodeData(vParent, value, container) {
7014
+ const deserializedSubMap = container.$getObjectById$(value);
7015
+ if (!vParent[_EFFECT_BACK_REF]) {
7016
+ Object.defineProperty(vParent, _EFFECT_BACK_REF, {
7017
+ value: deserializedSubMap,
7018
+ });
7019
+ }
7020
+ else {
7021
+ const subMap = vParent[_EFFECT_BACK_REF];
7022
+ mergeMaps(subMap, deserializedSubMap);
7023
+ }
7024
+ }
6851
7025
  const processVNodeData$1 = (vData, callback) => {
6852
7026
  let nextToConsumeIdx = 0;
6853
7027
  let ch = 0;
@@ -6892,8 +7066,9 @@ const vnode_getAttrKeys = (vnode) => {
6892
7066
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6893
7067
  vnode_ensureElementInflated(vnode);
6894
7068
  const keys = [];
6895
- for (let i = vnode_getPropStartIndex(vnode); i < vnode.length; i = i + 2) {
6896
- const key = vnode[i];
7069
+ const props = vnode_getProps(vnode);
7070
+ for (let i = 0; i < props.length; i = i + 2) {
7071
+ const key = props[i];
6897
7072
  if (!key.startsWith(Q_PROPS_SEPARATOR)) {
6898
7073
  keys.push(key);
6899
7074
  }
@@ -6906,22 +7081,23 @@ const vnode_setAttr = (journal, vnode, key, value) => {
6906
7081
  const type = vnode[VNodeProps.flags];
6907
7082
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6908
7083
  vnode_ensureElementInflated(vnode);
6909
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7084
+ const props = vnode_getProps(vnode);
7085
+ const idx = mapApp_findIndx(props, key, 0);
6910
7086
  if (idx >= 0) {
6911
- if (vnode[idx + 1] != value && (type & VNodeFlags.Element) !== 0) {
7087
+ if (props[idx + 1] != value && (type & VNodeFlags.Element) !== 0) {
6912
7088
  // Values are different, update DOM
6913
7089
  const element = vnode[ElementVNodeProps.element];
6914
7090
  journal && journal.push(VNodeJournalOpCode.SetAttribute, element, key, value);
6915
7091
  }
6916
7092
  if (value == null) {
6917
- vnode.splice(idx, 2);
7093
+ props.splice(idx, 2);
6918
7094
  }
6919
7095
  else {
6920
- vnode[idx + 1] = value;
7096
+ props[idx + 1] = value;
6921
7097
  }
6922
7098
  }
6923
7099
  else if (value != null) {
6924
- vnode.splice(idx ^ -1, 0, key, value);
7100
+ props.splice(idx ^ -1, 0, key, value);
6925
7101
  if ((type & VNodeFlags.Element) !== 0) {
6926
7102
  // New value, update DOM
6927
7103
  const element = vnode[ElementVNodeProps.element];
@@ -6934,7 +7110,8 @@ const vnode_getAttr = (vnode, key) => {
6934
7110
  const type = vnode[VNodeProps.flags];
6935
7111
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6936
7112
  vnode_ensureElementInflated(vnode);
6937
- return mapArray_get(vnode, key, vnode_getPropStartIndex(vnode));
7113
+ const props = vnode_getProps(vnode);
7114
+ return mapArray_get(props, key, 0);
6938
7115
  }
6939
7116
  return null;
6940
7117
  };
@@ -6942,11 +7119,12 @@ const vnode_getProp = (vnode, key, getObject) => {
6942
7119
  const type = vnode[VNodeProps.flags];
6943
7120
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6944
7121
  type & VNodeFlags.Element && vnode_ensureElementInflated(vnode);
6945
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7122
+ const props = vnode_getProps(vnode);
7123
+ const idx = mapApp_findIndx(props, key, 0);
6946
7124
  if (idx >= 0) {
6947
- let value = vnode[idx + 1];
7125
+ let value = props[idx + 1];
6948
7126
  if (typeof value === 'string' && getObject) {
6949
- vnode[idx + 1] = value = getObject(value);
7127
+ props[idx + 1] = value = getObject(value);
6950
7128
  }
6951
7129
  return value;
6952
7130
  }
@@ -6955,12 +7133,13 @@ const vnode_getProp = (vnode, key, getObject) => {
6955
7133
  };
6956
7134
  const vnode_setProp = (vnode, key, value) => {
6957
7135
  ensureElementOrVirtualVNode(vnode);
6958
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7136
+ const props = vnode_getProps(vnode);
7137
+ const idx = mapApp_findIndx(props, key, 0);
6959
7138
  if (idx >= 0) {
6960
- vnode[idx + 1] = value;
7139
+ props[idx + 1] = value;
6961
7140
  }
6962
7141
  else if (value != null) {
6963
- vnode.splice(idx ^ -1, 0, key, value);
7142
+ props.splice(idx ^ -1, 0, key, value);
6964
7143
  }
6965
7144
  };
6966
7145
  const vnode_getPropStartIndex = (vnode) => {
@@ -6973,6 +7152,9 @@ const vnode_getPropStartIndex = (vnode) => {
6973
7152
  }
6974
7153
  throw qError(QError.invalidVNodeType, [type]);
6975
7154
  };
7155
+ const vnode_getProps = (vnode) => {
7156
+ return vnode[vnode_getPropStartIndex(vnode)];
7157
+ };
6976
7158
  const vnode_getParent = (vnode) => {
6977
7159
  return vnode[VNodeProps.parent] || null;
6978
7160
  };
@@ -7124,9 +7306,6 @@ function materializeFromVNodeData(vParent, vData, element, child) {
7124
7306
  else if (peek() === VNodeDataChar.PROPS) {
7125
7307
  vnode_setAttr(null, vParent, ELEMENT_PROPS, consumeValue());
7126
7308
  }
7127
- else if (peek() === VNodeDataChar.SLOT_REF) {
7128
- vnode_setAttr(null, vParent, QSlotRef, consumeValue());
7129
- }
7130
7309
  else if (peek() === VNodeDataChar.KEY) {
7131
7310
  vnode_setAttr(null, vParent, ELEMENT_KEY, consumeValue());
7132
7311
  }
@@ -7136,8 +7315,14 @@ function materializeFromVNodeData(vParent, vData, element, child) {
7136
7315
  else if (peek() === VNodeDataChar.SEQ_IDX) {
7137
7316
  vnode_setAttr(null, vParent, ELEMENT_SEQ_IDX, consumeValue());
7138
7317
  }
7139
- else if (peek() === VNodeDataChar.SUBS) {
7140
- vnode_setProp(vParent, QSubscribers, consumeValue());
7318
+ else if (peek() === VNodeDataChar.BACK_REFS) {
7319
+ if (!container) {
7320
+ container = getDomContainer(element);
7321
+ }
7322
+ setEffectBackRefFromVNodeData(vParent, consumeValue(), container);
7323
+ }
7324
+ else if (peek() === VNodeDataChar.SLOT_PARENT) {
7325
+ vnode_setProp(vParent, QSlotParent, consumeValue());
7141
7326
  }
7142
7327
  else if (peek() === VNodeDataChar.CONTEXT) {
7143
7328
  vnode_setAttr(null, vParent, QCtxAttr, consumeValue());
@@ -7245,23 +7430,20 @@ const vnode_getProjectionParentComponent = (vHost, rootVNode) => {
7245
7430
  };
7246
7431
  const VNodeArray = class VNode extends Array {
7247
7432
  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);
7433
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, firstChild, lastChild, element, elementName, []);
7250
7434
  return vnode;
7251
7435
  }
7252
7436
  static createText(flags, parent, previousSibling, nextSibling, textNode, text) {
7253
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7254
- vnode.push(textNode, text);
7437
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, textNode, text);
7255
7438
  return vnode;
7256
7439
  }
7257
7440
  static createVirtual(flags, parent, previousSibling, nextSibling, firstChild, lastChild) {
7258
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7259
- vnode.push(firstChild, lastChild);
7441
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, firstChild, lastChild, []);
7260
7442
  return vnode;
7261
7443
  }
7262
- constructor(flags, parent, previousSibling, nextSibling) {
7263
- super();
7264
- this.push(flags, parent, previousSibling, nextSibling);
7444
+ constructor(flags, parent, previousSibling, nextSibling, ...rest) {
7445
+ // @ts-expect-error
7446
+ super(flags, parent, previousSibling, nextSibling, ...rest);
7265
7447
  if (isDev) {
7266
7448
  this.toString = vnode_toString;
7267
7449
  }
@@ -7326,10 +7508,12 @@ function invokeApply(context, fn, args) {
7326
7508
  return returnValue;
7327
7509
  }
7328
7510
  const newInvokeContextFromTuple = ([element, event, url]) => {
7329
- const container = element.closest(QContainerSelector);
7511
+ const domContainer = getDomContainer(element);
7512
+ const container = domContainer.element;
7513
+ const vNode = container ? vnode_locate(domContainer.rootVNode, element) : undefined;
7330
7514
  const locale = container?.getAttribute(QLocaleAttr) || undefined;
7331
7515
  locale && setLocale(locale);
7332
- return newInvokeContext(locale, undefined, element, event, url);
7516
+ return newInvokeContext(locale, vNode, element, event, url);
7333
7517
  };
7334
7518
  // TODO how about putting url and locale (and event/custom?) in to a "static" object
7335
7519
  const newInvokeContext = (locale, hostElement, element, event, url) => {
@@ -7364,16 +7548,14 @@ const trackInvocation = /*#__PURE__*/ newInvokeContext(undefined, undefined, und
7364
7548
  * @param property `true` - subscriber is component `false` - subscriber is VNode `string` -
7365
7549
  * subscriber is property
7366
7550
  * @param container
7551
+ * @param data - Additional subscription data
7367
7552
  * @returns
7368
7553
  */
7369
7554
  const trackSignal = (fn, subscriber, property, container, data) => {
7370
7555
  const previousSubscriber = trackInvocation.$effectSubscriber$;
7371
7556
  const previousContainer = trackInvocation.$container$;
7372
7557
  try {
7373
- trackInvocation.$effectSubscriber$ = [subscriber, property];
7374
- if (data) {
7375
- trackInvocation.$effectSubscriber$.push(data);
7376
- }
7558
+ trackInvocation.$effectSubscriber$ = getSubscriber(subscriber, property, data);
7377
7559
  trackInvocation.$container$ = container;
7378
7560
  return invoke(trackInvocation, fn);
7379
7561
  }
@@ -7394,8 +7576,16 @@ const _getContextElement = () => {
7394
7576
  if (iCtx) {
7395
7577
  const hostElement = iCtx.$hostElement$;
7396
7578
  let element = null;
7397
- if (vnode_isVNode(hostElement) && vnode_isElementVNode(hostElement)) {
7398
- element = vnode_getNode(hostElement);
7579
+ if (hostElement != null) {
7580
+ if (vnode_isVNode(hostElement)) {
7581
+ if (vnode_isElementVNode(hostElement)) {
7582
+ element = vnode_getNode(hostElement);
7583
+ }
7584
+ }
7585
+ else {
7586
+ // isSSRnode
7587
+ element = hostElement;
7588
+ }
7399
7589
  }
7400
7590
  return element ?? iCtx.$qrl$?.$setContainer$(undefined);
7401
7591
  }
@@ -8031,12 +8221,12 @@ class DomContainer extends _SharedContainer {
8031
8221
  $storeProxyMap$ = new WeakMap();
8032
8222
  $qFuncs$;
8033
8223
  $instanceHash$;
8034
- stateData;
8224
+ vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
8225
+ $stateData$;
8035
8226
  $styleIds$ = null;
8036
- $vnodeLocate$ = (id) => vnode_locate(this.rootVNode, id);
8037
8227
  $renderCount$ = 0;
8038
8228
  constructor(element) {
8039
- super(() => this.scheduleRender(), () => vnode_applyJournal(this.$journal$), {}, element.getAttribute('q:locale'));
8229
+ super(() => this.scheduleRender(), () => vnode_applyJournal(this.$journal$), {}, element.getAttribute(QLocaleAttr));
8040
8230
  this.qContainer = element.getAttribute(QContainerAttr);
8041
8231
  if (!this.qContainer) {
8042
8232
  throw qError(QError.elementWithoutContainer);
@@ -8059,29 +8249,29 @@ class DomContainer extends _SharedContainer {
8059
8249
  this.rootVNode = vnode_newUnMaterializedElement(this.element);
8060
8250
  // These are here to initialize all properties at once for single class transition
8061
8251
  this.$rawStateData$ = null;
8062
- this.stateData = null;
8252
+ this.$stateData$ = null;
8063
8253
  const document = this.element.ownerDocument;
8064
8254
  if (!document.qVNodeData) {
8065
8255
  processVNodeData(document);
8066
8256
  }
8067
8257
  this.$rawStateData$ = [];
8068
- this.stateData = [];
8258
+ this.$stateData$ = [];
8069
8259
  const qwikStates = element.querySelectorAll('script[type="qwik/state"]');
8070
8260
  if (qwikStates.length !== 0) {
8071
8261
  const lastState = qwikStates[qwikStates.length - 1];
8072
8262
  this.$rawStateData$ = JSON.parse(lastState.textContent);
8073
- this.stateData = wrapDeserializerProxy(this, this.$rawStateData$);
8263
+ this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
8074
8264
  }
8075
8265
  this.$qFuncs$ = getQFuncs(document, this.$instanceHash$) || EMPTY_ARRAY;
8076
8266
  }
8077
8267
  $setRawState$(id, vParent) {
8078
- this.stateData[id] = vParent;
8268
+ this.$stateData$[id] = vParent;
8079
8269
  }
8080
8270
  parseQRL(qrl) {
8081
8271
  return inflateQRL(this, parseQRL(qrl));
8082
8272
  }
8083
8273
  handleError(err, host) {
8084
- if (qDev) {
8274
+ if (qDev && host) {
8085
8275
  // Clean vdom
8086
8276
  if (typeof document !== 'undefined') {
8087
8277
  const vHost = host;
@@ -8105,7 +8295,7 @@ class DomContainer extends _SharedContainer {
8105
8295
  throw err;
8106
8296
  }
8107
8297
  }
8108
- const errorStore = this.resolveContext(host, ERROR_CONTEXT);
8298
+ const errorStore = host && this.resolveContext(host, ERROR_CONTEXT);
8109
8299
  if (!errorStore) {
8110
8300
  throw err;
8111
8301
  }
@@ -8141,7 +8331,7 @@ class DomContainer extends _SharedContainer {
8141
8331
  vNode =
8142
8332
  vnode_getParent(vNode) ||
8143
8333
  // If virtual node, than it could be a slot so we need to read its parent.
8144
- vnode_getProp(vNode, QSlotParent, this.$vnodeLocate$);
8334
+ vnode_getProp(vNode, QSlotParent, this.vNodeLocate);
8145
8335
  }
8146
8336
  else {
8147
8337
  vNode = vnode_getParent(vNode);
@@ -8161,7 +8351,7 @@ class DomContainer extends _SharedContainer {
8161
8351
  case ELEMENT_PROPS:
8162
8352
  case OnRenderProp:
8163
8353
  case QCtxAttr:
8164
- case QSubscribers:
8354
+ case QBackRefs:
8165
8355
  getObjectById = this.$getObjectById$;
8166
8356
  break;
8167
8357
  case ELEMENT_SEQ_IDX:
@@ -8174,7 +8364,7 @@ class DomContainer extends _SharedContainer {
8174
8364
  scheduleRender() {
8175
8365
  this.$renderCount$++;
8176
8366
  this.renderDone ||= getPlatform().nextTick(() => this.processChores());
8177
- return this.renderDone;
8367
+ return this.renderDone.finally(() => emitEvent('qrender', { instanceHash: this.$instanceHash$, renderCount: this.$renderCount$ }));
8178
8368
  }
8179
8369
  processChores() {
8180
8370
  let renderCount = this.$renderCount$;
@@ -8197,12 +8387,13 @@ class DomContainer extends _SharedContainer {
8197
8387
  ensureProjectionResolved(vNode) {
8198
8388
  if ((vNode[VNodeProps.flags] & VNodeFlags.Resolved) === 0) {
8199
8389
  vNode[VNodeProps.flags] |= VNodeFlags.Resolved;
8200
- for (let i = vnode_getPropStartIndex(vNode); i < vNode.length; i = i + 2) {
8201
- const prop = vNode[i];
8390
+ const props = vnode_getProps(vNode);
8391
+ for (let i = 0; i < props.length; i = i + 2) {
8392
+ const prop = props[i];
8202
8393
  if (isSlotProp(prop)) {
8203
- const value = vNode[i + 1];
8394
+ const value = props[i + 1];
8204
8395
  if (typeof value == 'string') {
8205
- vNode[i + 1] = this.$vnodeLocate$(value);
8396
+ props[i + 1] = this.vNodeLocate(value);
8206
8397
  }
8207
8398
  }
8208
8399
  }
@@ -8213,7 +8404,7 @@ class DomContainer extends _SharedContainer {
8213
8404
  id = parseFloat(id);
8214
8405
  }
8215
8406
  assertTrue(id < this.$rawStateData$.length / 2, `Invalid reference: ${id} >= ${this.$rawStateData$.length / 2}`);
8216
- return this.stateData[id];
8407
+ return this.$stateData$[id];
8217
8408
  };
8218
8409
  getSyncFn(id) {
8219
8410
  const fn = this.$qFuncs$[id];
@@ -8285,8 +8476,7 @@ class DeserializationHandler {
8285
8476
  ? parseInt(property, 10)
8286
8477
  : NaN;
8287
8478
  if (Number.isNaN(i) || i < 0 || i >= this.$length$) {
8288
- const out = Reflect.get(target, property, receiver);
8289
- return out;
8479
+ return Reflect.get(target, property, receiver);
8290
8480
  }
8291
8481
  // The serialized data is an array with 2 values for each item
8292
8482
  const idx = i * 2;
@@ -8352,6 +8542,7 @@ const inflate = (container, target, typeId, data) => {
8352
8542
  switch (typeId) {
8353
8543
  case TypeIds.Object:
8354
8544
  // We use getters for making complex values lazy
8545
+ // TODO scan the data for computeQRLs and schedule resolve chores
8355
8546
  for (let i = 0; i < data.length; i += 4) {
8356
8547
  const key = deserializeData(container, data[i], data[i + 1]);
8357
8548
  const valType = data[i + 2];
@@ -8391,7 +8582,7 @@ const inflate = (container, target, typeId, data) => {
8391
8582
  task.$flags$ = v[1];
8392
8583
  task.$index$ = v[2];
8393
8584
  task.$el$ = v[3];
8394
- task.$effectDependencies$ = v[4];
8585
+ task[_EFFECT_BACK_REF] = v[4];
8395
8586
  task.$state$ = v[5];
8396
8587
  break;
8397
8588
  case TypeIds.Resource:
@@ -8414,12 +8605,9 @@ const inflate = (container, target, typeId, data) => {
8414
8605
  break;
8415
8606
  case TypeIds.Store:
8416
8607
  case TypeIds.StoreArray: {
8417
- const [value, flags, effects, storeEffect] = data;
8608
+ const [value, flags, effects] = data;
8418
8609
  const store = getOrCreateStore(value, flags, container);
8419
8610
  const storeHandler = getStoreHandler(store);
8420
- if (storeEffect) {
8421
- effects[STORE_ARRAY_PROP] = storeEffect;
8422
- }
8423
8611
  storeHandler.$effects$ = effects;
8424
8612
  target = store;
8425
8613
  break;
@@ -8428,7 +8616,7 @@ const inflate = (container, target, typeId, data) => {
8428
8616
  const signal = target;
8429
8617
  const d = data;
8430
8618
  signal.$untrackedValue$ = d[0];
8431
- signal.$effects$ = d.slice(1);
8619
+ signal.$effects$ = new Set(d.slice(1));
8432
8620
  break;
8433
8621
  }
8434
8622
  case TypeIds.WrappedSignal: {
@@ -8436,10 +8624,11 @@ const inflate = (container, target, typeId, data) => {
8436
8624
  const d = data;
8437
8625
  signal.$func$ = container.getSyncFn(d[0]);
8438
8626
  signal.$args$ = d[1];
8439
- signal.$effectDependencies$ = d[2];
8627
+ signal[_EFFECT_BACK_REF] = d[2];
8440
8628
  signal.$untrackedValue$ = d[3];
8441
- signal.$hostElement$ = d[4];
8442
- signal.$effects$ = d.slice(5);
8629
+ signal.$flags$ = d[4];
8630
+ signal.$hostElement$ = d[5];
8631
+ signal.$effects$ = new Set(d.slice(6));
8443
8632
  break;
8444
8633
  }
8445
8634
  case TypeIds.ComputedSignal: {
@@ -8451,7 +8640,7 @@ const inflate = (container, target, typeId, data) => {
8451
8640
  computed.$untrackedValue$ = d[2];
8452
8641
  }
8453
8642
  else {
8454
- computed.$invalid$ = true;
8643
+ computed.$flags$ |= SignalFlags.INVALID;
8455
8644
  /**
8456
8645
  * If we try to compute value and the qrl is not resolved, then system throws an error with
8457
8646
  * qrl promise. To prevent that we should early resolve computed qrl while computed
@@ -8559,6 +8748,7 @@ const _constants = [
8559
8748
  EMPTY_ARRAY,
8560
8749
  EMPTY_OBJ,
8561
8750
  NEEDS_COMPUTATION,
8751
+ STORE_ALL_PROPS,
8562
8752
  Slot,
8563
8753
  Fragment,
8564
8754
  NaN,
@@ -8639,6 +8829,8 @@ const allocate = (container, typeId, value) => {
8639
8829
  reject = rej;
8640
8830
  });
8641
8831
  resolvers.set(promise, [resolve, reject]);
8832
+ // Don't leave unhandled promise rejections
8833
+ promise.catch(() => { });
8642
8834
  return promise;
8643
8835
  case TypeIds.Uint8Array:
8644
8836
  const encodedLength = value.length;
@@ -8648,12 +8840,9 @@ const allocate = (container, typeId, value) => {
8648
8840
  return new Uint8Array(decodedLength);
8649
8841
  case TypeIds.PropsProxy:
8650
8842
  return createPropsProxy(null, null);
8651
- case TypeIds.RefVNode:
8652
8843
  case TypeIds.VNode:
8653
- const vnodeOrDocument = retrieveVNodeOrDocument(container, value);
8654
- if (typeId === TypeIds.VNode) {
8655
- return vnodeOrDocument;
8656
- }
8844
+ return retrieveVNodeOrDocument(container, value);
8845
+ case TypeIds.RefVNode:
8657
8846
  const vNode = retrieveVNodeOrDocument(container, value);
8658
8847
  if (vnode_isVNode(vNode)) {
8659
8848
  return vnode_getNode(vNode);
@@ -8662,7 +8851,7 @@ const allocate = (container, typeId, value) => {
8662
8851
  throw qError(QError.serializeErrorExpectedVNode, [typeof vNode]);
8663
8852
  }
8664
8853
  case TypeIds.EffectData:
8665
- return new EffectPropData({});
8854
+ return new SubscriptionData({});
8666
8855
  default:
8667
8856
  throw qError(QError.serializeErrorCannotAllocate, [typeId]);
8668
8857
  }
@@ -8694,7 +8883,7 @@ function parseQRL(qrl) {
8694
8883
  assertDefined(backChannel, 'Missing QRL_RUNTIME_CHUNK');
8695
8884
  qrlRef = backChannel.get(symbol);
8696
8885
  }
8697
- return createQRL(chunk, symbol, qrlRef, null, captureIds, null, null);
8886
+ return createQRL(chunk, symbol, qrlRef, null, captureIds, null);
8698
8887
  }
8699
8888
  function inflateQRL(container, qrl) {
8700
8889
  const captureIds = qrl.$capture$;
@@ -8812,7 +9001,7 @@ prepVNodeData) => {
8812
9001
  /** Visit an object, adding anything that will be serialized as to scan */
8813
9002
  const visit = (obj) => {
8814
9003
  if (typeof obj === 'function') {
8815
- if (isQrl(obj)) {
9004
+ if (isQrl$1(obj)) {
8816
9005
  if (obj.$captureRef$) {
8817
9006
  discoveredValues.push(...obj.$captureRef$);
8818
9007
  }
@@ -8831,6 +9020,7 @@ prepVNodeData) => {
8831
9020
  obj instanceof RegExp ||
8832
9021
  obj instanceof Uint8Array ||
8833
9022
  obj instanceof URLSearchParams ||
9023
+ vnode_isVNode(obj) ||
8834
9024
  (typeof FormData !== 'undefined' && obj instanceof FormData) ||
8835
9025
  // Ignore the no serialize objects
8836
9026
  fastSkipSerialize(obj)) ;
@@ -8840,8 +9030,7 @@ prepVNodeData) => {
8840
9030
  else if (isStore(obj)) {
8841
9031
  const target = getStoreTarget(obj);
8842
9032
  const effects = getStoreHandler(obj).$effects$;
8843
- const storeEffect = effects?.[STORE_ARRAY_PROP] ?? null;
8844
- discoveredValues.push(target, effects, storeEffect);
9033
+ discoveredValues.push(target, effects);
8845
9034
  for (const prop in target) {
8846
9035
  const propValue = target[prop];
8847
9036
  if (storeProxyMap.has(propValue)) {
@@ -8864,20 +9053,19 @@ prepVNodeData) => {
8864
9053
  */
8865
9054
  const v = obj instanceof WrappedSignal
8866
9055
  ? obj.untrackedValue
8867
- : obj instanceof ComputedSignal && (obj.$invalid$ || fastSkipSerialize(obj))
9056
+ : obj instanceof ComputedSignal &&
9057
+ (obj.$flags$ & SignalFlags.INVALID || fastSkipSerialize(obj))
8868
9058
  ? NEEDS_COMPUTATION
8869
9059
  : obj.$untrackedValue$;
8870
9060
  if (v !== NEEDS_COMPUTATION) {
8871
9061
  discoveredValues.push(v);
8872
9062
  }
8873
9063
  if (obj.$effects$) {
8874
- discoveredValues.push(...obj.$effects$);
9064
+ discoveredValues.push(obj.$effects$);
8875
9065
  }
8876
9066
  // WrappedSignal uses syncQrl which has no captured refs
8877
9067
  if (obj instanceof WrappedSignal) {
8878
- if (obj.$effectDependencies$) {
8879
- discoveredValues.push(...obj.$effectDependencies$);
8880
- }
9068
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8881
9069
  if (obj.$args$) {
8882
9070
  discoveredValues.push(...obj.$args$);
8883
9071
  }
@@ -8886,11 +9074,13 @@ prepVNodeData) => {
8886
9074
  }
8887
9075
  }
8888
9076
  else if (obj instanceof ComputedSignal) {
9077
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8889
9078
  discoveredValues.push(obj.$computeQrl$);
8890
9079
  }
8891
9080
  }
8892
9081
  else if (obj instanceof Task) {
8893
- discoveredValues.push(obj.$el$, obj.$qrl$, obj.$state$, obj.$effectDependencies$);
9082
+ discoveredValues.push(obj.$el$, obj.$qrl$, obj.$state$);
9083
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8894
9084
  }
8895
9085
  else if (isSsrNode(obj)) {
8896
9086
  discoverValuesForVNodeData(obj.vnodeData, discoveredValues);
@@ -8909,7 +9099,7 @@ prepVNodeData) => {
8909
9099
  else if (Array.isArray(obj)) {
8910
9100
  discoveredValues.push(...obj);
8911
9101
  }
8912
- else if (isQrl(obj)) {
9102
+ else if (isQrl$1(obj)) {
8913
9103
  obj.$captureRef$ && obj.$captureRef$.length && discoveredValues.push(...obj.$captureRef$);
8914
9104
  }
8915
9105
  else if (isPropsProxy(obj)) {
@@ -8925,7 +9115,7 @@ prepVNodeData) => {
8925
9115
  });
8926
9116
  promises.push(obj);
8927
9117
  }
8928
- else if (obj instanceof EffectPropData) {
9118
+ else if (obj instanceof SubscriptionData) {
8929
9119
  discoveredValues.push(obj.data);
8930
9120
  }
8931
9121
  else if (isObjectLiteral(obj)) {
@@ -8970,15 +9160,20 @@ const discoverValuesForVNodeData = (vnodeData, discoveredValues) => {
8970
9160
  for (const value of vnodeData) {
8971
9161
  if (isSsrAttrs(value)) {
8972
9162
  for (let i = 1; i < value.length; i += 2) {
8973
- if (value[i - 1] === ELEMENT_KEY) {
9163
+ const attrValue = value[i];
9164
+ if (typeof attrValue === 'string') {
8974
9165
  continue;
8975
9166
  }
8976
- const attrValue = value[i];
8977
9167
  discoveredValues.push(attrValue);
8978
9168
  }
8979
9169
  }
8980
9170
  }
8981
9171
  };
9172
+ const discoverEffectBackRefs = (effectsBackRefs, discoveredValues) => {
9173
+ if (effectsBackRefs) {
9174
+ discoveredValues.push(effectsBackRefs);
9175
+ }
9176
+ };
8982
9177
  const promiseResults = new WeakMap();
8983
9178
  /**
8984
9179
  * Format:
@@ -9050,7 +9245,7 @@ function serialize(serializationContext) {
9050
9245
  else if (value === Fragment) {
9051
9246
  output(TypeIds.Constant, Constants.Fragment);
9052
9247
  }
9053
- else if (isQrl(value)) {
9248
+ else if (isQrl$1(value)) {
9054
9249
  const qrl = qrlToString(serializationContext, value);
9055
9250
  const id = serializationContext.$addRoot$(qrl);
9056
9251
  output(TypeIds.QRL, id);
@@ -9125,6 +9320,9 @@ function serialize(serializationContext) {
9125
9320
  else if (value === NEEDS_COMPUTATION) {
9126
9321
  output(TypeIds.Constant, Constants.NEEDS_COMPUTATION);
9127
9322
  }
9323
+ else if (value === STORE_ALL_PROPS) {
9324
+ output(TypeIds.Constant, Constants.STORE_ALL_PROPS);
9325
+ }
9128
9326
  else {
9129
9327
  throw qError(QError.serializeErrorUnknownType, [typeof value]);
9130
9328
  }
@@ -9161,7 +9359,7 @@ function serialize(serializationContext) {
9161
9359
  : 0;
9162
9360
  output(TypeIds.PropsProxy, out);
9163
9361
  }
9164
- else if (value instanceof EffectPropData) {
9362
+ else if (value instanceof SubscriptionData) {
9165
9363
  output(TypeIds.EffectData, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
9166
9364
  }
9167
9365
  else if (isStore(value)) {
@@ -9172,6 +9370,7 @@ function serialize(serializationContext) {
9172
9370
  if (!res) {
9173
9371
  throw qError(QError.serializeErrorUnvisited, ['resource']);
9174
9372
  }
9373
+ // TODO the effects include the resourcereturn which has duplicate data
9175
9374
  output(TypeIds.Resource, [...res, getStoreHandler(value).$effects$]);
9176
9375
  }
9177
9376
  else {
@@ -9179,7 +9378,6 @@ function serialize(serializationContext) {
9179
9378
  const storeTarget = getStoreTarget(value);
9180
9379
  const flags = storeHandler.$flags$;
9181
9380
  const effects = storeHandler.$effects$;
9182
- const storeEffect = effects?.[STORE_ARRAY_PROP] ?? null;
9183
9381
  const innerStores = [];
9184
9382
  for (const prop in storeTarget) {
9185
9383
  const propValue = storeTarget[prop];
@@ -9189,7 +9387,7 @@ function serialize(serializationContext) {
9189
9387
  serializationContext.$addRoot$(innerStore);
9190
9388
  }
9191
9389
  }
9192
- const out = [storeTarget, flags, effects, storeEffect, ...innerStores];
9390
+ const out = [storeTarget, flags, effects, ...innerStores];
9193
9391
  while (out[out.length - 1] == null) {
9194
9392
  out.pop();
9195
9393
  }
@@ -9222,14 +9420,15 @@ function serialize(serializationContext) {
9222
9420
  * It can never be meant to become a vNode, because vNodes are internal only.
9223
9421
  */
9224
9422
  const v = value instanceof ComputedSignal &&
9225
- (value.$invalid$ || fastSkipSerialize(value.$untrackedValue$))
9423
+ (value.$flags$ & SignalFlags.INVALID || fastSkipSerialize(value.$untrackedValue$))
9226
9424
  ? NEEDS_COMPUTATION
9227
9425
  : value.$untrackedValue$;
9228
9426
  if (value instanceof WrappedSignal) {
9229
9427
  output(TypeIds.WrappedSignal, [
9230
9428
  ...serializeWrappingFn(serializationContext, value),
9231
- value.$effectDependencies$,
9429
+ filterEffectBackRefs(value[_EFFECT_BACK_REF]),
9232
9430
  v,
9431
+ value.$flags$,
9233
9432
  value.$hostElement$,
9234
9433
  ...(value.$effects$ || []),
9235
9434
  ]);
@@ -9335,7 +9534,7 @@ function serialize(serializationContext) {
9335
9534
  value.$flags$,
9336
9535
  value.$index$,
9337
9536
  value.$el$,
9338
- value.$effectDependencies$,
9537
+ value[_EFFECT_BACK_REF],
9339
9538
  value.$state$,
9340
9539
  ];
9341
9540
  while (out[out.length - 1] == null) {
@@ -9358,12 +9557,27 @@ function serialize(serializationContext) {
9358
9557
  const out = btoa(buf).replace(/=+$/, '');
9359
9558
  output(TypeIds.Uint8Array, out);
9360
9559
  }
9560
+ else if (vnode_isVNode(value)) {
9561
+ output(TypeIds.Constant, Constants.Undefined);
9562
+ }
9361
9563
  else {
9362
9564
  throw qError(QError.serializeErrorUnknownType, [typeof value]);
9363
9565
  }
9364
9566
  };
9365
9567
  writeValue(serializationContext.$roots$, -1);
9366
9568
  }
9569
+ function filterEffectBackRefs(effectBackRef) {
9570
+ let effectBackRefToSerialize = null;
9571
+ if (effectBackRef) {
9572
+ for (const [effectProp, effect] of effectBackRef) {
9573
+ if (effect[EffectSubscriptionProp.BACK_REF]) {
9574
+ effectBackRefToSerialize ||= new Map();
9575
+ effectBackRefToSerialize.set(effectProp, effect);
9576
+ }
9577
+ }
9578
+ }
9579
+ return effectBackRefToSerialize;
9580
+ }
9367
9581
  function serializeWrappingFn(serializationContext, value) {
9368
9582
  // if value is an object then we need to wrap this in ()
9369
9583
  if (value.$funcStr$ && value.$funcStr$[0] === '{') {
@@ -9376,15 +9590,12 @@ function serializeWrappingFn(serializationContext, value) {
9376
9590
  function qrlToString(serializationContext, value) {
9377
9591
  let symbol = value.$symbol$;
9378
9592
  let chunk = value.$chunk$;
9379
- const refSymbol = value.$refSymbol$ ?? symbol;
9380
9593
  const platform = getPlatform();
9381
9594
  if (platform) {
9382
- const result = platform.chunkForSymbol(refSymbol, chunk, value.dev?.file);
9595
+ const result = platform.chunkForSymbol(symbol, chunk, value.dev?.file);
9383
9596
  if (result) {
9384
9597
  chunk = result[1];
9385
- if (!value.$refSymbol$) {
9386
- symbol = result[0];
9387
- }
9598
+ symbol = result[0];
9388
9599
  }
9389
9600
  }
9390
9601
  const isSync = isSyncQrl(value);
@@ -9561,9 +9772,9 @@ const frameworkType = (obj) => {
9561
9772
  return ((typeof obj === 'object' &&
9562
9773
  obj !== null &&
9563
9774
  (obj instanceof Signal || obj instanceof Task || isJSXNode(obj))) ||
9564
- isQrl(obj));
9775
+ isQrl$1(obj));
9565
9776
  };
9566
- const canSerialize = (value) => {
9777
+ const canSerialize = (value, seen = new WeakSet()) => {
9567
9778
  if (value == null ||
9568
9779
  typeof value === 'string' ||
9569
9780
  typeof value === 'number' ||
@@ -9572,6 +9783,10 @@ const canSerialize = (value) => {
9572
9783
  return true;
9573
9784
  }
9574
9785
  else if (typeof value === 'object') {
9786
+ if (seen.has(value)) {
9787
+ return true;
9788
+ }
9789
+ seen.add(value);
9575
9790
  const proto = Object.getPrototypeOf(value);
9576
9791
  if (isStore(value)) {
9577
9792
  value = getStoreTarget(value);
@@ -9580,7 +9795,7 @@ const canSerialize = (value) => {
9580
9795
  for (const key in value) {
9581
9796
  // if the value is a props proxy, then sometimes we could create a component-level subscription,
9582
9797
  // so we should call untrack here to avoid tracking the value
9583
- if (!canSerialize(untrack(() => value[key]))) {
9798
+ if (!canSerialize(untrack(() => value[key]), seen)) {
9584
9799
  return false;
9585
9800
  }
9586
9801
  }
@@ -9588,7 +9803,7 @@ const canSerialize = (value) => {
9588
9803
  }
9589
9804
  else if (proto == Array.prototype) {
9590
9805
  for (let i = 0; i < value.length; i++) {
9591
- if (!canSerialize(value[i])) {
9806
+ if (!canSerialize(value[i], seen)) {
9592
9807
  return false;
9593
9808
  }
9594
9809
  }
@@ -9638,7 +9853,7 @@ const canSerialize = (value) => {
9638
9853
  }
9639
9854
  }
9640
9855
  else if (typeof value === 'function') {
9641
- if (isQrl(value) || isQwikComponent(value)) {
9856
+ if (isQrl$1(value) || isQwikComponent(value)) {
9642
9857
  return true;
9643
9858
  }
9644
9859
  }
@@ -9691,20 +9906,21 @@ var Constants;
9691
9906
  Constants[Constants["EMPTY_ARRAY"] = 5] = "EMPTY_ARRAY";
9692
9907
  Constants[Constants["EMPTY_OBJ"] = 6] = "EMPTY_OBJ";
9693
9908
  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";
9909
+ Constants[Constants["STORE_ALL_PROPS"] = 8] = "STORE_ALL_PROPS";
9910
+ Constants[Constants["Slot"] = 9] = "Slot";
9911
+ Constants[Constants["Fragment"] = 10] = "Fragment";
9912
+ Constants[Constants["NaN"] = 11] = "NaN";
9913
+ Constants[Constants["PositiveInfinity"] = 12] = "PositiveInfinity";
9914
+ Constants[Constants["NegativeInfinity"] = 13] = "NegativeInfinity";
9915
+ Constants[Constants["MaxSafeInt"] = 14] = "MaxSafeInt";
9700
9916
  // used for close fragment
9701
- Constants[Constants["AlmostMaxSafeInt"] = 14] = "AlmostMaxSafeInt";
9702
- Constants[Constants["MinSafeInt"] = 15] = "MinSafeInt";
9917
+ Constants[Constants["AlmostMaxSafeInt"] = 15] = "AlmostMaxSafeInt";
9918
+ Constants[Constants["MinSafeInt"] = 16] = "MinSafeInt";
9703
9919
  })(Constants || (Constants = {}));
9704
9920
 
9705
9921
  /** @internal */
9706
9922
  const verifySerializable = (value, preMessage) => {
9707
- const seen = new Set();
9923
+ const seen = new WeakSet();
9708
9924
  return _verifySerializable(value, seen, '_', preMessage);
9709
9925
  };
9710
9926
  const _verifySerializable = (value, seen, ctx, preMessage) => {
@@ -9713,10 +9929,12 @@ const _verifySerializable = (value, seen, ctx, preMessage) => {
9713
9929
  return value;
9714
9930
  }
9715
9931
  if (shouldSerialize(unwrapped)) {
9716
- if (seen.has(unwrapped)) {
9717
- return value;
9932
+ if (typeof unwrapped === 'object') {
9933
+ if (seen.has(unwrapped)) {
9934
+ return value;
9935
+ }
9936
+ seen.add(unwrapped);
9718
9937
  }
9719
- seen.add(unwrapped);
9720
9938
  if (isSignal(unwrapped)) {
9721
9939
  return value;
9722
9940
  }
@@ -9820,16 +10038,7 @@ const _weakSerialize = (input) => {
9820
10038
  return input;
9821
10039
  };
9822
10040
 
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) => {
10041
+ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
9833
10042
  if (qDev && qSerialize) {
9834
10043
  if (captureRef) {
9835
10044
  for (const item of captureRef) {
@@ -9851,9 +10060,14 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9851
10060
  };
9852
10061
  function bindFnToContext(currentCtx, beforeFn) {
9853
10062
  // Note that we bind the current `this`
9854
- return (...args) => maybeThen(resolveLazy(), (fn) => {
9855
- if (!isFunction(fn)) {
9856
- throw qError(QError.qrlIsNotFunction);
10063
+ const bound = (...args) => {
10064
+ if (!qrl.resolved) {
10065
+ return qrl.resolve().then((fn) => {
10066
+ if (!isFunction(fn)) {
10067
+ throw qError(QError.qrlIsNotFunction);
10068
+ }
10069
+ return bound(...args);
10070
+ });
9857
10071
  }
9858
10072
  if (beforeFn && beforeFn() === false) {
9859
10073
  return;
@@ -9866,13 +10080,14 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9866
10080
  context.$qrl$ = qrl;
9867
10081
  context.$event$ ||= this;
9868
10082
  try {
9869
- return invoke.call(this, context, fn, ...args);
10083
+ return invoke.call(this, context, symbolRef, ...args);
9870
10084
  }
9871
10085
  finally {
9872
10086
  context.$qrl$ = prevQrl;
9873
10087
  context.$event$ = prevEvent;
9874
10088
  }
9875
- });
10089
+ };
10090
+ return bound;
9876
10091
  }
9877
10092
  const resolveLazy = (containerEl) => {
9878
10093
  return symbolRef !== null ? symbolRef : resolve(containerEl);
@@ -9884,8 +10099,20 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9884
10099
  }
9885
10100
  return function (...args) {
9886
10101
  let context = tryGetInvokeContext();
10102
+ // use the given qrl if it is the right one
9887
10103
  if (context) {
9888
- return fn.apply(this, args);
10104
+ // TODO check if this is necessary in production
10105
+ if (context.$qrl$?.$symbol$ === qrl.$symbol$) {
10106
+ return fn.apply(this, args);
10107
+ }
10108
+ const prevQrl = context.$qrl$;
10109
+ context.$qrl$ = qrl;
10110
+ try {
10111
+ return fn.apply(this, args);
10112
+ }
10113
+ finally {
10114
+ context.$qrl$ = prevQrl;
10115
+ }
9889
10116
  }
9890
10117
  context = newInvokeContext();
9891
10118
  context.$qrl$ = qrl;
@@ -9913,11 +10140,11 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9913
10140
  const start = now();
9914
10141
  const ctx = tryGetInvokeContext();
9915
10142
  if (symbolFn !== null) {
9916
- symbolRef = symbolFn().then((module) => (qrl.resolved = symbolRef = wrapFn(module[symbol])));
10143
+ symbolRef = symbolFn().then((module) => (qrl.resolved = wrapFn((symbolRef = module[symbol]))));
9917
10144
  }
9918
10145
  else {
9919
10146
  const imported = getPlatform().importSymbol(_containerEl, chunk, symbol);
9920
- symbolRef = maybeThen(imported, (ref) => (qrl.resolved = symbolRef = wrapFn(ref)));
10147
+ symbolRef = maybeThen(imported, (ref) => (qrl.resolved = wrapFn((symbolRef = ref))));
9921
10148
  }
9922
10149
  if (typeof symbolRef === 'object' && isPromise(symbolRef)) {
9923
10150
  symbolRef.then(() => emitUsedSymbol(symbol, ctx?.$element$, start), (err) => {
@@ -9940,10 +10167,9 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9940
10167
  return invoke;
9941
10168
  }
9942
10169
  };
9943
- const resolvedSymbol = refSymbol ?? symbol;
9944
- const hash = getSymbolHash(resolvedSymbol);
10170
+ const hash = getSymbolHash(symbol);
9945
10171
  Object.assign(qrl, {
9946
- getSymbol: () => resolvedSymbol,
10172
+ getSymbol: () => symbol,
9947
10173
  getHash: () => hash,
9948
10174
  getCaptured: () => captureRef,
9949
10175
  resolve,
@@ -9951,7 +10177,6 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9951
10177
  $setContainer$: setContainer,
9952
10178
  $chunk$: chunk,
9953
10179
  $symbol$: symbol,
9954
- $refSymbol$: refSymbol,
9955
10180
  $hash$: hash,
9956
10181
  getFn: bindFnToContext,
9957
10182
  $capture$: capture,
@@ -9960,8 +10185,8 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9960
10185
  resolved: undefined,
9961
10186
  });
9962
10187
  if (symbolRef) {
9963
- // Replace symbolRef with (a promise for) the value or wrapped function
9964
- symbolRef = maybeThen(symbolRef, (resolved) => (qrl.resolved = symbolRef = wrapFn(resolved)));
10188
+ // Unwrap any promises
10189
+ symbolRef = maybeThen(symbolRef, (resolved) => (qrl.resolved = wrapFn((symbolRef = resolved))));
9965
10190
  }
9966
10191
  if (isDev) {
9967
10192
  Object.defineProperty(qrl, '_devOnlySymbolRef', {
@@ -9975,20 +10200,6 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9975
10200
  }
9976
10201
  return qrl;
9977
10202
  };
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
10203
  const EMITTED = /*#__PURE__*/ new Set();
9993
10204
  const emitUsedSymbol = (symbol, element, reqTime) => {
9994
10205
  if (!EMITTED.has(symbol)) {
@@ -10101,7 +10312,7 @@ const $ = (expression) => {
10101
10312
  if (!qRuntimeQrl && qDev) {
10102
10313
  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
10314
  }
10104
- return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null, null);
10315
+ return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null);
10105
10316
  };
10106
10317
  /** @private Use To avoid optimizer replacement */
10107
10318
  const dollar = $;
@@ -10127,7 +10338,7 @@ const sync$ = (fn) => {
10127
10338
  // eslint-disable-next-line no-new-func
10128
10339
  fn = new Function('return ' + fn.toString())();
10129
10340
  }
10130
- return createQRL('', SYNC_QRL, fn, null, null, null, null);
10341
+ return createQRL('', SYNC_QRL, fn, null, null, null);
10131
10342
  };
10132
10343
  /**
10133
10344
  * Extract function into a synchronously loadable QRL.
@@ -10144,7 +10355,7 @@ const _qrlSync = function (fn, serializedFn) {
10144
10355
  serializedFn = fn.toString();
10145
10356
  }
10146
10357
  fn.serialized = serializedFn;
10147
- return createQRL('', SYNC_QRL, fn, null, null, null, null);
10358
+ return createQRL('', SYNC_QRL, fn, null, null, null);
10148
10359
  };
10149
10360
 
10150
10361
  /** @internal */
@@ -10863,6 +11074,21 @@ const useVisibleTaskQrl = (qrl, opts) => {
10863
11074
  iCtx.$container$.$scheduler$(ChoreType.VISIBLE, task);
10864
11075
  }
10865
11076
  };
11077
+ const useRunTask = (task, eagerness) => {
11078
+ if (eagerness === 'intersection-observer') {
11079
+ useOn('qvisible', getTaskHandlerQrl(task));
11080
+ }
11081
+ else if (eagerness === 'document-ready') {
11082
+ useOnDocument('qinit', getTaskHandlerQrl(task));
11083
+ }
11084
+ else if (eagerness === 'document-idle') {
11085
+ useOnDocument('qidle', getTaskHandlerQrl(task));
11086
+ }
11087
+ };
11088
+ const getTaskHandlerQrl = (task) => {
11089
+ const taskHandler = createQRL(null, '_task', scheduleTask, null, null, [task]);
11090
+ return taskHandler;
11091
+ };
10866
11092
 
10867
11093
  // <docs markdown="../readme.md#useResource">
10868
11094
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
@@ -11173,5 +11399,5 @@ const PrefetchGraph = (opts = {}) => {
11173
11399
  return _jsxSorted('script', null, props, null, 0, 'prefetch-graph');
11174
11400
  };
11175
11401
 
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 };
11402
+ 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, _wrapStore, 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
11403
  //# sourceMappingURL=core.mjs.map