@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.cjs CHANGED
@@ -1,6 +1,6 @@
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
@@ -121,8 +121,8 @@
121
121
  // Keep one error, one line to make it easier to search for the error message.
122
122
  const MAP = [
123
123
  'Error while serializing class or style attributes', // 0
124
- '', // 1 unused
125
- '', // 2 unused
124
+ 'Scheduler not found', // 1
125
+ 'track() received object, without prop to track', // 2
126
126
  'Only primitive and object literals can be serialized. {{0}}', // 3
127
127
  '', // 4 unused
128
128
  'You can render over a existing q:container. Skipping render().', // 5
@@ -165,12 +165,11 @@
165
165
  "Element must have 'q:container' attribute.", // 42
166
166
  'Unknown vnode type {{0}}.', // 43
167
167
  'Materialize error: missing element: {{0}} {{1}} {{2}}', // 44
168
- 'Cannot coerce a Signal, use `.value` instead', // 46
169
- 'useComputedSignal$ QRL {{0}} {{1}} returned a Promise', // 47
170
- 'ComputedSignal is read-only', // 48
171
- 'WrappedSignal is read-only', // 49
172
- 'SsrError: Promises not expected here.', // 50
173
- 'Attribute value is unsafe for SSR', // 51
168
+ 'Cannot coerce a Signal, use `.value` instead', // 45
169
+ 'useComputedSignal$ QRL {{0}} {{1}} returned a Promise', // 46
170
+ 'ComputedSignal is read-only', // 47
171
+ 'WrappedSignal is read-only', // 48
172
+ 'Attribute value is unsafe for SSR', // 49
174
173
  ];
175
174
  let text = MAP[code] ?? '';
176
175
  if (parts.length) {
@@ -192,8 +191,8 @@
192
191
  var QError;
193
192
  (function (QError) {
194
193
  QError[QError["stringifyClassOrStyle"] = 0] = "stringifyClassOrStyle";
195
- QError[QError["UNUSED_1"] = 1] = "UNUSED_1";
196
- QError[QError["UNUSED_2"] = 2] = "UNUSED_2";
194
+ QError[QError["schedulerNotFound"] = 1] = "schedulerNotFound";
195
+ QError[QError["trackObjectWithoutProp"] = 2] = "trackObjectWithoutProp";
197
196
  QError[QError["verifySerializable"] = 3] = "verifySerializable";
198
197
  QError[QError["UNUSED_4"] = 4] = "UNUSED_4";
199
198
  QError[QError["cannotRenderOverExistingContainer"] = 5] = "cannotRenderOverExistingContainer";
@@ -240,14 +239,37 @@
240
239
  QError[QError["computedNotSync"] = 46] = "computedNotSync";
241
240
  QError[QError["computedReadOnly"] = 47] = "computedReadOnly";
242
241
  QError[QError["wrappedReadOnly"] = 48] = "wrappedReadOnly";
243
- QError[QError["promisesNotExpected"] = 49] = "promisesNotExpected";
244
- QError[QError["unsafeAttr"] = 50] = "unsafeAttr";
242
+ QError[QError["unsafeAttr"] = 49] = "unsafeAttr";
245
243
  })(QError || (QError = {}));
246
244
  const qError = (code, errorMessageArgs = []) => {
247
245
  const text = codeToText(code, ...errorMessageArgs);
248
246
  return logErrorAndStop(text, ...errorMessageArgs);
249
247
  };
250
248
 
249
+ /** QRL related utilities that you can import without importing all of Qwik. */
250
+ const SYNC_QRL = '<sync>';
251
+ /** Sync QRL is a function which is serialized into `<script q:func="qwik/json">` tag. */
252
+ const isSyncQrl = (value) => {
253
+ return isQrl$1(value) && value.$symbol$ == SYNC_QRL;
254
+ };
255
+ const isQrl$1 = (value) => {
256
+ return typeof value === 'function' && typeof value.getSymbol === 'function';
257
+ };
258
+ function assertQrl(qrl) {
259
+ if (build.isDev) {
260
+ if (!isQrl$1(qrl)) {
261
+ throw new Error('Not a QRL');
262
+ }
263
+ }
264
+ }
265
+ const getSymbolHash = (symbolName) => {
266
+ const index = symbolName.lastIndexOf('_');
267
+ if (index > -1) {
268
+ return symbolName.slice(index + 1);
269
+ }
270
+ return symbolName;
271
+ };
272
+
251
273
  /**
252
274
  * A friendly name tag for a VirtualVNode.
253
275
  *
@@ -291,11 +313,10 @@
291
313
  /** State factory of the component. */
292
314
  const OnRenderProp = 'q:renderFn';
293
315
  /** Component style content prefix */
294
- const ComponentStylesPrefixContent = '⭐️';
316
+ const ComponentStylesPrefixContent = '⚡️';
295
317
  /** `<some-element q:slot="...">` */
296
318
  const QSlot = 'q:slot';
297
- const QSlotParent = ':';
298
- const QSlotRef = 'q:sref';
319
+ const QSlotParent = 'q:sparent';
299
320
  const QSlotS = 'q:s';
300
321
  const QStyle = 'q:style';
301
322
  const QStyleSelector = 'style[q\\:style]';
@@ -303,7 +324,7 @@
303
324
  const QStylesAllSelector = QStyleSelector + ',' + QStyleSSelector;
304
325
  const QScopedStyle = 'q:sstyle';
305
326
  const QCtxAttr = 'q:ctx';
306
- const QSubscribers = 'q:subs';
327
+ const QBackRefs = 'q:brefs';
307
328
  const QFuncsPrefix = 'qFuncs_';
308
329
  const getQFuncs = (document, hash) => {
309
330
  return document[QFuncsPrefix + hash] || [];
@@ -332,6 +353,7 @@
332
353
  const ResourceEvent = 'qResource';
333
354
  const RenderEvent = 'qRender';
334
355
  const TaskEvent = 'qTask';
356
+ /** `<q:slot name="...">` */
335
357
  const QDefaultSlot = '';
336
358
  /**
337
359
  * Attribute to mark that this VNode has a pointer to itself from the `qwik/json` state.
@@ -499,11 +521,6 @@
499
521
  ? valueOrPromise.then(thenFn, shouldNotError)
500
522
  : thenFn(valueOrPromise);
501
523
  };
502
- const maybeThenPassError = (valueOrPromise, thenFn) => {
503
- return isPromise(valueOrPromise)
504
- ? valueOrPromise.then(thenFn)
505
- : thenFn(valueOrPromise);
506
- };
507
524
  const shouldNotError = (reason) => {
508
525
  throwErrorAndStop(reason);
509
526
  };
@@ -621,7 +638,7 @@
621
638
  _locale = locale;
622
639
  }
623
640
 
624
- const isQrl$1 = (value) => {
641
+ const isQrl = (value) => {
625
642
  return typeof value === 'function' && typeof value.getSymbol === 'function';
626
643
  };
627
644
 
@@ -693,21 +710,21 @@
693
710
  // Emit event
694
711
  announcedQRL.add(symbol);
695
712
  emitEvent('qprefetch', {
696
- symbols: [getSymbolHash(symbol)],
713
+ symbols: [symbol],
697
714
  bundles: chunk && [chunk],
698
715
  });
699
716
  }
700
717
  // Unwrap subscribers
701
- return createQRL(chunk, symbol, null, symbolFn, null, lexicalScopeCapture, null);
718
+ return createQRL(chunk, symbol, null, symbolFn, null, lexicalScopeCapture);
702
719
  };
703
720
  /** @internal */
704
721
  const inlinedQrl = (symbol, symbolName, lexicalScopeCapture = EMPTY_ARRAY) => {
705
722
  // Unwrap subscribers
706
- return createQRL(null, symbolName, symbol, null, null, lexicalScopeCapture, null);
723
+ return createQRL(null, symbolName, symbol, null, null, lexicalScopeCapture);
707
724
  };
708
725
  /** @internal */
709
726
  const _noopQrl = (symbolName, lexicalScopeCapture = EMPTY_ARRAY) => {
710
- return createQRL(null, symbolName, null, null, null, lexicalScopeCapture, null);
727
+ return createQRL(null, symbolName, null, null, null, lexicalScopeCapture);
711
728
  };
712
729
  /** @internal */
713
730
  const _noopQrlDEV = (symbolName, opts, lexicalScopeCapture = EMPTY_ARRAY) => {
@@ -736,6 +753,80 @@
736
753
  return symbol;
737
754
  };
738
755
 
756
+ var ChoreType;
757
+ (function (ChoreType) {
758
+ /// MASKS defining three levels of sorting
759
+ ChoreType[ChoreType["MACRO"] = 240] = "MACRO";
760
+ /* order of elements (not encoded here) */
761
+ ChoreType[ChoreType["MICRO"] = 15] = "MICRO";
762
+ /** Ensure that the QRL promise is resolved before processing next chores in the queue */
763
+ ChoreType[ChoreType["QRL_RESOLVE"] = 1] = "QRL_RESOLVE";
764
+ ChoreType[ChoreType["RUN_QRL"] = 2] = "RUN_QRL";
765
+ ChoreType[ChoreType["TASK"] = 3] = "TASK";
766
+ ChoreType[ChoreType["NODE_DIFF"] = 4] = "NODE_DIFF";
767
+ ChoreType[ChoreType["NODE_PROP"] = 5] = "NODE_PROP";
768
+ ChoreType[ChoreType["COMPONENT"] = 6] = "COMPONENT";
769
+ ChoreType[ChoreType["RECOMPUTE_AND_SCHEDULE_EFFECTS"] = 7] = "RECOMPUTE_AND_SCHEDULE_EFFECTS";
770
+ // Next macro level
771
+ ChoreType[ChoreType["JOURNAL_FLUSH"] = 16] = "JOURNAL_FLUSH";
772
+ // Next macro level
773
+ ChoreType[ChoreType["VISIBLE"] = 32] = "VISIBLE";
774
+ // Next macro level
775
+ ChoreType[ChoreType["CLEANUP_VISIBLE"] = 48] = "CLEANUP_VISIBLE";
776
+ // Next macro level
777
+ ChoreType[ChoreType["WAIT_FOR_ALL"] = 255] = "WAIT_FOR_ALL";
778
+ })(ChoreType || (ChoreType = {}));
779
+
780
+ // <docs markdown="../readme.md#useLexicalScope">
781
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
782
+ // (edit ../readme.md#useLexicalScope instead and run `pnpm docs.sync`)
783
+ /**
784
+ * Used by the Qwik Optimizer to restore the lexically scoped variables.
785
+ *
786
+ * This method should not be present in the application source code.
787
+ *
788
+ * NOTE: `useLexicalScope` method can only be used in the synchronous portion of the callback
789
+ * (before any `await` statements.)
790
+ *
791
+ * @internal
792
+ */
793
+ // </docs>
794
+ const useLexicalScope = () => {
795
+ const context = getInvokeContext();
796
+ let qrl = context.$qrl$;
797
+ if (!qrl) {
798
+ const el = context.$element$;
799
+ assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
800
+ const containerElement = _getQContainerElement(el);
801
+ assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
802
+ const container = getDomContainer(containerElement);
803
+ qrl = container.parseQRL(decodeURIComponent(String(context.$url$)));
804
+ }
805
+ else {
806
+ assertQrl(qrl);
807
+ assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
808
+ }
809
+ return qrl.$captureRef$;
810
+ };
811
+
812
+ /**
813
+ * This is called by qwik-loader to schedule a QRL. It has to be synchronous.
814
+ *
815
+ * @internal
816
+ */
817
+ const queueQRL = (...args) => {
818
+ // This will already check container
819
+ const [runQrl] = useLexicalScope();
820
+ const context = getInvokeContext();
821
+ const hostElement = context.$hostElement$;
822
+ const container = getDomContainer(hostElement);
823
+ const scheduler = container.$scheduler$;
824
+ if (!scheduler) {
825
+ throw qError(QError.schedulerNotFound);
826
+ }
827
+ return scheduler(ChoreType.RUN_QRL, hostElement, runQrl, args);
828
+ };
829
+
739
830
  /**
740
831
  * Allows to project the children of the current component. <Slot/> can only be used within the
741
832
  * context of a component defined with `component$`.
@@ -860,6 +951,47 @@
860
951
  return key.startsWith('preventdefault:');
861
952
  }
862
953
 
954
+ function getFileLocationFromJsx(jsxDev) {
955
+ if (!jsxDev) {
956
+ return null;
957
+ }
958
+ const sanitizedFileName = jsxDev.fileName?.replace(/\\/g, '/');
959
+ if (sanitizedFileName) {
960
+ return `${sanitizedFileName}:${jsxDev.lineNumber}:${jsxDev.columnNumber}`;
961
+ }
962
+ return null;
963
+ }
964
+
965
+ const styleContent = (styleId) => {
966
+ return ComponentStylesPrefixContent + styleId;
967
+ };
968
+ function hasClassAttr(props) {
969
+ for (const key in props) {
970
+ if (Object.prototype.hasOwnProperty.call(props, key) && isClassAttr(key)) {
971
+ return true;
972
+ }
973
+ }
974
+ return false;
975
+ }
976
+ function isClassAttr(key) {
977
+ return key === 'class' || key === 'className';
978
+ }
979
+ function convertScopedStyleIdsToArray(scopedStyleIds) {
980
+ return scopedStyleIds?.split(' ') ?? null;
981
+ }
982
+ function convertStyleIdsToString(scopedStyleIds) {
983
+ return Array.from(scopedStyleIds).join(' ');
984
+ }
985
+ const addComponentStylePrefix = (styleId) => {
986
+ if (styleId) {
987
+ let idx = 0;
988
+ do {
989
+ styleId = styleId.substring(0, idx) + styleContent(styleId.substring(idx));
990
+ } while ((idx = styleId.indexOf(' ', idx) + 1) !== 0);
991
+ }
992
+ return styleId || null;
993
+ };
994
+
863
995
  /** CSS properties which accept numbers but are not in units of "px". */
864
996
  const unitlessNumbers = new Set([
865
997
  'animationIterationCount',
@@ -1018,40 +1150,10 @@
1018
1150
  assertQrl(qStyles);
1019
1151
  return `${hashCode(qStyles.$hash$)}-${index}`;
1020
1152
  };
1021
- const styleContent = (styleId) => {
1022
- return ComponentStylesPrefixContent + styleId;
1023
- };
1024
-
1025
- function hasClassAttr(props) {
1026
- for (const key in props) {
1027
- if (Object.prototype.hasOwnProperty.call(props, key) && isClassAttr(key)) {
1028
- return true;
1029
- }
1030
- }
1031
- return false;
1032
- }
1033
- function isClassAttr(key) {
1034
- return key === 'class' || key === 'className';
1035
- }
1036
- function convertScopedStyleIdsToArray(scopedStyleIds) {
1037
- return scopedStyleIds?.split(' ') ?? null;
1038
- }
1039
- function convertStyleIdsToString(scopedStyleIds) {
1040
- return Array.from(scopedStyleIds).join(' ');
1041
- }
1042
- const addComponentStylePrefix = (styleId) => {
1043
- if (styleId) {
1044
- let idx = 0;
1045
- do {
1046
- styleId = styleId.substring(0, idx) + styleContent(styleId.substring(idx));
1047
- } while ((idx = styleId.indexOf(' ', idx) + 1) !== 0);
1048
- }
1049
- return styleId || null;
1050
- };
1051
1153
 
1052
1154
  const STORE_TARGET = Symbol('store.target');
1053
1155
  const STORE_HANDLER = Symbol('store.handler');
1054
- const STORE_ARRAY_PROP = Symbol('store.array');
1156
+ const STORE_ALL_PROPS = Symbol('store.all');
1055
1157
  var StoreFlags;
1056
1158
  (function (StoreFlags) {
1057
1159
  StoreFlags[StoreFlags["NONE"] = 0] = "NONE";
@@ -1126,7 +1228,7 @@
1126
1228
  }
1127
1229
  const effectSubscriber = ctx.$effectSubscriber$;
1128
1230
  if (effectSubscriber) {
1129
- addEffect(target, Array.isArray(target) ? STORE_ARRAY_PROP : prop, this, effectSubscriber);
1231
+ addStoreEffect(target, Array.isArray(target) ? STORE_ALL_PROPS : prop, this, effectSubscriber);
1130
1232
  }
1131
1233
  }
1132
1234
  if (prop === 'toString' && value === Object.prototype.toString) {
@@ -1177,7 +1279,7 @@
1177
1279
  if (ctx) {
1178
1280
  const effectSubscriber = ctx.$effectSubscriber$;
1179
1281
  if (effectSubscriber) {
1180
- addEffect(target, Array.isArray(target) ? STORE_ARRAY_PROP : prop, this, effectSubscriber);
1282
+ addStoreEffect(target, Array.isArray(target) ? STORE_ALL_PROPS : prop, this, effectSubscriber);
1181
1283
  }
1182
1284
  }
1183
1285
  }
@@ -1187,13 +1289,17 @@
1187
1289
  const ctx = tryGetInvokeContext();
1188
1290
  const effectSubscriber = ctx?.$effectSubscriber$;
1189
1291
  if (effectSubscriber) {
1190
- addEffect(target, STORE_ARRAY_PROP, this, effectSubscriber);
1292
+ addStoreEffect(target, STORE_ALL_PROPS, this, effectSubscriber);
1191
1293
  }
1192
1294
  return Reflect.ownKeys(target);
1193
1295
  }
1194
1296
  getOwnPropertyDescriptor(target, prop) {
1297
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
1195
1298
  if (Array.isArray(target) || typeof prop === 'symbol') {
1196
- return Object.getOwnPropertyDescriptor(target, prop);
1299
+ return descriptor;
1300
+ }
1301
+ if (descriptor && !descriptor.configurable) {
1302
+ return descriptor;
1197
1303
  }
1198
1304
  return {
1199
1305
  enumerable: true,
@@ -1201,355 +1307,280 @@
1201
1307
  };
1202
1308
  }
1203
1309
  }
1204
- function addEffect(target, prop, store, effectSubscriber) {
1205
- const effectsMap = (store.$effects$ ||= {});
1206
- const effects = (Object.prototype.hasOwnProperty.call(effectsMap, prop) && effectsMap[prop]) ||
1207
- (effectsMap[prop] = []);
1310
+ function addStoreEffect(target, prop, store, effectSubscription) {
1311
+ const effectsMap = (store.$effects$ ||= new Map());
1312
+ let effects = effectsMap.get(prop);
1313
+ if (!effects) {
1314
+ effects = new Set();
1315
+ effectsMap.set(prop, effects);
1316
+ }
1208
1317
  // Let's make sure that we have a reference to this effect.
1209
1318
  // Adding reference is essentially adding a subscription, so if the signal
1210
1319
  // changes we know who to notify.
1211
- ensureContainsEffect(effects, effectSubscriber);
1320
+ ensureContainsSubscription(effects, effectSubscription);
1212
1321
  // But when effect is scheduled in needs to be able to know which signals
1213
1322
  // to unsubscribe from. So we need to store the reference from the effect back
1214
1323
  // to this signal.
1215
- ensureContains(effectSubscriber, target);
1216
- // We need to add the subscriber to the effect so that we can clean it up later
1217
- ensureEffectContainsSubscriber(effectSubscriber[EffectSubscriptionsProp.EFFECT], target, store.$container$);
1324
+ ensureContainsBackRef(effectSubscription, target);
1325
+ addQrlToSerializationCtx(effectSubscription, store.$container$);
1218
1326
  }
1219
1327
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
1220
1328
  target[prop] = value;
1221
1329
  triggerEffects(currentStore.$container$, currentStore, getEffects(target, prop, currentStore.$effects$));
1222
1330
  }
1223
1331
  function getEffects(target, prop, storeEffects) {
1224
- let effectsToTrigger = storeEffects
1225
- ? Array.isArray(target)
1226
- ? Object.values(storeEffects).flatMap((effects) => effects)
1227
- : storeEffects[prop]
1228
- : null;
1229
- const storeArrayValue = storeEffects?.[STORE_ARRAY_PROP];
1332
+ let effectsToTrigger;
1333
+ if (storeEffects) {
1334
+ if (Array.isArray(target)) {
1335
+ for (const effects of storeEffects.values()) {
1336
+ effectsToTrigger ||= new Set();
1337
+ for (const effect of effects) {
1338
+ effectsToTrigger.add(effect);
1339
+ }
1340
+ }
1341
+ }
1342
+ else {
1343
+ effectsToTrigger = storeEffects.get(prop);
1344
+ }
1345
+ }
1346
+ const storeArrayValue = storeEffects?.get(STORE_ALL_PROPS);
1230
1347
  if (storeArrayValue) {
1231
- effectsToTrigger ||= [];
1232
- effectsToTrigger.push(...storeArrayValue);
1348
+ effectsToTrigger ||= new Set();
1349
+ for (const effect of storeArrayValue) {
1350
+ effectsToTrigger.add(effect);
1351
+ }
1233
1352
  }
1234
- return effectsToTrigger;
1353
+ return effectsToTrigger || null;
1235
1354
  }
1236
1355
 
1237
1356
  /**
1238
- * @internal
1239
- * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
1357
+ * Special value used to mark that a given signal needs to be computed. This is essentially a
1358
+ * "marked as dirty" flag.
1240
1359
  */
1241
- const useSequentialScope = () => {
1242
- const iCtx = useInvokeContext();
1243
- const hostElement = iCtx.$hostElement$;
1244
- const host = hostElement;
1245
- let seq = iCtx.$container$.getHostProp(host, ELEMENT_SEQ);
1246
- if (seq === null) {
1247
- seq = [];
1248
- iCtx.$container$.setHostProp(host, ELEMENT_SEQ, seq);
1249
- }
1250
- let seqIdx = iCtx.$container$.getHostProp(host, ELEMENT_SEQ_IDX);
1251
- if (seqIdx === null) {
1252
- seqIdx = 0;
1253
- }
1254
- iCtx.$container$.setHostProp(host, ELEMENT_SEQ_IDX, seqIdx + 1);
1255
- while (seq.length <= seqIdx) {
1256
- seq.push(undefined);
1257
- }
1258
- const set = (value) => {
1259
- if (qDev && qSerialize) {
1260
- verifySerializable(value);
1261
- }
1262
- return (seq[seqIdx] = value);
1263
- };
1264
- return {
1265
- val: seq[seqIdx],
1266
- set,
1267
- i: seqIdx,
1268
- iCtx,
1269
- };
1270
- };
1360
+ const NEEDS_COMPUTATION = Symbol('invalid');
1361
+ /** @internal */
1362
+ const _EFFECT_BACK_REF = Symbol('backRef');
1271
1363
 
1272
- class Subscriber {
1273
- $effectDependencies$ = null;
1364
+ /** Class for back reference to the EffectSubscription */
1365
+ class BackRef {
1366
+ [_EFFECT_BACK_REF] = null;
1274
1367
  }
1275
- function isSubscriber(value) {
1276
- return value instanceof Subscriber || value instanceof WrappedSignal;
1277
- }
1278
- function clearVNodeEffectDependencies(container, value) {
1279
- if (vnode_isElementVNode(value)) {
1280
- ensureMaterialized(value);
1368
+ function clearAllEffects(container, consumer) {
1369
+ if (vnode_isVNode(consumer) && vnode_isElementVNode(consumer)) {
1370
+ ensureMaterialized(consumer);
1281
1371
  }
1282
- const effects = vnode_getProp(value, QSubscribers, container.$getObjectById$);
1372
+ const effects = consumer[_EFFECT_BACK_REF];
1283
1373
  if (!effects) {
1284
1374
  return;
1285
1375
  }
1286
- for (let i = effects.length - 1; i >= 0; i--) {
1287
- const subscriber = effects[i];
1288
- clearEffects(subscriber, value, effects, i, container);
1289
- }
1290
- if (effects.length === 0) {
1291
- vnode_setProp(value, QSubscribers, null);
1292
- }
1293
- }
1294
- function clearSubscriberEffectDependencies(container, value) {
1295
- if (value.$effectDependencies$) {
1296
- for (let i = value.$effectDependencies$.length - 1; i >= 0; i--) {
1297
- const subscriber = value.$effectDependencies$[i];
1298
- clearEffects(subscriber, value, value.$effectDependencies$, i, container);
1299
- }
1300
- if (value.$effectDependencies$.length === 0) {
1301
- value.$effectDependencies$ = null;
1376
+ for (const [, effect] of effects) {
1377
+ const backRefs = effect[EffectSubscriptionProp.BACK_REF];
1378
+ if (!backRefs) {
1379
+ return;
1302
1380
  }
1303
- }
1304
- }
1305
- function clearEffects(subscriber, value, effectArray, indexToRemove, container) {
1306
- let subscriptionRemoved = false;
1307
- const seenSet = new Set();
1308
- if (subscriber instanceof WrappedSignal) {
1309
- subscriptionRemoved = clearSignalEffects(subscriber, value, seenSet);
1310
- }
1311
- else if (container.$storeProxyMap$.has(subscriber)) {
1312
- const store = container.$storeProxyMap$.get(subscriber);
1313
- const handler = getStoreHandler(store);
1314
- subscriptionRemoved = clearStoreEffects(handler, value);
1315
- }
1316
- if (subscriptionRemoved) {
1317
- effectArray.splice(indexToRemove, 1);
1318
- }
1319
- }
1320
- function clearSignalEffects(subscriber, value, seenSet) {
1321
- const effectSubscriptions = subscriber.$effects$;
1322
- let subscriptionRemoved = false;
1323
- if (effectSubscriptions) {
1324
- for (let i = effectSubscriptions.length - 1; i >= 0; i--) {
1325
- const effect = effectSubscriptions[i];
1326
- if (effect[EffectSubscriptionsProp.EFFECT] === value) {
1327
- effectSubscriptions.splice(i, 1);
1328
- subscriptionRemoved = true;
1381
+ for (const producer of backRefs) {
1382
+ if (producer instanceof Signal) {
1383
+ clearSignal(container, producer, effect);
1329
1384
  }
1330
- }
1331
- }
1332
- if (subscriber instanceof WrappedSignal) {
1333
- const hostElement = subscriber.$hostElement$;
1334
- if (hostElement && hostElement === value) {
1335
- subscriber.$hostElement$ = null;
1336
- }
1337
- // clear the effects of the arguments
1338
- const args = subscriber.$args$;
1339
- if (args) {
1340
- clearArgsEffects(args, subscriber, seenSet);
1341
- }
1342
- }
1343
- return subscriptionRemoved;
1344
- }
1345
- function clearStoreEffects(storeHandler, value) {
1346
- const effectSubscriptions = storeHandler.$effects$;
1347
- if (!effectSubscriptions) {
1348
- return false;
1349
- }
1350
- let subscriptionRemoved = false;
1351
- for (const key in effectSubscriptions) {
1352
- const effects = effectSubscriptions[key];
1353
- for (let i = effects.length - 1; i >= 0; i--) {
1354
- const effect = effects[i];
1355
- if (effect[EffectSubscriptionsProp.EFFECT] === value) {
1356
- effects.splice(i, 1);
1357
- subscriptionRemoved = true;
1385
+ else if (container.$storeProxyMap$.has(producer)) {
1386
+ const target = container.$storeProxyMap$.get(producer);
1387
+ const storeHandler = getStoreHandler(target);
1388
+ clearStore(storeHandler, effect);
1358
1389
  }
1359
1390
  }
1360
- if (effects.length === 0) {
1361
- delete effectSubscriptions[key];
1362
- }
1363
- }
1364
- return subscriptionRemoved;
1365
- }
1366
- function clearArgsEffects(args, subscriber, seenSet) {
1367
- for (let i = args.length - 1; i >= 0; i--) {
1368
- const arg = args[i];
1369
- clearArgEffect(arg, subscriber, seenSet);
1370
1391
  }
1371
1392
  }
1372
- function clearArgEffect(arg, subscriber, seenSet) {
1373
- if (seenSet.has(arg)) {
1374
- return;
1393
+ function clearSignal(container, producer, effect) {
1394
+ const effects = producer.$effects$;
1395
+ if (effects) {
1396
+ effects.delete(effect);
1375
1397
  }
1376
- seenSet.add(arg);
1377
- if (isSignal(arg)) {
1378
- clearSignalEffects(arg, subscriber, seenSet);
1398
+ if (producer instanceof WrappedSignal) {
1399
+ producer.$hostElement$ = null;
1400
+ clearAllEffects(container, producer);
1379
1401
  }
1380
- else if (typeof arg === 'object' && arg !== null) {
1381
- if (isStore(arg)) {
1382
- clearStoreEffects(getStoreHandler(arg), subscriber);
1383
- }
1384
- else if (isPropsProxy(arg)) {
1385
- // Separate check for props proxy, because props proxy getter could call signal getter.
1386
- // To avoid that we need to get the constProps and varProps directly
1387
- // from the props proxy object and loop over them.
1388
- const constProps = arg[_CONST_PROPS];
1389
- const varProps = arg[_VAR_PROPS];
1390
- if (constProps) {
1391
- for (const key in constProps) {
1392
- clearArgEffect(constProps[key], subscriber, seenSet);
1393
- }
1394
- }
1395
- for (const key in varProps) {
1396
- clearArgEffect(varProps[key], subscriber, seenSet);
1397
- }
1398
- }
1399
- else {
1400
- for (const key in arg) {
1401
- clearArgEffect(arg[key], subscriber, seenSet);
1402
- }
1402
+ }
1403
+ function clearStore(producer, effect) {
1404
+ const effects = producer?.$effects$;
1405
+ if (effects) {
1406
+ for (const propEffects of effects.values()) {
1407
+ propEffects.delete(effect);
1403
1408
  }
1404
1409
  }
1405
- else if (Array.isArray(arg)) {
1406
- clearArgsEffects(arg, subscriber, seenSet);
1407
- }
1408
- else ;
1409
1410
  }
1410
1411
 
1411
- /** @internal */
1412
- const useResourceQrl = (qrl, opts) => {
1413
- const { val, set, i, iCtx } = useSequentialScope();
1414
- if (val != null) {
1415
- return val;
1416
- }
1417
- assertQrl(qrl);
1418
- const container = iCtx.$container$;
1419
- const resource = createResourceReturn(container, opts);
1420
- const el = iCtx.$hostElement$;
1421
- const task = new Task(TaskFlags.DIRTY | TaskFlags.RESOURCE, i, el, qrl, resource, null);
1422
- runResource(task, container, iCtx.$hostElement$);
1423
- set(resource);
1424
- return resource;
1425
- };
1426
- // <docs markdown="../readme.md#useResource">
1412
+ // <docs markdown="../../readme.md#implicit$FirstArg">
1427
1413
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
1428
- // (edit ../readme.md#useResource instead and run `pnpm docs.sync`)
1414
+ // (edit ../../readme.md#implicit$FirstArg instead and run `pnpm docs.sync`)
1429
1415
  /**
1430
- * This method works like an async memoized function that runs whenever some tracked value changes
1431
- * and returns some data.
1416
+ * Create a `____$(...)` convenience method from `___(...)`.
1432
1417
  *
1433
- * `useResource` however returns immediate a `ResourceReturn` object that contains the data and a
1434
- * state that indicates if the data is available or not.
1418
+ * It is very common for functions to take a lazy-loadable resource as a first argument. For this
1419
+ * reason, the Qwik Optimizer automatically extracts the first argument from any function which ends
1420
+ * in `$`.
1435
1421
  *
1436
- * The status can be one of the following:
1422
+ * This means that `foo$(arg0)` and `foo($(arg0))` are equivalent with respect to Qwik Optimizer.
1423
+ * The former is just a shorthand for the latter.
1437
1424
  *
1438
- * - `pending` - the data is not yet available.
1439
- * - `resolved` - the data is available.
1440
- * - `rejected` - the data is not available due to an error or timeout.
1425
+ * For example, these function calls are equivalent:
1441
1426
  *
1442
- * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
1443
- * re-throw it (or a new Error), the resource status will never be `rejected`.
1427
+ * - `component$(() => {...})` is same as `component($(() => {...}))`
1444
1428
  *
1445
- * ### Example
1429
+ * ```tsx
1430
+ * export function myApi(callback: QRL<() => void>): void {
1431
+ * // ...
1432
+ * }
1446
1433
  *
1447
- * Example showing how `useResource` to perform a fetch to request the weather, whenever the input
1448
- * city name changes.
1434
+ * export const myApi$ = implicit$FirstArg(myApi);
1435
+ * // type of myApi$: (callback: () => void): void
1449
1436
  *
1450
- * ```tsx
1451
- * const Cmp = component$(() => {
1452
- * const cityS = useSignal('');
1437
+ * // can be used as:
1438
+ * myApi$(() => console.log('callback'));
1453
1439
  *
1454
- * const weatherResource = useResource$(async ({ track, cleanup }) => {
1455
- * const cityName = track(cityS);
1456
- * const abortController = new AbortController();
1457
- * cleanup(() => abortController.abort('cleanup'));
1458
- * const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
1459
- * signal: abortController.signal,
1460
- * });
1461
- * const data = await res.json();
1462
- * return data as { temp: number };
1463
- * });
1440
+ * // will be transpiled to:
1441
+ * // FILE: <current file>
1442
+ * myApi(qrl('./chunk-abc.js', 'callback'));
1464
1443
  *
1465
- * return (
1466
- * <div>
1467
- * <input name="city" bind:value={cityS} />
1468
- * <Resource
1469
- * value={weatherResource}
1470
- * onResolved={(weather) => {
1471
- * return <div>Temperature: {weather.temp}</div>;
1472
- * }}
1473
- * />
1474
- * </div>
1475
- * );
1476
- * });
1444
+ * // FILE: chunk-abc.js
1445
+ * export const callback = () => console.log('callback');
1477
1446
  * ```
1478
1447
  *
1448
+ * @param fn - A function that should have its first argument automatically `$`.
1479
1449
  * @public
1480
- * @see Resource
1481
- * @see ResourceReturn
1482
1450
  */
1483
1451
  // </docs>
1484
- const Resource = (props) => {
1485
- // Resource path
1486
- return _jsxSorted(Fragment, null, null, getResourceValueAsPromise(props), 0, null);
1452
+ const implicit$FirstArg = (fn) => {
1453
+ return function (first, ...rest) {
1454
+ return fn.call(null, dollar(first), ...rest);
1455
+ };
1487
1456
  };
1488
- function getResourceValueAsPromise(props) {
1489
- const resource = props.value;
1490
- if (isResourceReturn(resource)) {
1491
- const isBrowser = !isServerPlatform();
1492
- if (isBrowser) {
1493
- // create a subscription for the resource._state changes
1494
- const state = resource._state;
1495
- if (state === 'pending' && props.onPending) {
1496
- return Promise.resolve().then(useBindInvokeContext(props.onPending));
1497
- }
1498
- else if (state === 'rejected' && props.onRejected) {
1499
- return Promise.resolve(resource._error).then(useBindInvokeContext(props.onRejected));
1500
- }
1501
- else {
1502
- const resolvedValue = untrack(() => resource._resolved);
1503
- if (resolvedValue !== undefined) {
1504
- // resolved, pending without onPending prop or rejected without onRejected prop
1505
- return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved));
1506
- }
1507
- }
1508
- }
1509
- return resource.value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1510
- }
1511
- else if (isPromise(resource)) {
1512
- return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1457
+
1458
+ const createSignal$1 = (value) => {
1459
+ return new Signal(null, value);
1460
+ };
1461
+ const createComputedSignal = (qrl) => {
1462
+ throwIfQRLNotResolved(qrl);
1463
+ return new ComputedSignal(null, qrl);
1464
+ };
1465
+
1466
+ /**
1467
+ * Creates a Signal with the given value. If no value is given, the signal is created with
1468
+ * `undefined`.
1469
+ *
1470
+ * @public
1471
+ */
1472
+ const createSignal = createSignal$1;
1473
+ /** @internal */
1474
+ const createComputedQrl = createComputedSignal;
1475
+ /**
1476
+ * Create a computed signal which is calculated from the given QRL. A computed signal is a signal
1477
+ * which is calculated from other signals. When the signals change, the computed signal is
1478
+ * recalculated.
1479
+ *
1480
+ * The QRL must be a function which returns the value of the signal. The function must not have side
1481
+ * effects, and it mus be synchronous.
1482
+ *
1483
+ * If you need the function to be async, use `useSignal` and `useTask$` instead.
1484
+ *
1485
+ * @public
1486
+ */
1487
+ const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedQrl);
1488
+
1489
+ /**
1490
+ * @internal
1491
+ * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
1492
+ */
1493
+ const useSequentialScope = () => {
1494
+ const iCtx = useInvokeContext();
1495
+ const hostElement = iCtx.$hostElement$;
1496
+ const host = hostElement;
1497
+ let seq = iCtx.$container$.getHostProp(host, ELEMENT_SEQ);
1498
+ if (seq === null) {
1499
+ seq = [];
1500
+ iCtx.$container$.setHostProp(host, ELEMENT_SEQ, seq);
1513
1501
  }
1514
- else if (isSignal(resource)) {
1515
- return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1502
+ let seqIdx = iCtx.$container$.getHostProp(host, ELEMENT_SEQ_IDX);
1503
+ if (seqIdx === null) {
1504
+ seqIdx = 0;
1516
1505
  }
1517
- else {
1518
- return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1506
+ iCtx.$container$.setHostProp(host, ELEMENT_SEQ_IDX, seqIdx + 1);
1507
+ while (seq.length <= seqIdx) {
1508
+ seq.push(undefined);
1519
1509
  }
1520
- }
1521
- const _createResourceReturn = (opts) => {
1522
- const resource = {
1523
- __brand: 'resource',
1524
- value: undefined,
1525
- loading: isServerPlatform() ? false : true,
1526
- _resolved: undefined,
1527
- _error: undefined,
1528
- _state: 'pending',
1529
- _timeout: opts?.timeout ?? -1,
1530
- _cache: 0,
1510
+ const set = (value) => {
1511
+ if (qDev && qSerialize) {
1512
+ verifySerializable(value);
1513
+ }
1514
+ return (seq[seqIdx] = value);
1515
+ };
1516
+ return {
1517
+ val: seq[seqIdx],
1518
+ set,
1519
+ i: seqIdx,
1520
+ iCtx,
1531
1521
  };
1532
- return resource;
1533
- };
1534
- const createResourceReturn = (container, opts, initialPromise) => {
1535
- const result = _createResourceReturn(opts);
1536
- result.value = initialPromise;
1537
- return createStore(container, result, StoreFlags.RECURSIVE);
1538
1522
  };
1539
- const isResourceReturn = (obj) => {
1540
- return isObject(obj) && (getStoreTarget(obj) || obj).__brand === 'resource';
1523
+
1524
+ function getSubscriber(effect, prop, data) {
1525
+ if (!effect[_EFFECT_BACK_REF]) {
1526
+ if (build.isServer && isSsrNode(effect)) {
1527
+ effect.setProp(QBackRefs, new Map());
1528
+ }
1529
+ else {
1530
+ effect[_EFFECT_BACK_REF] = new Map();
1531
+ }
1532
+ }
1533
+ const subMap = effect[_EFFECT_BACK_REF];
1534
+ let sub = subMap.get(prop);
1535
+ if (!sub) {
1536
+ sub = [effect, prop];
1537
+ subMap.set(prop, sub);
1538
+ }
1539
+ if (data) {
1540
+ sub[EffectSubscriptionProp.DATA] = data;
1541
+ }
1542
+ return sub;
1543
+ }
1544
+ function isSsrNode(value) {
1545
+ return '__brand__' in value && 'currentComponentNode' in value;
1546
+ }
1547
+
1548
+ var TaskFlags;
1549
+ (function (TaskFlags) {
1550
+ TaskFlags[TaskFlags["VISIBLE_TASK"] = 1] = "VISIBLE_TASK";
1551
+ TaskFlags[TaskFlags["TASK"] = 2] = "TASK";
1552
+ TaskFlags[TaskFlags["RESOURCE"] = 4] = "RESOURCE";
1553
+ TaskFlags[TaskFlags["DIRTY"] = 8] = "DIRTY";
1554
+ })(TaskFlags || (TaskFlags = {}));
1555
+ /** @internal */
1556
+ const useTaskQrl = (qrl) => {
1557
+ const { val, set, iCtx, i } = useSequentialScope();
1558
+ if (val) {
1559
+ return;
1560
+ }
1561
+ assertQrl(qrl);
1562
+ set(1);
1563
+ const task = new Task(TaskFlags.DIRTY | TaskFlags.TASK, i, iCtx.$hostElement$, qrl, undefined, null);
1564
+ // In V2 we add the task to the sequential scope. We need to do this
1565
+ // in order to be able to retrieve it later when the parent element is
1566
+ // deleted and we need to be able to release the task subscriptions.
1567
+ set(task);
1568
+ const container = iCtx.$container$;
1569
+ const promise = container.$scheduler$(ChoreType.TASK, task);
1570
+ if (isPromise(promise)) {
1571
+ // TODO: should we handle this differently?
1572
+ promise.catch(() => { });
1573
+ }
1541
1574
  };
1542
- const runResource = (task, container, host) => {
1575
+ const runTask = (task, container, host) => {
1543
1576
  task.$flags$ &= ~TaskFlags.DIRTY;
1544
1577
  cleanupTask(task);
1545
- const iCtx = newInvokeContext(container.$locale$, host, undefined, ResourceEvent);
1578
+ const iCtx = newInvokeContext(container.$locale$, host, undefined, TaskEvent);
1546
1579
  iCtx.$container$ = container;
1547
- const taskFn = task.$qrl$.getFn(iCtx, () => clearSubscriberEffectDependencies(container, task));
1548
- const resource = task.$state$;
1549
- assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
1580
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
1550
1581
  const track = (obj, prop) => {
1551
1582
  const ctx = newInvokeContext();
1552
- ctx.$effectSubscriber$ = [task, EffectProperty.COMPONENT];
1583
+ ctx.$effectSubscriber$ = getSubscriber(task, EffectProperty.COMPONENT);
1553
1584
  ctx.$container$ = container;
1554
1585
  return invoke(ctx, () => {
1555
1586
  if (isFunction(obj)) {
@@ -1561,116 +1592,91 @@
1561
1592
  else if (isSignal(obj)) {
1562
1593
  return obj.value;
1563
1594
  }
1564
- else {
1595
+ else if (isStore(obj)) {
1596
+ // track whole store
1597
+ addStoreEffect(getStoreTarget(obj), STORE_ALL_PROPS, getStoreHandler(obj), ctx.$effectSubscriber$);
1565
1598
  return obj;
1566
1599
  }
1600
+ else {
1601
+ throw qError(QError.trackObjectWithoutProp);
1602
+ }
1567
1603
  });
1568
1604
  };
1569
1605
  const handleError = (reason) => container.handleError(reason, host);
1570
- const cleanups = [];
1571
- task.$destroy$ = noSerialize(() => {
1572
- cleanups.forEach((fn) => {
1573
- try {
1574
- fn();
1575
- }
1576
- catch (err) {
1577
- handleError(err);
1606
+ let cleanupFns = null;
1607
+ const cleanup = (fn) => {
1608
+ if (typeof fn == 'function') {
1609
+ if (!cleanupFns) {
1610
+ cleanupFns = [];
1611
+ task.$destroy$ = noSerialize(() => {
1612
+ task.$destroy$ = null;
1613
+ cleanupFns.forEach((fn) => {
1614
+ try {
1615
+ fn();
1616
+ }
1617
+ catch (err) {
1618
+ handleError(err);
1619
+ }
1620
+ });
1621
+ });
1578
1622
  }
1579
- });
1580
- done = true;
1623
+ cleanupFns.push(fn);
1624
+ }
1625
+ };
1626
+ const taskApi = { track, cleanup };
1627
+ const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
1628
+ // If a Promise is thrown, that means we need to re-run the task.
1629
+ if (isPromise(err)) {
1630
+ return err.then(() => runTask(task, container, host));
1631
+ }
1632
+ else {
1633
+ throw err;
1634
+ }
1581
1635
  });
1582
- const resourceTarget = unwrapStore(resource);
1583
- const opts = {
1584
- track,
1585
- cleanup(fn) {
1586
- if (typeof fn === 'function') {
1587
- cleanups.push(fn);
1588
- }
1589
- },
1590
- cache(policy) {
1591
- let milliseconds = 0;
1592
- if (policy === 'immutable') {
1593
- milliseconds = Infinity;
1594
- }
1595
- else {
1596
- milliseconds = policy;
1597
- }
1598
- resource._cache = milliseconds;
1599
- },
1600
- previous: resourceTarget._resolved,
1601
- };
1602
- let resolve;
1603
- let reject;
1604
- let done = false;
1605
- const setState = (resolved, value) => {
1606
- if (!done) {
1607
- done = true;
1608
- if (resolved) {
1609
- done = true;
1610
- resource.loading = false;
1611
- resource._state = 'resolved';
1612
- resource._resolved = value;
1613
- resource._error = undefined;
1614
- resolve(value);
1615
- }
1616
- else {
1617
- done = true;
1618
- resource.loading = false;
1619
- resource._state = 'rejected';
1620
- resource._error = value;
1621
- reject(value);
1622
- }
1623
- return true;
1624
- }
1625
- return false;
1626
- };
1627
- /**
1628
- * Add cleanup to resolve the resource if we are trying to run the same resource again while the
1629
- * previous one is not resolved yet. The next `runResource` run will call this cleanup
1630
- */
1631
- cleanups.push(() => {
1632
- if (untrack(() => resource.loading) === true) {
1633
- const value = untrack(() => resource._resolved);
1634
- setState(true, value);
1635
- }
1636
- });
1637
- // Execute mutation inside empty invocation
1638
- invoke(iCtx, () => {
1639
- // console.log('RESOURCE.pending: ');
1640
- resource._state = 'pending';
1641
- resource.loading = !isServerPlatform();
1642
- const promise = (resource.value = new Promise((r, re) => {
1643
- resolve = r;
1644
- reject = re;
1645
- }));
1646
- promise.catch(ignoreErrorToPreventNodeFromCrashing);
1647
- });
1648
- const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
1649
- setState(true, value);
1650
- }, (err) => {
1651
- if (isPromise(err)) {
1652
- return err.then(() => runResource(task, container, host));
1636
+ return result;
1637
+ };
1638
+ const cleanupTask = (task) => {
1639
+ const destroy = task.$destroy$;
1640
+ if (destroy) {
1641
+ task.$destroy$ = null;
1642
+ try {
1643
+ destroy();
1653
1644
  }
1654
- else {
1655
- setState(false, err);
1645
+ catch (err) {
1646
+ logError(err);
1656
1647
  }
1657
- });
1658
- const timeout = resourceTarget._timeout;
1659
- if (timeout > 0) {
1660
- return Promise.race([
1661
- promise,
1662
- delay(timeout).then(() => {
1663
- if (setState(false, new Error('timeout'))) {
1664
- cleanupTask(task);
1665
- }
1666
- }),
1667
- ]);
1668
1648
  }
1669
- return promise;
1670
1649
  };
1671
- const ignoreErrorToPreventNodeFromCrashing = (err) => {
1672
- // ignore error to prevent node from crashing
1673
- // node will crash in promise is rejected and no one is listening to the rejection.
1650
+ class Task extends BackRef {
1651
+ $flags$;
1652
+ $index$;
1653
+ $el$;
1654
+ $qrl$;
1655
+ $state$;
1656
+ $destroy$;
1657
+ constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
1658
+ super();
1659
+ this.$flags$ = $flags$;
1660
+ this.$index$ = $index$;
1661
+ this.$el$ = $el$;
1662
+ this.$qrl$ = $qrl$;
1663
+ this.$state$ = $state$;
1664
+ this.$destroy$ = $destroy$;
1665
+ }
1666
+ }
1667
+ const isTask = (value) => {
1668
+ return value instanceof Task;
1669
+ };
1670
+ /**
1671
+ * Used internally as a qrl event handler to schedule a task.
1672
+ *
1673
+ * @internal
1674
+ */
1675
+ const scheduleTask = (_event, element) => {
1676
+ const [task] = useLexicalScope();
1677
+ const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK;
1678
+ const container = getDomContainer(element);
1679
+ container.$scheduler$(type, task);
1674
1680
  };
1675
1681
 
1676
1682
  /** @file Public types for the client deserialization */
@@ -1748,22 +1754,67 @@
1748
1754
  VirtualVNodeProps[VirtualVNodeProps["PROPS_OFFSET"] = 6] = "PROPS_OFFSET";
1749
1755
  })(VirtualVNodeProps || (VirtualVNodeProps = {}));
1750
1756
 
1751
- const isForeignObjectElement = (elementName) => elementName.toLowerCase() === 'foreignobject';
1757
+ const mapApp_findIndx = (array, key, start) => {
1758
+ assertTrue(start % 2 === 0, 'Expecting even number.');
1759
+ let bottom = start >> 1;
1760
+ let top = (array.length - 2) >> 1;
1761
+ while (bottom <= top) {
1762
+ const mid = bottom + ((top - bottom) >> 1);
1763
+ const midKey = array[mid << 1];
1764
+ if (midKey === key) {
1765
+ return mid << 1;
1766
+ }
1767
+ if (midKey < key) {
1768
+ bottom = mid + 1;
1769
+ }
1770
+ else {
1771
+ top = mid - 1;
1772
+ }
1773
+ }
1774
+ return (bottom << 1) ^ -1;
1775
+ };
1776
+ const mapArray_set = (array, key, value, start) => {
1777
+ const indx = mapApp_findIndx(array, key, start);
1778
+ if (indx >= 0) {
1779
+ if (value == null) {
1780
+ array.splice(indx, 2);
1781
+ }
1782
+ else {
1783
+ array[indx + 1] = value;
1784
+ }
1785
+ }
1786
+ else if (value != null) {
1787
+ array.splice(indx ^ -1, 0, key, value);
1788
+ }
1789
+ };
1790
+ const mapArray_get = (array, key, start) => {
1791
+ const indx = mapApp_findIndx(array, key, start);
1792
+ if (indx >= 0) {
1793
+ return array[indx + 1];
1794
+ }
1795
+ else {
1796
+ return null;
1797
+ }
1798
+ };
1799
+
1800
+ const isForeignObjectElement = (elementName) => {
1801
+ return build.isDev ? elementName.toLowerCase() === 'foreignobject' : elementName === 'foreignObject';
1802
+ };
1752
1803
  const isSvgElement = (elementName) => elementName === 'svg' || isForeignObjectElement(elementName);
1753
1804
  const isMathElement = (elementName) => elementName === 'math';
1754
1805
  const vnode_isDefaultNamespace = (vnode) => {
1755
1806
  const flags = vnode[VNodeProps.flags];
1756
1807
  return (flags & VNodeFlags.NAMESPACE_MASK) === 0;
1757
1808
  };
1758
- const vnode_getElementNamespaceFlags = (elementName) => {
1759
- if (isSvgElement(elementName)) {
1760
- return VNodeFlags.NS_svg;
1761
- }
1762
- else if (isMathElement(elementName)) {
1763
- return VNodeFlags.NS_math;
1764
- }
1765
- else {
1766
- return VNodeFlags.NS_html;
1809
+ const vnode_getElementNamespaceFlags = (element) => {
1810
+ const namespace = fastNamespaceURI(element);
1811
+ switch (namespace) {
1812
+ case SVG_NS:
1813
+ return VNodeFlags.NS_svg;
1814
+ case MATH_NS:
1815
+ return VNodeFlags.NS_math;
1816
+ default:
1817
+ return VNodeFlags.NS_html;
1767
1818
  }
1768
1819
  };
1769
1820
  function vnode_getDomChildrenWithCorrectNamespacesToInsert(journal, domParentVNode, newChild) {
@@ -2117,19 +2168,21 @@
2117
2168
  * @returns
2118
2169
  */
2119
2170
  const executeComponent = (container, renderHost, subscriptionHost, componentQRL, props) => {
2120
- const iCtx = newInvokeContext(container.$locale$, subscriptionHost, undefined, RenderEvent);
2121
- iCtx.$effectSubscriber$ = [subscriptionHost, EffectProperty.COMPONENT];
2122
- iCtx.$container$ = container;
2171
+ const iCtx = newInvokeContext(container.$locale$, subscriptionHost || undefined, undefined, RenderEvent);
2172
+ if (subscriptionHost) {
2173
+ iCtx.$effectSubscriber$ = getSubscriber(subscriptionHost, EffectProperty.COMPONENT);
2174
+ iCtx.$container$ = container;
2175
+ }
2123
2176
  let componentFn;
2124
2177
  container.ensureProjectionResolved(renderHost);
2125
2178
  let isInlineComponent = false;
2126
2179
  if (componentQRL === null) {
2127
- componentQRL = componentQRL || container.getHostProp(renderHost, OnRenderProp);
2180
+ componentQRL = container.getHostProp(renderHost, OnRenderProp);
2128
2181
  assertDefined(componentQRL, 'No Component found at this location');
2129
2182
  }
2130
- if (isQrl(componentQRL)) {
2183
+ if (isQrl$1(componentQRL)) {
2131
2184
  props = props || container.getHostProp(renderHost, ELEMENT_PROPS) || EMPTY_OBJ;
2132
- if (props && props.children) {
2185
+ if (props.children) {
2133
2186
  delete props.children;
2134
2187
  }
2135
2188
  componentFn = componentQRL.getFn(iCtx);
@@ -2147,18 +2200,16 @@
2147
2200
  if (!isInlineComponent) {
2148
2201
  container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
2149
2202
  container.setHostProp(renderHost, USE_ON_LOCAL_SEQ_IDX, null);
2150
- if (container.getHostProp(renderHost, ELEMENT_PROPS) !== props) {
2151
- container.setHostProp(renderHost, ELEMENT_PROPS, props);
2152
- }
2203
+ container.setHostProp(renderHost, ELEMENT_PROPS, props);
2153
2204
  }
2154
2205
  if (vnode_isVNode(renderHost)) {
2155
- clearVNodeEffectDependencies(container, renderHost);
2206
+ clearAllEffects(container, renderHost);
2156
2207
  }
2157
2208
  return componentFn(props);
2158
2209
  }, (jsx) => {
2159
2210
  const useOnEvents = container.getHostProp(renderHost, USE_ON_LOCAL);
2160
2211
  if (useOnEvents) {
2161
- return maybeThen(addUseOnEvents(jsx, useOnEvents), () => jsx);
2212
+ return addUseOnEvents(jsx, useOnEvents);
2162
2213
  }
2163
2214
  return jsx;
2164
2215
  }, (err) => {
@@ -2184,6 +2235,7 @@
2184
2235
  */
2185
2236
  function addUseOnEvents(jsx, useOnEvents) {
2186
2237
  const jsxElement = findFirstStringJSX(jsx);
2238
+ let jsxResult = jsx;
2187
2239
  return maybeThen(jsxElement, (jsxElement) => {
2188
2240
  let isInvisibleComponent = false;
2189
2241
  if (!jsxElement) {
@@ -2202,13 +2254,15 @@
2202
2254
  if (Object.prototype.hasOwnProperty.call(useOnEvents, key)) {
2203
2255
  if (isInvisibleComponent) {
2204
2256
  if (key === 'onQvisible$') {
2205
- jsxElement = addScriptNodeForInvisibleComponents(jsx);
2257
+ const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2258
+ jsxResult = jsx;
2206
2259
  if (jsxElement) {
2207
2260
  addUseOnEvent(jsxElement, 'document:onQinit$', useOnEvents[key]);
2208
2261
  }
2209
2262
  }
2210
2263
  else if (key.startsWith('document:') || key.startsWith('window:')) {
2211
- jsxElement = addScriptNodeForInvisibleComponents(jsx);
2264
+ const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2265
+ jsxResult = jsx;
2212
2266
  if (jsxElement) {
2213
2267
  addUseOnEvent(jsxElement, key, useOnEvents[key]);
2214
2268
  }
@@ -2226,7 +2280,7 @@
2226
2280
  }
2227
2281
  }
2228
2282
  }
2229
- return jsxElement;
2283
+ return jsxResult;
2230
2284
  });
2231
2285
  }
2232
2286
  function addUseOnEvent(jsxElement, key, value) {
@@ -2272,6 +2326,9 @@
2272
2326
  type: 'placeholder',
2273
2327
  hidden: '',
2274
2328
  }, null, 3);
2329
+ if (jsx.type === Slot) {
2330
+ return [jsxElement, _jsxSorted(Fragment, null, null, [jsx, jsxElement], 0, null)];
2331
+ }
2275
2332
  if (jsx.children == null) {
2276
2333
  jsx.children = jsxElement;
2277
2334
  }
@@ -2281,29 +2338,46 @@
2281
2338
  else {
2282
2339
  jsx.children = [jsx.children, jsxElement];
2283
2340
  }
2284
- return jsxElement;
2341
+ return [jsxElement, jsx];
2285
2342
  }
2286
2343
  else if (Array.isArray(jsx) && jsx.length) {
2287
2344
  // get first element
2288
- return addScriptNodeForInvisibleComponents(jsx[0]);
2345
+ const [jsxElement, _] = addScriptNodeForInvisibleComponents(jsx[0]);
2346
+ return [jsxElement, jsx];
2289
2347
  }
2290
- return null;
2348
+ return [null, null];
2291
2349
  }
2292
2350
 
2351
+ /** @internal */
2352
+ const _CONST_PROPS = Symbol('CONST');
2353
+ /** @internal */
2354
+ const _VAR_PROPS = Symbol('VAR');
2355
+ /** @internal @deprecated v1 compat */
2356
+ const _IMMUTABLE = Symbol('IMMUTABLE');
2357
+
2293
2358
  function isSlotProp(prop) {
2294
2359
  return !prop.startsWith('q:') && !prop.startsWith(NON_SERIALIZABLE_MARKER_PREFIX);
2295
2360
  }
2296
- function isParentSlotProp(prop) {
2297
- return prop.startsWith(QSlotParent);
2298
- }
2299
2361
  /** @internal */
2300
2362
  const _restProps = (props, omit, target = {}) => {
2301
- for (const key in props) {
2363
+ let constPropsTarget = null;
2364
+ const constProps = props[_CONST_PROPS];
2365
+ if (constProps) {
2366
+ for (const key in constProps) {
2367
+ if (!omit.includes(key)) {
2368
+ constPropsTarget ||= {};
2369
+ constPropsTarget[key] = constProps[key];
2370
+ }
2371
+ }
2372
+ }
2373
+ const varPropsTarget = target;
2374
+ const varProps = props[_VAR_PROPS];
2375
+ for (const key in varProps) {
2302
2376
  if (!omit.includes(key)) {
2303
- target[key] = props[key];
2377
+ varPropsTarget[key] = varProps[key];
2304
2378
  }
2305
2379
  }
2306
- return target;
2380
+ return createPropsProxy(varPropsTarget, constPropsTarget);
2307
2381
  };
2308
2382
 
2309
2383
  function escapeHTML(html) {
@@ -2345,17 +2419,6 @@
2345
2419
  }
2346
2420
  }
2347
2421
 
2348
- function getFileLocationFromJsx(jsxDev) {
2349
- if (!jsxDev) {
2350
- return null;
2351
- }
2352
- const sanitizedFileName = jsxDev.fileName?.replace(/\\/g, '/');
2353
- if (sanitizedFileName) {
2354
- return `${sanitizedFileName}:${jsxDev.lineNumber}:${jsxDev.columnNumber}`;
2355
- }
2356
- return null;
2357
- }
2358
-
2359
2422
  const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2360
2423
  let journal = container.$journal$;
2361
2424
  /**
@@ -2374,9 +2437,9 @@
2374
2437
  /// (Node can be null, if we are at the end of the list.)
2375
2438
  let vCurrent = null;
2376
2439
  /// When we insert new node we start it here so that we can descend into it.
2377
- /// NOTE: it can't be stored in `vCurrent` because `vNewCurrent` is in journal
2440
+ /// NOTE: it can't be stored in `vCurrent` because `vNewNode` is in journal
2378
2441
  /// and is not connected to the tree.
2379
- let vNewNode = null; // TODO: delete, because journal is on vNode, the above comment no longer applies
2442
+ let vNewNode = null;
2380
2443
  /// When elements have keys they can be consumed out of order and therefore we can't use nextSibling.
2381
2444
  /// In such a case this array will contain the elements after the current location.
2382
2445
  /// The array even indices will contains keys and odd indices the vNode.
@@ -2429,7 +2492,7 @@
2429
2492
  }
2430
2493
  else if (isSignal(jsxValue)) {
2431
2494
  if (vCurrent) {
2432
- clearVNodeEffectDependencies(container, vCurrent);
2495
+ clearAllEffects(container, vCurrent);
2433
2496
  }
2434
2497
  expectVirtual(VirtualType.WrappedSignal, null);
2435
2498
  descend(trackSignalAndAssignHost(jsxValue, (vNewNode || vCurrent), EffectProperty.VNODE, container), true);
@@ -2644,9 +2707,10 @@
2644
2707
  };
2645
2708
  const projections = [];
2646
2709
  if (host) {
2710
+ const props = vnode_getProps(host);
2647
2711
  // we need to create empty projections for all the slots to remove unused slots content
2648
- for (let i = vnode_getPropStartIndex(host); i < host.length; i = i + 2) {
2649
- const prop = host[i];
2712
+ for (let i = 0; i < props.length; i = i + 2) {
2713
+ const prop = props[i];
2650
2714
  if (isSlotProp(prop)) {
2651
2715
  const slotName = prop;
2652
2716
  projections.push(slotName);
@@ -2687,6 +2751,8 @@
2687
2751
  // console.log('expectProjection', JSON.stringify(slotName));
2688
2752
  vCurrent = vnode_getProp(vParent, // The parent is the component and it should have our portal.
2689
2753
  slotName, (id) => vnode_locate(container.rootVNode, id));
2754
+ // if projection is marked as deleted then we need to create a new one
2755
+ vCurrent = vCurrent && vCurrent[VNodeProps.flags] & VNodeFlags.Deleted ? null : vCurrent;
2690
2756
  if (vCurrent == null) {
2691
2757
  vNewNode = vnode_newVirtual();
2692
2758
  // you may be tempted to add the projection into the current parent, but
@@ -2723,6 +2789,19 @@
2723
2789
  }
2724
2790
  else if (vProjectedNode === vCurrent) ;
2725
2791
  else {
2792
+ const parent = vnode_getParent(vProjectedNode);
2793
+ const isAlreadyProjected = !!parent && !(vnode_isElementVNode(parent) && vnode_getElementName(parent) === QTemplate);
2794
+ if (isAlreadyProjected && vParent !== parent) {
2795
+ /**
2796
+ * The node is already projected, but structure has been changed. In next steps we will
2797
+ * insert the vProjectedNode at the end. However we will find existing projection elements
2798
+ * (from already projected THE SAME projection as vProjectedNode!) during
2799
+ * vnode_insertBefore. We need to remove vnode from the vnode tree to avoid referencing it
2800
+ * to self and cause infinite loop. Don't remove it from DOM to avoid additional operations
2801
+ * and flickering.
2802
+ */
2803
+ vnode_remove(journal, parent, vProjectedNode, false);
2804
+ }
2726
2805
  // move from q:template to the target node
2727
2806
  vnode_insertBefore(journal, vParent, (vNewNode = vProjectedNode), vCurrent && getInsertBefore());
2728
2807
  vnode_setProp(vNewNode, QSlot, slotNameKey);
@@ -2776,8 +2855,8 @@
2776
2855
  while (vCurrent) {
2777
2856
  const toRemove = vCurrent;
2778
2857
  advanceToNextSibling();
2779
- cleanup(container, toRemove);
2780
2858
  if (vParent === vnode_getParent(toRemove)) {
2859
+ cleanup(container, toRemove);
2781
2860
  // If we are diffing projection than the parent is not the parent of the node.
2782
2861
  // If that is the case we don't want to remove the node from the parent.
2783
2862
  vnode_remove(journal, vParent, toRemove, true);
@@ -2814,10 +2893,19 @@
2814
2893
  // But we need to mark them so that they don't get pulled into the diff.
2815
2894
  const eventName = getEventNameFromJsxProp(key);
2816
2895
  const scope = getEventNameScopeFromJsxProp(key);
2817
- vnode_setProp(vNewNode, HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
2818
2896
  if (eventName) {
2897
+ vnode_setProp(vNewNode, HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
2819
2898
  registerQwikLoaderEvent(eventName);
2820
2899
  }
2900
+ if (scope) {
2901
+ // add an event attr with empty value for qwikloader element selector.
2902
+ // We don't need value here. For ssr this value is a QRL,
2903
+ // but for CSR value should be just empty
2904
+ const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
2905
+ if (htmlEvent) {
2906
+ vnode_setAttr(journal, vNewNode, htmlEvent, '');
2907
+ }
2908
+ }
2821
2909
  needsQDispatchEventPatch = true;
2822
2910
  continue;
2823
2911
  }
@@ -2830,12 +2918,15 @@
2830
2918
  value(element);
2831
2919
  continue;
2832
2920
  }
2921
+ else if (value == null) {
2922
+ continue;
2923
+ }
2833
2924
  else {
2834
2925
  throw qError(QError.invalidRefValue, [currentFile]);
2835
2926
  }
2836
2927
  }
2837
2928
  if (isSignal(value)) {
2838
- const signalData = new EffectPropData({
2929
+ const signalData = new SubscriptionData({
2839
2930
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
2840
2931
  $isConst$: true,
2841
2932
  });
@@ -2939,7 +3030,7 @@
2939
3030
  let returnValue = false;
2940
3031
  qrls.flat(2).forEach((qrl) => {
2941
3032
  if (qrl) {
2942
- const value = qrl(event, element);
3033
+ const value = container.$scheduler$(ChoreType.RUN_QRL, vNode, qrl, [event, element]);
2943
3034
  returnValue = returnValue || value === true;
2944
3035
  }
2945
3036
  });
@@ -2951,10 +3042,10 @@
2951
3042
  /** @param tag Returns true if `qDispatchEvent` needs patching */
2952
3043
  function setBulkProps(vnode, srcAttrs, currentFile) {
2953
3044
  vnode_ensureElementInflated(vnode);
2954
- const dstAttrs = vnode;
3045
+ const dstAttrs = vnode_getProps(vnode);
2955
3046
  let srcIdx = 0;
2956
3047
  const srcLength = srcAttrs.length;
2957
- let dstIdx = ElementVNodeProps.PROPS_OFFSET;
3048
+ let dstIdx = 0;
2958
3049
  let dstLength = dstAttrs.length;
2959
3050
  let srcKey = srcIdx < srcLength ? srcAttrs[srcIdx++] : null;
2960
3051
  let dstKey = dstIdx < dstLength ? dstAttrs[dstIdx++] : null;
@@ -2974,12 +3065,15 @@
2974
3065
  value(element);
2975
3066
  return;
2976
3067
  }
3068
+ else if (value == null) {
3069
+ return;
3070
+ }
2977
3071
  else {
2978
3072
  throw qError(QError.invalidRefValue, [currentFile]);
2979
3073
  }
2980
3074
  }
2981
3075
  if (isSignal(value)) {
2982
- const signalData = new EffectPropData({
3076
+ const signalData = new SubscriptionData({
2983
3077
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
2984
3078
  $isConst$: false,
2985
3079
  });
@@ -2993,21 +3087,21 @@
2993
3087
  };
2994
3088
  const recordJsxEvent = (key, value) => {
2995
3089
  const eventName = getEventNameFromJsxProp(key);
3090
+ const scope = getEventNameScopeFromJsxProp(key);
2996
3091
  if (eventName) {
2997
- const scope = getEventNameScopeFromJsxProp(key);
2998
3092
  record(':' + scope + ':' + eventName, value);
2999
- }
3000
- // add an event attr with empty value for qwikloader element selector.
3001
- // We don't need value here. For ssr this value is a QRL,
3002
- // but for CSR value should be just empty
3003
- const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
3004
- if (htmlEvent) {
3005
- record(htmlEvent, '');
3006
- }
3007
- // register an event for qwik loader
3008
- if (eventName) {
3093
+ // register an event for qwik loader
3009
3094
  registerQwikLoaderEvent(eventName);
3010
3095
  }
3096
+ if (scope) {
3097
+ // add an event attr with empty value for qwikloader element selector.
3098
+ // We don't need value here. For ssr this value is a QRL,
3099
+ // but for CSR value should be just empty
3100
+ const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
3101
+ if (htmlEvent) {
3102
+ record(htmlEvent, '');
3103
+ }
3104
+ }
3011
3105
  };
3012
3106
  while (srcKey !== null || dstKey !== null) {
3013
3107
  if (dstKey?.startsWith(HANDLER_PREFIX) || dstKey?.startsWith(Q_PREFIX)) {
@@ -3202,14 +3296,8 @@
3202
3296
  }
3203
3297
  else if (!hashesAreEqual) {
3204
3298
  insertNewComponent(host, componentQRL, jsxProps);
3205
- if (vNewNode) {
3206
- if (host) {
3207
- // TODO(varixo): not sure why we need to copy flags here.
3208
- vNewNode[VNodeProps.flags] = host[VNodeProps.flags];
3209
- }
3210
- host = vNewNode;
3211
- shouldRender = true;
3212
- }
3299
+ host = vNewNode;
3300
+ shouldRender = true;
3213
3301
  }
3214
3302
  if (host) {
3215
3303
  const vNodeProps = vnode_getProp(host, ELEMENT_PROPS, container.$getObjectById$);
@@ -3230,6 +3318,7 @@
3230
3318
  const lookupKey = jsxNode.key;
3231
3319
  const vNodeLookupKey = getKey(host);
3232
3320
  const lookupKeysAreEqual = lookupKey === vNodeLookupKey;
3321
+ const vNodeComponentHash = getComponentHash(host, container.$getObjectById$);
3233
3322
  if (!lookupKeysAreEqual) {
3234
3323
  // See if we already have this inline component later on.
3235
3324
  vNewNode = retrieveChildWithKey(null, lookupKey);
@@ -3243,6 +3332,11 @@
3243
3332
  }
3244
3333
  host = vNewNode;
3245
3334
  }
3335
+ // inline components don't have component hash - q:renderFn prop, so it should be null
3336
+ else if (vNodeComponentHash != null) {
3337
+ insertNewInlineComponent();
3338
+ host = vNewNode;
3339
+ }
3246
3340
  if (host) {
3247
3341
  let componentHost = host;
3248
3342
  // Find the closest component host which has `OnRender` prop. This is need for subscriptions context.
@@ -3259,7 +3353,7 @@
3259
3353
  }
3260
3354
  function insertNewComponent(host, componentQRL, jsxProps) {
3261
3355
  if (host) {
3262
- clearVNodeEffectDependencies(container, host);
3356
+ clearAllEffects(container, host);
3263
3357
  }
3264
3358
  vnode_insertBefore(journal, vParent, (vNewNode = vnode_newVirtual()), vCurrent && getInsertBefore());
3265
3359
  const jsxNode = jsxValue;
@@ -3347,8 +3441,8 @@
3347
3441
  if (!src || !dst) {
3348
3442
  return true;
3349
3443
  }
3350
- let srcKeys = removePropsKeys(Object.keys(src), ['children', QSubscribers]);
3351
- let dstKeys = removePropsKeys(Object.keys(dst), ['children', QSubscribers]);
3444
+ let srcKeys = removePropsKeys(Object.keys(src), ['children', QBackRefs]);
3445
+ let dstKeys = removePropsKeys(Object.keys(dst), ['children', QBackRefs]);
3352
3446
  if (srcKeys.length !== dstKeys.length) {
3353
3447
  return true;
3354
3448
  }
@@ -3387,6 +3481,7 @@
3387
3481
  let vCursor = vNode;
3388
3482
  // Depth first traversal
3389
3483
  if (vnode_isTextVNode(vNode)) {
3484
+ markVNodeAsDeleted(vCursor);
3390
3485
  // Text nodes don't have subscriptions or children;
3391
3486
  return;
3392
3487
  }
@@ -3394,7 +3489,7 @@
3394
3489
  do {
3395
3490
  const type = vCursor[VNodeProps.flags];
3396
3491
  if (type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) {
3397
- clearVNodeEffectDependencies(container, vCursor);
3492
+ clearAllEffects(container, vCursor);
3398
3493
  markVNodeAsDeleted(vCursor);
3399
3494
  // Only elements and virtual nodes need to be traversed for children
3400
3495
  if (type & VNodeFlags.Virtual) {
@@ -3404,7 +3499,7 @@
3404
3499
  const obj = seq[i];
3405
3500
  if (isTask(obj)) {
3406
3501
  const task = obj;
3407
- clearSubscriberEffectDependencies(container, task);
3502
+ clearAllEffects(container, task);
3408
3503
  if (task.$flags$ & TaskFlags.VISIBLE_TASK) {
3409
3504
  container.$scheduler$(ChoreType.CLEANUP_VISIBLE, task);
3410
3505
  }
@@ -3419,10 +3514,10 @@
3419
3514
  vnode_getProp(vCursor, OnRenderProp, null) !== null;
3420
3515
  if (isComponent) {
3421
3516
  // SPECIAL CASE: If we are a component, we need to descend into the projected content and release the content.
3422
- const attrs = vCursor;
3423
- for (let i = VirtualVNodeProps.PROPS_OFFSET; i < attrs.length; i = i + 2) {
3517
+ const attrs = vnode_getProps(vCursor);
3518
+ for (let i = 0; i < attrs.length; i = i + 2) {
3424
3519
  const key = attrs[i];
3425
- if (!isParentSlotProp(key) && isSlotProp(key)) {
3520
+ if (isSlotProp(key)) {
3426
3521
  const value = attrs[i + 1];
3427
3522
  if (value) {
3428
3523
  attrs[i + 1] = null; // prevent infinite loop
@@ -3439,7 +3534,7 @@
3439
3534
  }
3440
3535
  }
3441
3536
  }
3442
- const isProjection = type & VNodeFlags.Virtual && vnode_getProp(vCursor, QSlot, null) !== null;
3537
+ const isProjection = vnode_isProjection(vCursor);
3443
3538
  // Descend into children
3444
3539
  if (!isProjection) {
3445
3540
  // Only if it is not a projection
@@ -3462,6 +3557,9 @@
3462
3557
  }
3463
3558
  }
3464
3559
  }
3560
+ else if (type & VNodeFlags.Text) {
3561
+ markVNodeAsDeleted(vCursor);
3562
+ }
3465
3563
  // Out of children
3466
3564
  if (vCursor === vNode) {
3467
3565
  // we are where we started, this means that vNode has no children, so we are done.
@@ -3528,82 +3626,270 @@
3528
3626
  SiblingsArray[SiblingsArray["NextVNode"] = 5] = "NextVNode";
3529
3627
  })(SiblingsArray || (SiblingsArray = {}));
3530
3628
 
3531
- // <docs markdown="../../readme.md#implicit$FirstArg">
3629
+ /** @internal */
3630
+ const useResourceQrl = (qrl, opts) => {
3631
+ const { val, set, i, iCtx } = useSequentialScope();
3632
+ if (val != null) {
3633
+ return val;
3634
+ }
3635
+ assertQrl(qrl);
3636
+ const container = iCtx.$container$;
3637
+ const resource = createResourceReturn(container, opts);
3638
+ const el = iCtx.$hostElement$;
3639
+ const task = new Task(TaskFlags.DIRTY | TaskFlags.RESOURCE, i, el, qrl, resource, null);
3640
+ container.$scheduler$(ChoreType.TASK, task);
3641
+ set(resource);
3642
+ return resource;
3643
+ };
3644
+ // <docs markdown="../readme.md#useResource">
3532
3645
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
3533
- // (edit ../../readme.md#implicit$FirstArg instead and run `pnpm docs.sync`)
3646
+ // (edit ../readme.md#useResource instead and run `pnpm docs.sync`)
3534
3647
  /**
3535
- * Create a `____$(...)` convenience method from `___(...)`.
3648
+ * This method works like an async memoized function that runs whenever some tracked value changes
3649
+ * and returns some data.
3536
3650
  *
3537
- * It is very common for functions to take a lazy-loadable resource as a first argument. For this
3538
- * reason, the Qwik Optimizer automatically extracts the first argument from any function which ends
3539
- * in `$`.
3651
+ * `useResource` however returns immediate a `ResourceReturn` object that contains the data and a
3652
+ * state that indicates if the data is available or not.
3540
3653
  *
3541
- * This means that `foo$(arg0)` and `foo($(arg0))` are equivalent with respect to Qwik Optimizer.
3542
- * The former is just a shorthand for the latter.
3654
+ * The status can be one of the following:
3543
3655
  *
3544
- * For example, these function calls are equivalent:
3656
+ * - `pending` - the data is not yet available.
3657
+ * - `resolved` - the data is available.
3658
+ * - `rejected` - the data is not available due to an error or timeout.
3545
3659
  *
3546
- * - `component$(() => {...})` is same as `component($(() => {...}))`
3660
+ * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
3661
+ * re-throw it (or a new Error), the resource status will never be `rejected`.
3547
3662
  *
3548
- * ```tsx
3549
- * export function myApi(callback: QRL<() => void>): void {
3550
- * // ...
3551
- * }
3663
+ * ### Example
3552
3664
  *
3553
- * export const myApi$ = implicit$FirstArg(myApi);
3554
- * // type of myApi$: (callback: () => void): void
3665
+ * Example showing how `useResource` to perform a fetch to request the weather, whenever the input
3666
+ * city name changes.
3555
3667
  *
3556
- * // can be used as:
3557
- * myApi$(() => console.log('callback'));
3668
+ * ```tsx
3669
+ * const Cmp = component$(() => {
3670
+ * const cityS = useSignal('');
3558
3671
  *
3559
- * // will be transpiled to:
3560
- * // FILE: <current file>
3561
- * myApi(qrl('./chunk-abc.js', 'callback'));
3672
+ * const weatherResource = useResource$(async ({ track, cleanup }) => {
3673
+ * const cityName = track(cityS);
3674
+ * const abortController = new AbortController();
3675
+ * cleanup(() => abortController.abort('cleanup'));
3676
+ * const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
3677
+ * signal: abortController.signal,
3678
+ * });
3679
+ * const data = await res.json();
3680
+ * return data as { temp: number };
3681
+ * });
3562
3682
  *
3563
- * // FILE: chunk-abc.js
3564
- * export const callback = () => console.log('callback');
3683
+ * return (
3684
+ * <div>
3685
+ * <input name="city" bind:value={cityS} />
3686
+ * <Resource
3687
+ * value={weatherResource}
3688
+ * onResolved={(weather) => {
3689
+ * return <div>Temperature: {weather.temp}</div>;
3690
+ * }}
3691
+ * />
3692
+ * </div>
3693
+ * );
3694
+ * });
3565
3695
  * ```
3566
3696
  *
3567
- * @param fn - A function that should have its first argument automatically `$`.
3568
3697
  * @public
3698
+ * @see Resource
3699
+ * @see ResourceReturn
3569
3700
  */
3570
3701
  // </docs>
3571
- const implicit$FirstArg = (fn) => {
3572
- return function (first, ...rest) {
3573
- return fn.call(null, dollar(first), ...rest);
3702
+ const Resource = (props) => {
3703
+ // Resource path
3704
+ return _jsxSorted(Fragment, null, null, getResourceValueAsPromise(props), 0, null);
3705
+ };
3706
+ function getResourceValueAsPromise(props) {
3707
+ const resource = props.value;
3708
+ if (isResourceReturn(resource) && resource.value) {
3709
+ const isBrowser = !isServerPlatform();
3710
+ if (isBrowser) {
3711
+ // create a subscription for the resource._state changes
3712
+ const state = resource._state;
3713
+ if (state === 'pending' && props.onPending) {
3714
+ return Promise.resolve().then(useBindInvokeContext(props.onPending));
3715
+ }
3716
+ else if (state === 'rejected' && props.onRejected) {
3717
+ return Promise.resolve(resource._error).then(useBindInvokeContext(props.onRejected));
3718
+ }
3719
+ else {
3720
+ const resolvedValue = untrack(() => resource._resolved);
3721
+ if (resolvedValue !== undefined) {
3722
+ // resolved, pending without onPending prop or rejected without onRejected prop
3723
+ return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved));
3724
+ }
3725
+ }
3726
+ }
3727
+ return resource.value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3728
+ }
3729
+ else if (isPromise(resource)) {
3730
+ return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3731
+ }
3732
+ else if (isSignal(resource)) {
3733
+ return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3734
+ }
3735
+ else {
3736
+ return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3737
+ }
3738
+ }
3739
+ const _createResourceReturn = (opts) => {
3740
+ const resource = {
3741
+ __brand: 'resource',
3742
+ value: undefined,
3743
+ loading: isServerPlatform() ? false : true,
3744
+ _resolved: undefined,
3745
+ _error: undefined,
3746
+ _state: 'pending',
3747
+ _timeout: opts?.timeout ?? -1,
3748
+ _cache: 0,
3574
3749
  };
3750
+ return resource;
3575
3751
  };
3576
-
3577
- const createSignal$1 = (value) => {
3578
- return new Signal(null, value);
3752
+ const createResourceReturn = (container, opts, initialPromise) => {
3753
+ const result = _createResourceReturn(opts);
3754
+ result.value = initialPromise;
3755
+ return createStore(container, result, StoreFlags.RECURSIVE);
3579
3756
  };
3580
- const createComputedSignal = (qrl) => {
3581
- throwIfQRLNotResolved(qrl);
3582
- return new ComputedSignal(null, qrl);
3757
+ const isResourceReturn = (obj) => {
3758
+ return isObject(obj) && (getStoreTarget(obj) || obj).__brand === 'resource';
3759
+ };
3760
+ const runResource = (task, container, host) => {
3761
+ task.$flags$ &= ~TaskFlags.DIRTY;
3762
+ cleanupTask(task);
3763
+ const iCtx = newInvokeContext(container.$locale$, host, undefined, ResourceEvent);
3764
+ iCtx.$container$ = container;
3765
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
3766
+ const resource = task.$state$;
3767
+ assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
3768
+ const track = (obj, prop) => {
3769
+ const ctx = newInvokeContext();
3770
+ ctx.$effectSubscriber$ = getSubscriber(task, EffectProperty.COMPONENT);
3771
+ ctx.$container$ = container;
3772
+ return invoke(ctx, () => {
3773
+ if (isFunction(obj)) {
3774
+ return obj();
3775
+ }
3776
+ if (prop) {
3777
+ return obj[prop];
3778
+ }
3779
+ else if (isSignal(obj)) {
3780
+ return obj.value;
3781
+ }
3782
+ else {
3783
+ return obj;
3784
+ }
3785
+ });
3786
+ };
3787
+ const handleError = (reason) => container.handleError(reason, host);
3788
+ const cleanups = [];
3789
+ task.$destroy$ = noSerialize(() => {
3790
+ cleanups.forEach((fn) => {
3791
+ try {
3792
+ fn();
3793
+ }
3794
+ catch (err) {
3795
+ handleError(err);
3796
+ }
3797
+ });
3798
+ done = true;
3799
+ });
3800
+ const resourceTarget = unwrapStore(resource);
3801
+ const opts = {
3802
+ track,
3803
+ cleanup(fn) {
3804
+ if (typeof fn === 'function') {
3805
+ cleanups.push(fn);
3806
+ }
3807
+ },
3808
+ cache(policy) {
3809
+ let milliseconds = 0;
3810
+ if (policy === 'immutable') {
3811
+ milliseconds = Infinity;
3812
+ }
3813
+ else {
3814
+ milliseconds = policy;
3815
+ }
3816
+ resource._cache = milliseconds;
3817
+ },
3818
+ previous: resourceTarget._resolved,
3819
+ };
3820
+ let resolve;
3821
+ let reject;
3822
+ let done = false;
3823
+ const setState = (resolved, value) => {
3824
+ if (!done) {
3825
+ done = true;
3826
+ if (resolved) {
3827
+ done = true;
3828
+ resource.loading = false;
3829
+ resource._state = 'resolved';
3830
+ resource._resolved = value;
3831
+ resource._error = undefined;
3832
+ resolve(value);
3833
+ }
3834
+ else {
3835
+ done = true;
3836
+ resource.loading = false;
3837
+ resource._state = 'rejected';
3838
+ resource._error = value;
3839
+ reject(value);
3840
+ }
3841
+ return true;
3842
+ }
3843
+ return false;
3844
+ };
3845
+ /**
3846
+ * Add cleanup to resolve the resource if we are trying to run the same resource again while the
3847
+ * previous one is not resolved yet. The next `runResource` run will call this cleanup
3848
+ */
3849
+ cleanups.push(() => {
3850
+ if (untrack(() => resource.loading) === true) {
3851
+ const value = untrack(() => resource._resolved);
3852
+ setState(true, value);
3853
+ }
3854
+ });
3855
+ // Execute mutation inside empty invocation
3856
+ invoke(iCtx, () => {
3857
+ // console.log('RESOURCE.pending: ');
3858
+ resource._state = 'pending';
3859
+ resource.loading = !isServerPlatform();
3860
+ const promise = (resource.value = new Promise((r, re) => {
3861
+ resolve = r;
3862
+ reject = re;
3863
+ }));
3864
+ promise.catch(ignoreErrorToPreventNodeFromCrashing);
3865
+ });
3866
+ const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
3867
+ setState(true, value);
3868
+ }, (err) => {
3869
+ if (isPromise(err)) {
3870
+ return err.then(() => runResource(task, container, host));
3871
+ }
3872
+ else {
3873
+ setState(false, err);
3874
+ }
3875
+ });
3876
+ const timeout = resourceTarget._timeout;
3877
+ if (timeout > 0) {
3878
+ return Promise.race([
3879
+ promise,
3880
+ delay(timeout).then(() => {
3881
+ if (setState(false, new Error('timeout'))) {
3882
+ cleanupTask(task);
3883
+ }
3884
+ }),
3885
+ ]);
3886
+ }
3887
+ return promise;
3888
+ };
3889
+ const ignoreErrorToPreventNodeFromCrashing = (err) => {
3890
+ // ignore error to prevent node from crashing
3891
+ // node will crash in promise is rejected and no one is listening to the rejection.
3583
3892
  };
3584
-
3585
- /**
3586
- * Creates a Signal with the given value. If no value is given, the signal is created with
3587
- * `undefined`.
3588
- *
3589
- * @public
3590
- */
3591
- const createSignal = createSignal$1;
3592
- /** @internal */
3593
- const createComputedQrl = createComputedSignal;
3594
- /**
3595
- * Create a computed signal which is calculated from the given QRL. A computed signal is a signal
3596
- * which is calculated from other signals. When the signals change, the computed signal is
3597
- * recalculated.
3598
- *
3599
- * The QRL must be a function which returns the value of the signal. The function must not have side
3600
- * effects, and it mus be synchronous.
3601
- *
3602
- * If you need the function to be async, use `useSignal` and `useTask$` instead.
3603
- *
3604
- * @public
3605
- */
3606
- const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedQrl);
3607
3893
 
3608
3894
  /// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
3609
3895
  const aVNodePath = [];
@@ -3788,38 +4074,29 @@
3788
4074
  * - Visible Tasks are sorted afterJournalFlush, than depth first on component and finally in
3789
4075
  * declaration order within component.
3790
4076
  */
3791
- var ChoreType;
3792
- (function (ChoreType) {
3793
- /// MASKS defining three levels of sorting
3794
- ChoreType[ChoreType["MACRO"] = 240] = "MACRO";
3795
- /* order of elements (not encoded here) */
3796
- ChoreType[ChoreType["MICRO"] = 15] = "MICRO";
3797
- /** Ensure tha the QRL promise is resolved before processing next chores in the queue */
3798
- ChoreType[ChoreType["QRL_RESOLVE"] = 1] = "QRL_RESOLVE";
3799
- ChoreType[ChoreType["RESOURCE"] = 2] = "RESOURCE";
3800
- ChoreType[ChoreType["TASK"] = 3] = "TASK";
3801
- ChoreType[ChoreType["NODE_DIFF"] = 4] = "NODE_DIFF";
3802
- ChoreType[ChoreType["NODE_PROP"] = 5] = "NODE_PROP";
3803
- ChoreType[ChoreType["COMPONENT_SSR"] = 6] = "COMPONENT_SSR";
3804
- ChoreType[ChoreType["COMPONENT"] = 7] = "COMPONENT";
3805
- ChoreType[ChoreType["RECOMPUTE_AND_SCHEDULE_EFFECTS"] = 8] = "RECOMPUTE_AND_SCHEDULE_EFFECTS";
3806
- ChoreType[ChoreType["JOURNAL_FLUSH"] = 16] = "JOURNAL_FLUSH";
3807
- ChoreType[ChoreType["VISIBLE"] = 32] = "VISIBLE";
3808
- ChoreType[ChoreType["CLEANUP_VISIBLE"] = 48] = "CLEANUP_VISIBLE";
3809
- ChoreType[ChoreType["WAIT_FOR_ALL"] = 255] = "WAIT_FOR_ALL";
3810
- })(ChoreType || (ChoreType = {}));
4077
+ // Turn this on to get debug output of what the scheduler is doing.
4078
+ const DEBUG$1 = false;
4079
+ const getPromise = (chore) => (chore.$promise$ ||= new Promise((resolve) => {
4080
+ chore.$resolve$ = resolve;
4081
+ }));
3811
4082
  const createScheduler = (container, scheduleDrain, journalFlush) => {
3812
4083
  const choreQueue = [];
4084
+ const qrlRuns = [];
3813
4085
  let currentChore = null;
3814
- let journalFlushScheduled = false;
4086
+ let drainScheduled = false;
3815
4087
  return schedule;
3816
4088
  ///// IMPLEMENTATION /////
3817
4089
  function schedule(type, hostOrTask = null, targetOrQrl = null, payload = null) {
3818
- const runLater = type !== ChoreType.WAIT_FOR_ALL && type !== ChoreType.COMPONENT_SSR;
3819
- const isTask = type === ChoreType.TASK ||
3820
- type === ChoreType.VISIBLE ||
3821
- type === ChoreType.RESOURCE ||
3822
- type === ChoreType.CLEANUP_VISIBLE;
4090
+ const isServer = !isDomContainer(container);
4091
+ const isComponentSsr = isServer && type === ChoreType.COMPONENT;
4092
+ const runLater = type !== ChoreType.WAIT_FOR_ALL && !isComponentSsr && type !== ChoreType.RUN_QRL;
4093
+ const isTask = type === ChoreType.TASK || type === ChoreType.VISIBLE || type === ChoreType.CLEANUP_VISIBLE;
4094
+ const isClientOnly = type === ChoreType.JOURNAL_FLUSH ||
4095
+ type === ChoreType.NODE_DIFF ||
4096
+ type === ChoreType.NODE_PROP;
4097
+ if (isServer && isClientOnly) {
4098
+ return;
4099
+ }
3823
4100
  if (isTask) {
3824
4101
  hostOrTask.$flags$ |= TaskFlags.DIRTY;
3825
4102
  }
@@ -3838,192 +4115,234 @@
3838
4115
  $returnValue$: null,
3839
4116
  $executed$: false,
3840
4117
  };
3841
- chore.$promise$ = new Promise((resolve) => (chore.$resolve$ = resolve));
3842
4118
  chore = sortedInsert(choreQueue, chore, container.rootVNode || null);
3843
- if (!journalFlushScheduled && runLater) {
4119
+ if (!drainScheduled && runLater) {
3844
4120
  // If we are not currently draining, we need to schedule a drain.
3845
- journalFlushScheduled = true;
4121
+ drainScheduled = true;
3846
4122
  schedule(ChoreType.JOURNAL_FLUSH);
3847
- scheduleDrain();
4123
+ // Catch here to avoid unhandled promise rejection
4124
+ scheduleDrain()?.catch?.(() => { });
3848
4125
  }
4126
+ // TODO figure out what to do with chore errors
3849
4127
  if (runLater) {
3850
- return chore.$promise$;
4128
+ return getPromise(chore);
3851
4129
  }
3852
4130
  else {
3853
- return drainUpTo(chore, container.rootVNode || null);
4131
+ return drainUpTo(chore, isServer);
3854
4132
  }
3855
4133
  }
3856
- /**
3857
- * Execute all of the chores up to and including the given chore.
3858
- *
3859
- * @param runUptoChore
3860
- */
3861
- function drainUpTo(runUptoChore, rootVNode) {
3862
- // If it already ran, it's not in the queue
3863
- if (runUptoChore.$executed$) {
3864
- return runUptoChore.$returnValue$;
3865
- }
3866
- if (currentChore) {
3867
- // Already running chore, which means we're waiting for async completion
3868
- return runUptoChore.$promise$;
3869
- }
4134
+ /** Execute all of the chores up to and including the given chore. */
4135
+ function drainUpTo(runUptoChore, isServer) {
4136
+ let maxRetries = 5000;
3870
4137
  while (choreQueue.length) {
3871
- const nextChore = choreQueue.shift();
3872
- const order = choreComparator(nextChore, runUptoChore, rootVNode);
3873
- if (order === null) {
3874
- continue;
4138
+ if (maxRetries-- < 0) {
4139
+ throw new Error('drainUpTo: max retries reached');
3875
4140
  }
3876
- if (order > 0) {
3877
- // we have processed all of the chores up to and including the given chore.
3878
- break;
4141
+ if (currentChore) {
4142
+ // Already running chore, which means we're waiting for async completion
4143
+ return getPromise(currentChore)
4144
+ .then(() => drainUpTo(runUptoChore, isServer))
4145
+ .catch((e) => {
4146
+ container.handleError(e, currentChore?.$host$);
4147
+ });
4148
+ }
4149
+ const nextChore = choreQueue[0];
4150
+ if (nextChore.$executed$) {
4151
+ choreQueue.shift();
4152
+ if (nextChore === runUptoChore) {
4153
+ break;
4154
+ }
4155
+ continue;
3879
4156
  }
3880
- const isDeletedVNode = vNodeAlreadyDeleted(nextChore);
3881
- if (isDeletedVNode &&
4157
+ if (vNodeAlreadyDeleted(nextChore) &&
3882
4158
  // we need to process cleanup tasks for deleted nodes
3883
4159
  nextChore.$type$ !== ChoreType.CLEANUP_VISIBLE) {
4160
+ choreQueue.shift();
3884
4161
  continue;
3885
4162
  }
3886
- const returnValue = executeChore(nextChore);
3887
- if (isPromise(returnValue)) {
3888
- const promise = returnValue.then(() => drainUpTo(runUptoChore, rootVNode));
3889
- return promise;
3890
- }
4163
+ executeChore(nextChore, isServer);
3891
4164
  }
3892
4165
  return runUptoChore.$returnValue$;
3893
4166
  }
3894
- function executeChore(chore) {
4167
+ function executeChore(chore, isServer) {
3895
4168
  const host = chore.$host$;
3896
4169
  assertEqual(currentChore, null, 'Chore already running.');
3897
4170
  currentChore = chore;
3898
4171
  let returnValue = null;
3899
- switch (chore.$type$) {
3900
- case ChoreType.JOURNAL_FLUSH:
3901
- returnValue = journalFlush();
3902
- journalFlushScheduled = false;
3903
- break;
3904
- case ChoreType.COMPONENT:
3905
- case ChoreType.COMPONENT_SSR:
3906
- returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
3907
- if (chore.$type$ === ChoreType.COMPONENT) {
3908
- const styleScopedId = container.getHostProp(host, QScopedStyle);
3909
- return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
4172
+ try {
4173
+ switch (chore.$type$) {
4174
+ case ChoreType.WAIT_FOR_ALL:
4175
+ {
4176
+ if (isServer) {
4177
+ drainScheduled = false;
4178
+ }
3910
4179
  }
3911
- else {
3912
- return jsx;
4180
+ break;
4181
+ case ChoreType.JOURNAL_FLUSH:
4182
+ {
4183
+ returnValue = journalFlush();
4184
+ drainScheduled = false;
4185
+ }
4186
+ break;
4187
+ case ChoreType.COMPONENT:
4188
+ {
4189
+ returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
4190
+ if (isServer) {
4191
+ return jsx;
4192
+ }
4193
+ else {
4194
+ const styleScopedId = container.getHostProp(host, QScopedStyle);
4195
+ return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
4196
+ }
4197
+ }, (err) => container.handleError(err, host));
4198
+ }
4199
+ break;
4200
+ case ChoreType.RUN_QRL:
4201
+ {
4202
+ const fn = chore.$target$.getFn();
4203
+ const result = retryOnPromise(() => fn(...chore.$payload$));
4204
+ if (isPromise(result)) {
4205
+ const handled = result
4206
+ .finally(() => {
4207
+ qrlRuns.splice(qrlRuns.indexOf(handled), 1);
4208
+ })
4209
+ .catch((error) => {
4210
+ container.handleError(error, chore.$host$);
4211
+ });
4212
+ // Don't wait for the promise to resolve
4213
+ // TODO come up with a better solution, we also want concurrent signal handling with tasks but serial tasks
4214
+ qrlRuns.push(handled);
4215
+ DEBUG$1 &&
4216
+ debugTrace('execute.DONE (but still running)', chore, currentChore, choreQueue);
4217
+ chore.$returnValue$ = handled;
4218
+ chore.$resolve$?.(handled);
4219
+ currentChore = null;
4220
+ chore.$executed$ = true;
4221
+ // early out so we don't call after()
4222
+ return;
4223
+ }
4224
+ returnValue = null;
4225
+ }
4226
+ break;
4227
+ case ChoreType.TASK:
4228
+ case ChoreType.VISIBLE:
4229
+ {
4230
+ const payload = chore.$payload$;
4231
+ if (payload.$flags$ & TaskFlags.RESOURCE) {
4232
+ const result = runResource(payload, container, host);
4233
+ // Don't await the return value of the resource, because async resources should not be awaited.
4234
+ // The reason for this is that we should be able to update for example a node with loading
4235
+ // text. If we await the resource, the loading text will not be displayed until the resource
4236
+ // is loaded.
4237
+ // Awaiting on the client also causes a deadlock.
4238
+ // In any case, the resource will never throw.
4239
+ returnValue = isServer ? result : null;
4240
+ }
4241
+ else {
4242
+ returnValue = runTask(payload, container, host);
4243
+ }
4244
+ }
4245
+ break;
4246
+ case ChoreType.CLEANUP_VISIBLE:
4247
+ {
4248
+ const task = chore.$payload$;
4249
+ cleanupTask(task);
4250
+ }
4251
+ break;
4252
+ case ChoreType.NODE_DIFF:
4253
+ {
4254
+ const parentVirtualNode = chore.$target$;
4255
+ let jsx = chore.$payload$;
4256
+ if (isSignal(jsx)) {
4257
+ jsx = jsx.value;
4258
+ }
4259
+ returnValue = retryOnPromise(() => vnode_diff(container, jsx, parentVirtualNode, null));
4260
+ }
4261
+ break;
4262
+ case ChoreType.NODE_PROP:
4263
+ {
4264
+ const virtualNode = chore.$host$;
4265
+ const payload = chore.$payload$;
4266
+ let value = payload.$value$;
4267
+ if (isSignal(value)) {
4268
+ value = value.value;
4269
+ }
4270
+ const isConst = payload.$isConst$;
4271
+ const journal = container.$journal$;
4272
+ const property = chore.$idx$;
4273
+ const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
4274
+ if (isConst) {
4275
+ const element = virtualNode[ElementVNodeProps.element];
4276
+ journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue);
4277
+ }
4278
+ else {
4279
+ vnode_setAttr(journal, virtualNode, property, serializedValue);
4280
+ }
4281
+ }
4282
+ break;
4283
+ case ChoreType.QRL_RESOLVE: {
4284
+ {
4285
+ const target = chore.$target$;
4286
+ returnValue = !target.resolved ? target.resolve() : null;
3913
4287
  }
3914
- }, (err) => container.handleError(err, host));
3915
- break;
3916
- case ChoreType.RESOURCE:
3917
- // Don't await the return value of the resource, because async resources should not be awaited.
3918
- // The reason for this is that we should be able to update for example a node with loading
3919
- // text. If we await the resource, the loading text will not be displayed until the resource
3920
- // is loaded.
3921
- const result = runResource(chore.$payload$, container, host);
3922
- returnValue = isDomContainer(container) ? null : result;
3923
- break;
3924
- case ChoreType.TASK:
3925
- returnValue = runTask(chore.$payload$, container, host);
3926
- break;
3927
- case ChoreType.VISIBLE:
3928
- returnValue = runTask(chore.$payload$, container, host);
3929
- break;
3930
- case ChoreType.CLEANUP_VISIBLE:
3931
- const task = chore.$payload$;
3932
- cleanupTask(task);
3933
- break;
3934
- case ChoreType.NODE_DIFF:
3935
- const parentVirtualNode = chore.$target$;
3936
- let jsx = chore.$payload$;
3937
- if (isSignal(jsx)) {
3938
- jsx = jsx.value;
3939
- }
3940
- returnValue = retryOnPromise(() => vnode_diff(container, jsx, parentVirtualNode, null));
3941
- break;
3942
- case ChoreType.NODE_PROP:
3943
- const virtualNode = chore.$host$;
3944
- const payload = chore.$payload$;
3945
- let value = payload.$value$;
3946
- if (isSignal(value)) {
3947
- value = value.value;
3948
- }
3949
- const isConst = payload.$isConst$;
3950
- const journal = container.$journal$;
3951
- const property = chore.$idx$;
3952
- const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
3953
- if (isConst) {
3954
- const element = virtualNode[ElementVNodeProps.element];
3955
- journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue);
3956
- }
3957
- else {
3958
- vnode_setAttr(journal, virtualNode, property, serializedValue);
3959
- }
3960
- break;
3961
- case ChoreType.QRL_RESOLVE: {
3962
- const target = chore.$target$;
3963
- returnValue = !target.resolved ? target.resolve() : null;
3964
- break;
3965
- }
3966
- case ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS: {
3967
- const target = chore.$target$;
3968
- const forceRunEffects = target.$forceRunEffects$;
3969
- target.$forceRunEffects$ = false;
3970
- if (!target.$effects$?.length) {
3971
4288
  break;
3972
4289
  }
3973
- returnValue = retryOnPromise(() => {
3974
- if (target.$computeIfNeeded$() || forceRunEffects) {
3975
- triggerEffects(container, target, target.$effects$);
4290
+ case ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS: {
4291
+ {
4292
+ const target = chore.$target$;
4293
+ const forceRunEffects = target.$forceRunEffects$;
4294
+ target.$forceRunEffects$ = false;
4295
+ if (!target.$effects$?.size) {
4296
+ break;
4297
+ }
4298
+ returnValue = retryOnPromise(() => {
4299
+ if (target.$computeIfNeeded$() || forceRunEffects) {
4300
+ triggerEffects(container, target, target.$effects$);
4301
+ }
4302
+ });
3976
4303
  }
3977
- });
3978
- break;
4304
+ break;
4305
+ }
3979
4306
  }
3980
4307
  }
3981
- return maybeThenPassError(returnValue, (value) => {
3982
- if (currentChore) {
3983
- currentChore.$executed$ = true;
3984
- currentChore.$resolve$?.(value);
3985
- }
4308
+ catch (e) {
4309
+ returnValue = Promise.reject(e);
4310
+ }
4311
+ const after = (value, error) => {
3986
4312
  currentChore = null;
3987
- return (chore.$returnValue$ = value);
3988
- });
3989
- }
3990
- };
3991
- const toNumber = (value) => {
3992
- return typeof value === 'number' ? value : -1;
3993
- };
3994
- /**
3995
- * When a derived signal is update we need to run vnode_diff. However the signal can update multiple
3996
- * times during component execution. For this reason it is necessary for us to update the schedule
3997
- * work with the latest result of the signal.
3998
- */
3999
- const choreUpdate = (existing, newChore) => {
4000
- if (existing.$type$ === ChoreType.NODE_DIFF) {
4001
- existing.$payload$ = newChore.$payload$;
4002
- }
4003
- };
4004
- function vNodeAlreadyDeleted(chore) {
4005
- return !!(chore.$host$ &&
4006
- vnode_isVNode(chore.$host$) &&
4007
- chore.$host$[VNodeProps.flags] & VNodeFlags.Deleted);
4008
- }
4009
- /**
4010
- * Compares two chores to determine their execution order in the scheduler's queue.
4011
- *
4012
- * @param a - The first chore to compare
4013
- * @param b - The second chore to compare
4014
- * @returns A number indicating the relative order of the chores. A negative number means `a` runs
4015
- * before `b`.
4016
- */
4017
- function choreComparator(a, b, rootVNode) {
4018
- const macroTypeDiff = (a.$type$ & ChoreType.MACRO) - (b.$type$ & ChoreType.MACRO);
4019
- if (macroTypeDiff !== 0) {
4020
- return macroTypeDiff;
4313
+ chore.$executed$ = true;
4314
+ if (error) {
4315
+ container.handleError(error, host);
4316
+ }
4317
+ else {
4318
+ chore.$returnValue$ = value;
4319
+ chore.$resolve$?.(value);
4320
+ }
4321
+ };
4322
+ if (isPromise(returnValue)) {
4323
+ chore.$promise$ = returnValue.then(after, (error) => after(undefined, error));
4324
+ chore.$resolve$?.(chore.$promise$);
4325
+ chore.$resolve$ = undefined;
4326
+ }
4327
+ else {
4328
+ after(returnValue);
4329
+ }
4021
4330
  }
4022
- // JOURNAL_FLUSH does not have a host or $idx$, so we can't compare it.
4023
- if (a.$type$ !== ChoreType.JOURNAL_FLUSH) {
4331
+ /**
4332
+ * Compares two chores to determine their execution order in the scheduler's queue.
4333
+ *
4334
+ * @param a - The first chore to compare
4335
+ * @param b - The second chore to compare
4336
+ * @returns A number indicating the relative order of the chores. A negative number means `a` runs
4337
+ * before `b`.
4338
+ */
4339
+ function choreComparator(a, b, rootVNode) {
4340
+ const macroTypeDiff = (a.$type$ & ChoreType.MACRO) - (b.$type$ & ChoreType.MACRO);
4341
+ if (macroTypeDiff !== 0) {
4342
+ return macroTypeDiff;
4343
+ }
4024
4344
  const aHost = a.$host$;
4025
4345
  const bHost = b.$host$;
4026
- // QRL_RESOLVE does not have a host.
4027
4346
  if (aHost !== bHost && aHost !== null && bHost !== null) {
4028
4347
  if (vnode_isVNode(aHost) && vnode_isVNode(bHost)) {
4029
4348
  // we are running on the client.
@@ -4033,6 +4352,8 @@
4033
4352
  }
4034
4353
  }
4035
4354
  else {
4355
+ assertFalse(vnode_isVNode(aHost), 'expected aHost to be SSRNode but it is a VNode');
4356
+ assertFalse(vnode_isVNode(bHost), 'expected bHost to be SSRNode but it is a VNode');
4036
4357
  // we are running on the server.
4037
4358
  // On server we can't schedule task for a different host!
4038
4359
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -4049,250 +4370,117 @@
4049
4370
  }
4050
4371
  }
4051
4372
  const microTypeDiff = (a.$type$ & ChoreType.MICRO) - (b.$type$ & ChoreType.MICRO);
4052
- if (microTypeDiff !== 0) {
4053
- return microTypeDiff;
4054
- }
4055
- const idxDiff = toNumber(a.$idx$) - toNumber(b.$idx$);
4056
- if (idxDiff !== 0) {
4057
- return idxDiff;
4058
- }
4059
- // If the host is the same, we need to compare the target.
4060
- if (a.$target$ !== b.$target$ &&
4061
- ((a.$type$ === ChoreType.QRL_RESOLVE && b.$type$ === ChoreType.QRL_RESOLVE) ||
4062
- (a.$type$ === ChoreType.NODE_PROP && b.$type$ === ChoreType.NODE_PROP) ||
4063
- (a.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS &&
4064
- b.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS))) {
4065
- // 1 means that we are going to process chores as FIFO
4066
- return 1;
4067
- }
4068
- }
4069
- return 0;
4070
- }
4071
- function sortedFindIndex(sortedArray, value, rootVNode) {
4072
- /// We need to ensure that the `queue` is sorted by priority.
4073
- /// 1. Find a place where to insert into.
4074
- let bottom = 0;
4075
- let top = sortedArray.length;
4076
- while (bottom < top) {
4077
- const middle = bottom + ((top - bottom) >> 1);
4078
- const midChore = sortedArray[middle];
4079
- const comp = choreComparator(value, midChore, rootVNode);
4080
- if (comp < 0) {
4081
- top = middle;
4082
- }
4083
- else if (comp > 0) {
4084
- bottom = middle + 1;
4085
- }
4086
- else {
4087
- // We already have the host in the queue.
4088
- return middle;
4089
- }
4090
- }
4091
- return ~bottom;
4092
- }
4093
- function sortedInsert(sortedArray, value, rootVNode) {
4094
- /// We need to ensure that the `queue` is sorted by priority.
4095
- /// 1. Find a place where to insert into.
4096
- const idx = sortedFindIndex(sortedArray, value, rootVNode);
4097
- if (idx < 0) {
4098
- /// 2. Insert the chore into the queue.
4099
- sortedArray.splice(~idx, 0, value);
4100
- return value;
4101
- }
4102
- const existing = sortedArray[idx];
4103
- choreUpdate(existing, value);
4104
- return existing;
4105
- }
4106
-
4107
- // <docs markdown="../readme.md#useLexicalScope">
4108
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
4109
- // (edit ../readme.md#useLexicalScope instead and run `pnpm docs.sync`)
4110
- /**
4111
- * Used by the Qwik Optimizer to restore the lexically scoped variables.
4112
- *
4113
- * This method should not be present in the application source code.
4114
- *
4115
- * NOTE: `useLexicalScope` method can only be used in the synchronous portion of the callback
4116
- * (before any `await` statements.)
4117
- *
4118
- * @internal
4119
- */
4120
- // </docs>
4121
- const useLexicalScope = () => {
4122
- const context = getInvokeContext();
4123
- let qrl = context.$qrl$;
4124
- if (!qrl) {
4125
- const el = context.$element$;
4126
- assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
4127
- const containerElement = _getQContainerElement(el);
4128
- assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
4129
- const container = getDomContainer(containerElement);
4130
- qrl = container.parseQRL(decodeURIComponent(String(context.$url$)));
4131
- }
4132
- else {
4133
- assertQrl(qrl);
4134
- assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
4135
- }
4136
- return qrl.$captureRef$;
4137
- };
4138
-
4139
- var TaskFlags;
4140
- (function (TaskFlags) {
4141
- TaskFlags[TaskFlags["VISIBLE_TASK"] = 1] = "VISIBLE_TASK";
4142
- TaskFlags[TaskFlags["TASK"] = 2] = "TASK";
4143
- TaskFlags[TaskFlags["RESOURCE"] = 4] = "RESOURCE";
4144
- TaskFlags[TaskFlags["DIRTY"] = 8] = "DIRTY";
4145
- })(TaskFlags || (TaskFlags = {}));
4146
- /** @internal */
4147
- const useTaskQrl = (qrl, opts) => {
4148
- const { val, set, iCtx, i } = useSequentialScope();
4149
- if (val) {
4150
- return;
4151
- }
4152
- assertQrl(qrl);
4153
- set(1);
4154
- const host = iCtx.$hostElement$;
4155
- const task = new Task(TaskFlags.DIRTY | TaskFlags.TASK, i, iCtx.$hostElement$, qrl, undefined, null);
4156
- // In V2 we add the task to the sequential scope. We need to do this
4157
- // in order to be able to retrieve it later when the parent element is
4158
- // deleted and we need to be able to release the task subscriptions.
4159
- set(task);
4160
- const result = runTask(task, iCtx.$container$, host);
4161
- if (isPromise(result)) {
4162
- throw result;
4163
- }
4164
- qrl.$resolveLazy$(iCtx.$element$);
4165
- if (isServerPlatform()) {
4166
- useRunTask(task, opts?.eagerness);
4167
- }
4168
- };
4169
- const runTask = (task, container, host) => {
4170
- task.$flags$ &= ~TaskFlags.DIRTY;
4171
- cleanupTask(task);
4172
- const iCtx = newInvokeContext(container.$locale$, host, undefined, TaskEvent);
4173
- iCtx.$container$ = container;
4174
- const taskFn = task.$qrl$.getFn(iCtx, () => clearSubscriberEffectDependencies(container, task));
4175
- const track = (obj, prop) => {
4176
- const ctx = newInvokeContext();
4177
- ctx.$effectSubscriber$ = [task, EffectProperty.COMPONENT];
4178
- ctx.$container$ = container;
4179
- return invoke(ctx, () => {
4180
- if (isFunction(obj)) {
4181
- return obj();
4182
- }
4183
- if (prop) {
4184
- return obj[prop];
4185
- }
4186
- else if (isSignal(obj)) {
4187
- return obj.value;
4188
- }
4189
- else {
4190
- return obj;
4191
- }
4192
- });
4193
- };
4194
- const handleError = (reason) => container.handleError(reason, host);
4195
- let cleanupFns = null;
4196
- const cleanup = (fn) => {
4197
- if (typeof fn == 'function') {
4198
- if (!cleanupFns) {
4199
- cleanupFns = [];
4200
- task.$destroy$ = noSerialize(() => {
4201
- task.$destroy$ = null;
4202
- cleanupFns.forEach((fn) => {
4203
- try {
4204
- fn();
4205
- }
4206
- catch (err) {
4207
- handleError(err);
4208
- }
4209
- });
4210
- });
4211
- }
4212
- cleanupFns.push(fn);
4213
- }
4214
- };
4215
- const taskApi = { track, cleanup };
4216
- const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
4217
- if (isPromise(err)) {
4218
- return err.then(() => runTask(task, container, host));
4219
- }
4220
- else {
4221
- return handleError(err);
4222
- }
4223
- });
4224
- return result;
4225
- };
4226
- const cleanupTask = (task) => {
4227
- const destroy = task.$destroy$;
4228
- if (destroy) {
4229
- task.$destroy$ = null;
4230
- try {
4231
- destroy();
4373
+ if (microTypeDiff !== 0) {
4374
+ return microTypeDiff;
4232
4375
  }
4233
- catch (err) {
4234
- logError(err);
4376
+ // types are the same
4377
+ const idxDiff = toNumber(a.$idx$) - toNumber(b.$idx$);
4378
+ if (idxDiff !== 0) {
4379
+ return idxDiff;
4235
4380
  }
4381
+ // If the host is the same (or missing), and the type is the same, we need to compare the target.
4382
+ if (a.$target$ !== b.$target$ || a.$payload$ !== b.$payload$) {
4383
+ // 1 means that we are going to process chores as FIFO
4384
+ return 1;
4385
+ }
4386
+ // If the chore is the same as the current chore, we will run it again
4387
+ if (b === currentChore) {
4388
+ return 1;
4389
+ }
4390
+ // The chores are the same and will run only once
4391
+ return 0;
4236
4392
  }
4237
- };
4238
- const useRunTask = (task, eagerness) => {
4239
- if (eagerness === 'visible' || eagerness === 'intersection-observer') {
4240
- useOn('qvisible', getTaskHandlerQrl(task));
4241
- }
4242
- else if (eagerness === 'load' || eagerness === 'document-ready') {
4243
- useOnDocument('qinit', getTaskHandlerQrl(task));
4393
+ function sortedFindIndex(sortedArray, value, rootVNode) {
4394
+ /// We need to ensure that the `queue` is sorted by priority.
4395
+ /// 1. Find a place where to insert into.
4396
+ let bottom = 0;
4397
+ let top = sortedArray.length;
4398
+ while (bottom < top) {
4399
+ const middle = bottom + ((top - bottom) >> 1);
4400
+ const midChore = sortedArray[middle];
4401
+ const comp = choreComparator(value, midChore, rootVNode);
4402
+ if (comp < 0) {
4403
+ top = middle;
4404
+ }
4405
+ else if (comp > 0) {
4406
+ bottom = middle + 1;
4407
+ }
4408
+ else {
4409
+ // We already have the host in the queue.
4410
+ return middle;
4411
+ }
4412
+ }
4413
+ return ~bottom;
4244
4414
  }
4245
- else if (eagerness === 'idle' || eagerness === 'document-idle') {
4246
- useOnDocument('qidle', getTaskHandlerQrl(task));
4415
+ function sortedInsert(sortedArray, value, rootVNode) {
4416
+ /// We need to ensure that the `queue` is sorted by priority.
4417
+ /// 1. Find a place where to insert into.
4418
+ const idx = sortedFindIndex(sortedArray, value, rootVNode);
4419
+ if (idx < 0) {
4420
+ /// 2. Insert the chore into the queue.
4421
+ sortedArray.splice(~idx, 0, value);
4422
+ return value;
4423
+ }
4424
+ const existing = sortedArray[idx];
4425
+ /**
4426
+ * When a derived signal is updated we need to run vnode_diff. However the signal can update
4427
+ * multiple times during component execution. For this reason it is necessary for us to update
4428
+ * the chore with the latest result of the signal.
4429
+ */
4430
+ if (existing.$type$ === ChoreType.NODE_DIFF) {
4431
+ existing.$payload$ = value.$payload$;
4432
+ }
4433
+ if (existing.$executed$) {
4434
+ existing.$executed$ = false;
4435
+ }
4436
+ return existing;
4247
4437
  }
4248
4438
  };
4249
- const getTaskHandlerQrl = (task) => {
4250
- const taskQrl = task.$qrl$;
4251
- const taskHandler = createQRL(taskQrl.$chunk$, '_hW', _hW, null, null, [task], taskQrl.$symbol$);
4252
- // Needed for chunk lookup in dev mode
4253
- if (taskQrl.dev) {
4254
- taskHandler.dev = taskQrl.dev;
4255
- }
4256
- return taskHandler;
4439
+ const toNumber = (value) => {
4440
+ return typeof value === 'number' ? value : -1;
4257
4441
  };
4258
- class Task extends Subscriber {
4259
- $flags$;
4260
- $index$;
4261
- $el$;
4262
- $qrl$;
4263
- $state$;
4264
- $destroy$;
4265
- constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
4266
- super();
4267
- this.$flags$ = $flags$;
4268
- this.$index$ = $index$;
4269
- this.$el$ = $el$;
4270
- this.$qrl$ = $qrl$;
4271
- this.$state$ = $state$;
4272
- this.$destroy$ = $destroy$;
4442
+ function vNodeAlreadyDeleted(chore) {
4443
+ return !!(chore.$host$ &&
4444
+ vnode_isVNode(chore.$host$) &&
4445
+ chore.$host$[VNodeProps.flags] & VNodeFlags.Deleted);
4446
+ }
4447
+ function debugChoreTypeToString(type) {
4448
+ return ({
4449
+ [ChoreType.QRL_RESOLVE]: 'QRL_RESOLVE',
4450
+ [ChoreType.RUN_QRL]: 'RUN_QRL',
4451
+ [ChoreType.TASK]: 'TASK',
4452
+ [ChoreType.NODE_DIFF]: 'NODE_DIFF',
4453
+ [ChoreType.NODE_PROP]: 'NODE_PROP',
4454
+ [ChoreType.COMPONENT]: 'COMPONENT',
4455
+ [ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS]: 'RECOMPUTE_SIGNAL',
4456
+ [ChoreType.JOURNAL_FLUSH]: 'JOURNAL_FLUSH',
4457
+ [ChoreType.VISIBLE]: 'VISIBLE',
4458
+ [ChoreType.CLEANUP_VISIBLE]: 'CLEANUP_VISIBLE',
4459
+ [ChoreType.WAIT_FOR_ALL]: 'WAIT_FOR_ALL',
4460
+ }[type] || 'UNKNOWN: ' + type);
4461
+ }
4462
+ function debugChoreToString(chore) {
4463
+ const type = debugChoreTypeToString(chore.$type$);
4464
+ const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
4465
+ const qrlTarget = chore.$target$?.$symbol$;
4466
+ return `Chore(${type} ${chore.$type$ === ChoreType.QRL_RESOLVE || chore.$type$ === ChoreType.RUN_QRL ? qrlTarget : host} ${chore.$idx$})`;
4467
+ }
4468
+ function debugTrace(action, arg, currentChore, queue) {
4469
+ const lines = ['===========================\nScheduler: ' + action];
4470
+ if (arg && !('$type$' in arg)) {
4471
+ lines.push(' arg: ' + String(arg).replaceAll(/\n.*/gim, ''));
4472
+ }
4473
+ if (queue) {
4474
+ queue.forEach((chore) => {
4475
+ const active = chore === arg ? '>>>' : ' ';
4476
+ lines.push(` ${active} > ` +
4477
+ (chore === currentChore ? '[running] ' : '') +
4478
+ debugChoreToString(chore));
4479
+ });
4273
4480
  }
4481
+ // eslint-disable-next-line no-console
4482
+ console.log(lines.join('\n') + '\n');
4274
4483
  }
4275
- const isTask = (value) => {
4276
- return value instanceof Task;
4277
- };
4278
- /**
4279
- * Low-level API used by the Optimizer to process `useTask$()` API. This method is not intended to
4280
- * be used by developers.
4281
- *
4282
- * @internal
4283
- */
4284
- const _hW = () => {
4285
- const [task] = useLexicalScope();
4286
- const container = getDomContainer(task.$el$);
4287
- const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK;
4288
- container.$scheduler$(type, task);
4289
- };
4290
-
4291
- /**
4292
- * Special value used to mark that a given signal needs to be computed. This is essentially a
4293
- * "marked as dirty" flag.
4294
- */
4295
- const NEEDS_COMPUTATION = Symbol('invalid');
4296
4484
 
4297
4485
  /**
4298
4486
  * @file
@@ -4310,6 +4498,15 @@
4310
4498
  const DEBUG = false;
4311
4499
  // eslint-disable-next-line no-console
4312
4500
  const log = (...args) => console.log('SIGNAL', ...args.map(qwikDebugToString));
4501
+ var SignalFlags;
4502
+ (function (SignalFlags) {
4503
+ SignalFlags[SignalFlags["INVALID"] = 1] = "INVALID";
4504
+ })(SignalFlags || (SignalFlags = {}));
4505
+ var WrappedSignalFlags;
4506
+ (function (WrappedSignalFlags) {
4507
+ // should subscribe to value and be unwrapped for PropsProxy
4508
+ WrappedSignalFlags[WrappedSignalFlags["UNWRAP"] = 2] = "UNWRAP";
4509
+ })(WrappedSignalFlags || (WrappedSignalFlags = {}));
4313
4510
  const throwIfQRLNotResolved = (qrl) => {
4314
4511
  const resolved = qrl.resolved;
4315
4512
  if (!resolved) {
@@ -4326,18 +4523,19 @@
4326
4523
  return value instanceof Signal;
4327
4524
  };
4328
4525
  /** @internal */
4329
- class EffectPropData {
4526
+ class SubscriptionData {
4330
4527
  data;
4331
4528
  constructor(data) {
4332
4529
  this.data = data;
4333
4530
  }
4334
4531
  }
4335
- var EffectSubscriptionsProp;
4336
- (function (EffectSubscriptionsProp) {
4337
- EffectSubscriptionsProp[EffectSubscriptionsProp["EFFECT"] = 0] = "EFFECT";
4338
- EffectSubscriptionsProp[EffectSubscriptionsProp["PROPERTY"] = 1] = "PROPERTY";
4339
- EffectSubscriptionsProp[EffectSubscriptionsProp["FIRST_BACK_REF_OR_DATA"] = 2] = "FIRST_BACK_REF_OR_DATA";
4340
- })(EffectSubscriptionsProp || (EffectSubscriptionsProp = {}));
4532
+ var EffectSubscriptionProp;
4533
+ (function (EffectSubscriptionProp) {
4534
+ EffectSubscriptionProp[EffectSubscriptionProp["CONSUMER"] = 0] = "CONSUMER";
4535
+ EffectSubscriptionProp[EffectSubscriptionProp["PROPERTY"] = 1] = "PROPERTY";
4536
+ EffectSubscriptionProp[EffectSubscriptionProp["BACK_REF"] = 2] = "BACK_REF";
4537
+ EffectSubscriptionProp[EffectSubscriptionProp["DATA"] = 3] = "DATA";
4538
+ })(EffectSubscriptionProp || (EffectSubscriptionProp = {}));
4341
4539
  var EffectProperty;
4342
4540
  (function (EffectProperty) {
4343
4541
  EffectProperty["COMPONENT"] = ":";
@@ -4374,19 +4572,16 @@
4374
4572
  }
4375
4573
  const effectSubscriber = ctx.$effectSubscriber$;
4376
4574
  if (effectSubscriber) {
4377
- const effects = (this.$effects$ ||= []);
4575
+ const effects = (this.$effects$ ||= new Set());
4378
4576
  // Let's make sure that we have a reference to this effect.
4379
4577
  // Adding reference is essentially adding a subscription, so if the signal
4380
4578
  // changes we know who to notify.
4381
- ensureContainsEffect(effects, effectSubscriber);
4579
+ ensureContainsSubscription(effects, effectSubscriber);
4382
4580
  // But when effect is scheduled in needs to be able to know which signals
4383
4581
  // to unsubscribe from. So we need to store the reference from the effect back
4384
4582
  // to this signal.
4385
- ensureContains(effectSubscriber, this);
4386
- if (isSubscriber(this)) {
4387
- // We need to add the subscriber to the effect so that we can clean it up later
4388
- ensureEffectContainsSubscriber(effectSubscriber[EffectSubscriptionsProp.EFFECT], this, this.$container$);
4389
- }
4583
+ ensureContainsBackRef(effectSubscriber, this);
4584
+ addQrlToSerializationCtx(effectSubscriber, this.$container$);
4390
4585
  }
4391
4586
  }
4392
4587
  return this.untrackedValue;
@@ -4404,123 +4599,98 @@
4404
4599
  }
4405
4600
  }
4406
4601
  toString() {
4407
- return (`[${this.constructor.name}${this.$invalid$ ? ' INVALID' : ''} ${String(this.$untrackedValue$)}]` +
4408
- (this.$effects$?.map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' ')).join('\n') || ''));
4602
+ return (`[${this.constructor.name}${this.$flags$ & SignalFlags.INVALID ? ' INVALID' : ''} ${String(this.$untrackedValue$)}]` +
4603
+ (Array.from(this.$effects$ || [])
4604
+ .map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' '))
4605
+ .join('\n') || ''));
4409
4606
  }
4410
4607
  toJSON() {
4411
4608
  return { value: this.$untrackedValue$ };
4412
4609
  }
4413
4610
  }
4414
- /** Ensure the item is in array (do nothing if already there) */
4415
- const ensureContains = (array, value) => {
4416
- const isMissing = array.indexOf(value) === -1;
4417
- if (isMissing) {
4418
- array.push(value);
4419
- }
4420
- };
4421
- const ensureContainsEffect = (array, effectSubscriptions) => {
4422
- for (let i = 0; i < array.length; i++) {
4423
- const existingEffect = array[i];
4424
- if (existingEffect[0] === effectSubscriptions[0] &&
4425
- existingEffect[1] === effectSubscriptions[1]) {
4426
- return;
4427
- }
4428
- }
4429
- array.push(effectSubscriptions);
4611
+ const ensureContainsSubscription = (array, effectSubscription) => {
4612
+ array.add(effectSubscription);
4430
4613
  };
4431
- const ensureEffectContainsSubscriber = (effect, subscriber, container) => {
4432
- if (isSubscriber(effect)) {
4433
- effect.$effectDependencies$ ||= [];
4434
- if (subscriberExistInSubscribers(effect.$effectDependencies$, subscriber)) {
4435
- return;
4614
+ /** Ensure the item is in back refs set */
4615
+ const ensureContainsBackRef = (array, value) => {
4616
+ array[EffectSubscriptionProp.BACK_REF] ||= new Set();
4617
+ array[EffectSubscriptionProp.BACK_REF].add(value);
4618
+ };
4619
+ const addQrlToSerializationCtx = (effectSubscriber, container) => {
4620
+ if (!!container && !isDomContainer(container)) {
4621
+ const effect = effectSubscriber[EffectSubscriptionProp.CONSUMER];
4622
+ const property = effectSubscriber[EffectSubscriptionProp.PROPERTY];
4623
+ let qrl = null;
4624
+ if (isTask(effect)) {
4625
+ qrl = effect.$qrl$;
4436
4626
  }
4437
- effect.$effectDependencies$.push(subscriber);
4438
- }
4439
- else if (vnode_isVNode(effect) && !vnode_isTextVNode(effect)) {
4440
- let subscribers = vnode_getProp(effect, QSubscribers, container ? container.$getObjectById$ : null);
4441
- subscribers ||= [];
4442
- if (subscriberExistInSubscribers(subscribers, subscriber)) {
4443
- return;
4627
+ else if (effect instanceof ComputedSignal) {
4628
+ qrl = effect.$computeQrl$;
4444
4629
  }
4445
- subscribers.push(subscriber);
4446
- vnode_setProp(effect, QSubscribers, subscribers);
4447
- }
4448
- else if (isSSRNode(effect)) {
4449
- let subscribers = effect.getProp(QSubscribers);
4450
- subscribers ||= [];
4451
- if (subscriberExistInSubscribers(subscribers, subscriber)) {
4452
- return;
4630
+ else if (property === EffectProperty.COMPONENT) {
4631
+ qrl = container.getHostProp(effect, OnRenderProp);
4453
4632
  }
4454
- subscribers.push(subscriber);
4455
- effect.setProp(QSubscribers, subscribers);
4456
- }
4457
- };
4458
- const isSSRNode = (effect) => {
4459
- return 'setProp' in effect && 'getProp' in effect && 'removeProp' in effect && 'id' in effect;
4460
- };
4461
- const subscriberExistInSubscribers = (subscribers, subscriber) => {
4462
- for (let i = 0; i < subscribers.length; i++) {
4463
- if (subscribers[i] === subscriber) {
4464
- return true;
4633
+ if (qrl) {
4634
+ container.serializationCtx.$eventQrls$.add(qrl);
4465
4635
  }
4466
4636
  }
4467
- return false;
4468
4637
  };
4469
4638
  const triggerEffects = (container, signal, effects) => {
4639
+ const isBrowser = isDomContainer(container);
4470
4640
  if (effects) {
4471
- const scheduleEffect = (effectSubscriptions) => {
4472
- const effect = effectSubscriptions[EffectSubscriptionsProp.EFFECT];
4473
- const property = effectSubscriptions[EffectSubscriptionsProp.PROPERTY];
4641
+ const scheduleEffect = (effectSubscription) => {
4642
+ const consumer = effectSubscription[EffectSubscriptionProp.CONSUMER];
4643
+ const property = effectSubscription[EffectSubscriptionProp.PROPERTY];
4474
4644
  assertDefined(container, 'Container must be defined.');
4475
- if (isTask(effect)) {
4476
- effect.$flags$ |= TaskFlags.DIRTY;
4645
+ if (isTask(consumer)) {
4646
+ consumer.$flags$ |= TaskFlags.DIRTY;
4477
4647
  let choreType = ChoreType.TASK;
4478
- if (effect.$flags$ & TaskFlags.VISIBLE_TASK) {
4648
+ if (consumer.$flags$ & TaskFlags.VISIBLE_TASK) {
4479
4649
  choreType = ChoreType.VISIBLE;
4480
4650
  }
4481
- else if (effect.$flags$ & TaskFlags.RESOURCE) {
4482
- choreType = ChoreType.RESOURCE;
4483
- }
4484
- container.$scheduler$(choreType, effect);
4651
+ container.$scheduler$(choreType, consumer);
4485
4652
  }
4486
- else if (effect instanceof Signal) {
4653
+ else if (consumer instanceof Signal) {
4487
4654
  // we don't schedule ComputedSignal/DerivedSignal directly, instead we invalidate it and
4488
4655
  // and schedule the signals effects (recursively)
4489
- if (effect instanceof ComputedSignal) {
4656
+ if (consumer instanceof ComputedSignal) {
4490
4657
  // Ensure that the computed signal's QRL is resolved.
4491
4658
  // If not resolved schedule it to be resolved.
4492
- if (!effect.$computeQrl$.resolved) {
4493
- container.$scheduler$(ChoreType.QRL_RESOLVE, null, effect.$computeQrl$);
4659
+ if (!consumer.$computeQrl$.resolved) {
4660
+ container.$scheduler$(ChoreType.QRL_RESOLVE, null, consumer.$computeQrl$);
4494
4661
  }
4495
4662
  }
4496
- effect.$invalidate$();
4663
+ consumer.$invalidate$();
4497
4664
  }
4498
4665
  else if (property === EffectProperty.COMPONENT) {
4499
- const host = effect;
4666
+ const host = consumer;
4500
4667
  const qrl = container.getHostProp(host, OnRenderProp);
4501
4668
  assertDefined(qrl, 'Component must have QRL');
4502
4669
  const props = container.getHostProp(host, ELEMENT_PROPS);
4503
4670
  container.$scheduler$(ChoreType.COMPONENT, host, qrl, props);
4504
4671
  }
4505
- else if (property === EffectProperty.VNODE) {
4506
- const host = effect;
4507
- const target = host;
4508
- container.$scheduler$(ChoreType.NODE_DIFF, host, target, signal);
4509
- }
4510
- else {
4511
- const host = effect;
4512
- const effectData = effectSubscriptions[EffectSubscriptionsProp.FIRST_BACK_REF_OR_DATA];
4513
- if (effectData instanceof EffectPropData) {
4514
- const data = effectData.data;
4515
- const payload = {
4516
- ...data,
4517
- $value$: signal,
4518
- };
4519
- container.$scheduler$(ChoreType.NODE_PROP, host, property, payload);
4672
+ else if (isBrowser) {
4673
+ if (property === EffectProperty.VNODE) {
4674
+ const host = consumer;
4675
+ container.$scheduler$(ChoreType.NODE_DIFF, host, host, signal);
4676
+ }
4677
+ else {
4678
+ const host = consumer;
4679
+ const effectData = effectSubscription[EffectSubscriptionProp.DATA];
4680
+ if (effectData instanceof SubscriptionData) {
4681
+ const data = effectData.data;
4682
+ const payload = {
4683
+ ...data,
4684
+ $value$: signal,
4685
+ };
4686
+ container.$scheduler$(ChoreType.NODE_PROP, host, property, payload);
4687
+ }
4520
4688
  }
4521
4689
  }
4522
4690
  };
4523
- effects.forEach(scheduleEffect);
4691
+ for (const effect of effects) {
4692
+ scheduleEffect(effect);
4693
+ }
4524
4694
  }
4525
4695
  };
4526
4696
  /**
@@ -4536,18 +4706,21 @@
4536
4706
  * resolve the QRL during the mark dirty phase so that any call to it will be synchronous). )
4537
4707
  */
4538
4708
  $computeQrl$;
4709
+ $flags$;
4710
+ $forceRunEffects$ = false;
4711
+ [_EFFECT_BACK_REF] = null;
4712
+ constructor(container, fn,
4539
4713
  // We need a separate flag to know when the computation needs running because
4540
4714
  // we need the old value to know if effects need running after computation
4541
- $invalid$ = true;
4542
- $forceRunEffects$ = false;
4543
- constructor(container, fn) {
4715
+ flags = SignalFlags.INVALID) {
4544
4716
  // The value is used for comparison when signals trigger, which can only happen
4545
4717
  // when it was calculated before. Therefore we can pass whatever we like.
4546
4718
  super(container, NEEDS_COMPUTATION);
4547
4719
  this.$computeQrl$ = fn;
4720
+ this.$flags$ = flags;
4548
4721
  }
4549
4722
  $invalidate$() {
4550
- this.$invalid$ = true;
4723
+ this.$flags$ |= SignalFlags.INVALID;
4551
4724
  this.$forceRunEffects$ = false;
4552
4725
  // We should only call subscribers if the calculation actually changed.
4553
4726
  // Therefore, we need to calculate the value now.
@@ -4558,7 +4731,7 @@
4558
4731
  * remained the same object
4559
4732
  */
4560
4733
  force() {
4561
- this.$invalid$ = true;
4734
+ this.$flags$ |= SignalFlags.INVALID;
4562
4735
  this.$forceRunEffects$ = false;
4563
4736
  triggerEffects(this.$container$, this, this.$effects$);
4564
4737
  }
@@ -4571,14 +4744,14 @@
4571
4744
  return this.$untrackedValue$;
4572
4745
  }
4573
4746
  $computeIfNeeded$() {
4574
- if (!this.$invalid$) {
4747
+ if (!(this.$flags$ & SignalFlags.INVALID)) {
4575
4748
  return false;
4576
4749
  }
4577
4750
  const computeQrl = this.$computeQrl$;
4578
4751
  throwIfQRLNotResolved(computeQrl);
4579
4752
  const ctx = tryGetInvokeContext();
4580
4753
  const previousEffectSubscription = ctx?.$effectSubscriber$;
4581
- ctx && (ctx.$effectSubscriber$ = [this, EffectProperty.VNODE]);
4754
+ ctx && (ctx.$effectSubscriber$ = getSubscriber(this, EffectProperty.VNODE));
4582
4755
  try {
4583
4756
  const untrackedValue = computeQrl.getFn(ctx)();
4584
4757
  if (isPromise(untrackedValue)) {
@@ -4588,7 +4761,7 @@
4588
4761
  ]);
4589
4762
  }
4590
4763
  DEBUG && log('Signal.$compute$', untrackedValue);
4591
- this.$invalid$ = false;
4764
+ this.$flags$ &= ~SignalFlags.INVALID;
4592
4765
  const didChange = untrackedValue !== this.$untrackedValue$;
4593
4766
  if (didChange) {
4594
4767
  this.$untrackedValue$ = untrackedValue;
@@ -4613,20 +4786,22 @@
4613
4786
  $args$;
4614
4787
  $func$;
4615
4788
  $funcStr$;
4616
- // We need a separate flag to know when the computation needs running because
4617
- // we need the old value to know if effects need running after computation
4618
- $invalid$ = true;
4619
- $effectDependencies$ = null;
4789
+ $flags$;
4620
4790
  $hostElement$ = null;
4621
4791
  $forceRunEffects$ = false;
4622
- constructor(container, fn, args, fnStr) {
4792
+ [_EFFECT_BACK_REF] = null;
4793
+ constructor(container, fn, args, fnStr,
4794
+ // We need a separate flag to know when the computation needs running because
4795
+ // we need the old value to know if effects need running after computation
4796
+ flags = SignalFlags.INVALID | WrappedSignalFlags.UNWRAP) {
4623
4797
  super(container, NEEDS_COMPUTATION);
4624
4798
  this.$args$ = args;
4625
4799
  this.$func$ = fn;
4626
4800
  this.$funcStr$ = fnStr;
4801
+ this.$flags$ = flags;
4627
4802
  }
4628
4803
  $invalidate$() {
4629
- this.$invalid$ = true;
4804
+ this.$flags$ |= SignalFlags.INVALID;
4630
4805
  this.$forceRunEffects$ = false;
4631
4806
  // We should only call subscribers if the calculation actually changed.
4632
4807
  // Therefore, we need to calculate the value now.
@@ -4637,7 +4812,7 @@
4637
4812
  * remained the same object
4638
4813
  */
4639
4814
  force() {
4640
- this.$invalid$ = true;
4815
+ this.$flags$ |= SignalFlags.INVALID;
4641
4816
  this.$forceRunEffects$ = false;
4642
4817
  triggerEffects(this.$container$, this, this.$effects$);
4643
4818
  }
@@ -4650,10 +4825,12 @@
4650
4825
  return this.$untrackedValue$;
4651
4826
  }
4652
4827
  $computeIfNeeded$() {
4653
- if (!this.$invalid$) {
4828
+ if (!(this.$flags$ & SignalFlags.INVALID)) {
4654
4829
  return false;
4655
4830
  }
4656
4831
  const untrackedValue = trackSignal(() => this.$func$(...this.$args$), this, EffectProperty.VNODE, this.$container$);
4832
+ // TODO: we should remove invalid flag here
4833
+ // this.$flags$ &= ~SignalFlags.INVALID;
4657
4834
  const didChange = untrackedValue !== this.$untrackedValue$;
4658
4835
  if (didChange) {
4659
4836
  this.$untrackedValue$ = untrackedValue;
@@ -4686,7 +4863,7 @@
4686
4863
  if (jsx.key !== null) {
4687
4864
  host.setProp(ELEMENT_KEY, jsx.key);
4688
4865
  }
4689
- return scheduler(ChoreType.COMPONENT_SSR, host, componentQrl, srcProps);
4866
+ return scheduler(ChoreType.COMPONENT, host, componentQrl, srcProps);
4690
4867
  };
4691
4868
 
4692
4869
  class ParentComponentData {
@@ -4698,21 +4875,10 @@
4698
4875
  }
4699
4876
  }
4700
4877
  /** @internal */
4701
- function _walkJSX(ssr, value, options) {
4878
+ async function _walkJSX(ssr, value, options) {
4702
4879
  const stack = [value];
4703
- let resolveDrain;
4704
- let rejectDrain;
4705
- const drained = options.allowPromises &&
4706
- new Promise((res, rej) => {
4707
- resolveDrain = res;
4708
- rejectDrain = rej;
4709
- });
4710
4880
  const enqueue = (value) => stack.push(value);
4711
- const resolveValue = (value) => {
4712
- stack.push(value);
4713
- drain();
4714
- };
4715
- const drain = () => {
4881
+ const drain = async () => {
4716
4882
  while (stack.length) {
4717
4883
  const value = stack.pop();
4718
4884
  if (value instanceof ParentComponentData) {
@@ -4722,20 +4888,10 @@
4722
4888
  }
4723
4889
  else if (typeof value === 'function') {
4724
4890
  if (value === Promise) {
4725
- if (!options.allowPromises) {
4726
- throw qError(QError.promisesNotExpected);
4727
- }
4728
- stack.pop().then(resolveValue, rejectDrain);
4729
- return;
4730
- }
4731
- const waitOn = value.apply(ssr);
4732
- if (waitOn) {
4733
- if (!options.allowPromises) {
4734
- throw qError(QError.promisesNotExpected);
4735
- }
4736
- waitOn.then(drain, rejectDrain);
4737
- return;
4891
+ stack.push(await stack.pop());
4892
+ continue;
4738
4893
  }
4894
+ await value.apply(ssr);
4739
4895
  continue;
4740
4896
  }
4741
4897
  processJSXNode(ssr, enqueue, value, {
@@ -4743,12 +4899,8 @@
4743
4899
  parentComponentFrame: options.parentComponentFrame,
4744
4900
  });
4745
4901
  }
4746
- if (stack.length === 0 && options.allowPromises) {
4747
- resolveDrain();
4748
- }
4749
4902
  };
4750
- drain();
4751
- return drained;
4903
+ await drain();
4752
4904
  }
4753
4905
  function processJSXNode(ssr, enqueue, value, options) {
4754
4906
  // console.log('processJSXNode', value);
@@ -4787,7 +4939,6 @@
4787
4939
  enqueue(async () => {
4788
4940
  for await (const chunk of value) {
4789
4941
  await _walkJSX(ssr, chunk, {
4790
- allowPromises: true,
4791
4942
  currentStyleScoped: options.styleScoped,
4792
4943
  parentComponentFrame: options.parentComponentFrame,
4793
4944
  });
@@ -4841,7 +4992,7 @@
4841
4992
  if (componentFrame) {
4842
4993
  const compId = componentFrame.componentNode.id || '';
4843
4994
  const projectionAttrs = build.isDev ? [DEBUG_TYPE, VirtualType.Projection] : [];
4844
- projectionAttrs.push(':', compId);
4995
+ projectionAttrs.push(QSlotParent, compId);
4845
4996
  ssr.openProjection(projectionAttrs);
4846
4997
  const host = componentFrame.componentNode;
4847
4998
  const node = ssr.getLastNode();
@@ -4874,7 +5025,6 @@
4874
5025
  value = generator({
4875
5026
  async write(chunk) {
4876
5027
  await _walkJSX(ssr, chunk, {
4877
- allowPromises: true,
4878
5028
  currentStyleScoped: options.styleScoped,
4879
5029
  parentComponentFrame: options.parentComponentFrame,
4880
5030
  });
@@ -5031,11 +5181,23 @@
5031
5181
  const appendToValue = (valueToAppend) => {
5032
5182
  value = (value == null ? '' : value + '\n') + valueToAppend;
5033
5183
  };
5184
+ const getQrlString = (qrl) => {
5185
+ /**
5186
+ * If there are captures we need to schedule so everything is executed in the right order + qrls
5187
+ * are resolved.
5188
+ *
5189
+ * For internal qrls (starting with `_`) we assume that they do the right thing.
5190
+ */
5191
+ if (!qrl.$symbol$.startsWith('_') && (qrl.$captureRef$ || qrl.$capture$)) {
5192
+ qrl = createQRL(null, '_run', queueQRL, null, null, [qrl]);
5193
+ }
5194
+ return qrlToString(serializationCtx, qrl);
5195
+ };
5034
5196
  if (Array.isArray(qrls)) {
5035
5197
  for (let i = 0; i <= qrls.length; i++) {
5036
5198
  const qrl = qrls[i];
5037
- if (isQrl(qrl)) {
5038
- appendToValue(qrlToString(serializationCtx, qrl));
5199
+ if (isQrl$1(qrl)) {
5200
+ appendToValue(getQrlString(qrl));
5039
5201
  addQwikEventToSerializationContext(serializationCtx, key, qrl);
5040
5202
  }
5041
5203
  else if (qrl != null) {
@@ -5047,8 +5209,8 @@
5047
5209
  }
5048
5210
  }
5049
5211
  }
5050
- else if (isQrl(qrls)) {
5051
- value = qrlToString(serializationCtx, qrls);
5212
+ else if (isQrl$1(qrls)) {
5213
+ value = getQrlString(qrls);
5052
5214
  addQwikEventToSerializationContext(serializationCtx, key, qrls);
5053
5215
  }
5054
5216
  return value;
@@ -5098,7 +5260,7 @@
5098
5260
  *
5099
5261
  * @public
5100
5262
  */
5101
- const version = "2.0.0-alpha.6-dev+d848ba5";
5263
+ const version = "2.0.0-alpha.8-dev+66037b5";
5102
5264
 
5103
5265
  /** @internal */
5104
5266
  class _SharedContainer {
@@ -5130,13 +5292,6 @@
5130
5292
  }
5131
5293
  }
5132
5294
 
5133
- /** @internal */
5134
- const _CONST_PROPS = Symbol('CONST');
5135
- /** @internal */
5136
- const _VAR_PROPS = Symbol('VAR');
5137
- /** @internal @deprecated v1 compat */
5138
- const _IMMUTABLE = Symbol('IMMUTABLE');
5139
-
5140
5295
  // Keep these properties named like this so they're the same as from wrapSignal
5141
5296
  const getValueProp = (p0) => p0.value;
5142
5297
  const getProp = (p0, p1) => p0[p1];
@@ -5160,7 +5315,7 @@
5160
5315
  }
5161
5316
  if (isSignal(obj)) {
5162
5317
  assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
5163
- if (obj instanceof WrappedSignal) {
5318
+ if (obj instanceof WrappedSignal && obj.flags & WrappedSignalFlags.UNWRAP) {
5164
5319
  return obj;
5165
5320
  }
5166
5321
  return getWrapped(args);
@@ -5186,6 +5341,17 @@
5186
5341
  // We need to forward the access to the original object
5187
5342
  return getWrapped(args);
5188
5343
  };
5344
+ /** @internal */
5345
+ const _wrapStore = (obj, prop) => {
5346
+ const target = getStoreTarget(obj);
5347
+ const value = target[prop];
5348
+ if (isSignal(value)) {
5349
+ return value;
5350
+ }
5351
+ else {
5352
+ return new WrappedSignal(null, getProp, [obj, prop], null, SignalFlags.INVALID);
5353
+ }
5354
+ };
5189
5355
  /** @internal @deprecated v1 compat */
5190
5356
  const _wrapSignal = (obj, prop) => {
5191
5357
  const r = _wrapProp(obj, prop);
@@ -5456,7 +5622,9 @@
5456
5622
  ? this.$constProps$[prop]
5457
5623
  : this.$varProps$[prop];
5458
5624
  // a proxied value that the optimizer made
5459
- return value instanceof WrappedSignal ? value.value : value;
5625
+ return value instanceof WrappedSignal && value.$flags$ & WrappedSignalFlags.UNWRAP
5626
+ ? value.value
5627
+ : value;
5460
5628
  }
5461
5629
  set(_, prop, value) {
5462
5630
  if (prop === _CONST_PROPS) {
@@ -5548,7 +5716,7 @@
5548
5716
  else if (isTask(value)) {
5549
5717
  return `Task(${qwikDebugToString(value.$qrl$)})`;
5550
5718
  }
5551
- else if (isQrl$1(value)) {
5719
+ else if (isQrl(value)) {
5552
5720
  return `Qrl(${value.$symbol$})`;
5553
5721
  }
5554
5722
  else if (typeof value === 'object' || typeof value === 'function') {
@@ -5690,8 +5858,8 @@
5690
5858
  ID_CHAR: /* ********* */ '=',
5691
5859
  PROPS: /* ************** */ 62, // `>` - `q:props' - Component Props
5692
5860
  PROPS_CHAR: /* ****** */ '>',
5693
- SLOT_REF: /* *********** */ 63, // `?` - `q:sref` - Slot reference.
5694
- SLOT_REF_CHAR: /* *** */ '?',
5861
+ SLOT_PARENT: /* ******** */ 63, // `?` - `q:sparent` - Slot parent.
5862
+ SLOT_PARENT_CHAR: /* */ '?',
5695
5863
  KEY: /* **************** */ 64, // `@` - `q:key` - Element key.
5696
5864
  KEY_CHAR: /* ******** */ '@',
5697
5865
  SEQ: /* **************** */ 91, // `[` - `q:seq' - Seq value from `useSequentialScope()`
@@ -5702,14 +5870,21 @@
5702
5870
  CONTEXT_CHAR: /* **** */ ']',
5703
5871
  SEQ_IDX: /* ************ */ 94, // `^` - `q:seqIdx' - Sequential scope id
5704
5872
  SEQ_IDX_CHAR: /* **** */ '^',
5705
- SUBS: /* *************** */ 96, // '`' - `q:subs' - Effect dependencies/subscriptions
5706
- SUBS_CHAR: /* ******* */ '`',
5873
+ BACK_REFS: /* ********** */ 96, // '`' - `q:brefs' - Effect dependencies/subscriptions
5874
+ BACK_REFS_CHAR: /* ** */ '`',
5707
5875
  SEPARATOR: /* ********* */ 124, // `|` - Separator char to encode any key/value pairs.
5708
5876
  SEPARATOR_CHAR: /* ** */ '|',
5709
5877
  SLOT: /* ************** */ 126, // `~` - `q:slot' - Slot name
5710
5878
  SLOT_CHAR: /* ******* */ '~',
5711
5879
  };
5712
5880
 
5881
+ const mergeMaps = (map1, map2) => {
5882
+ for (const [k, v] of map2) {
5883
+ map1.set(k, v);
5884
+ }
5885
+ return map1;
5886
+ };
5887
+
5713
5888
  /**
5714
5889
  * @file
5715
5890
  *
@@ -5932,6 +6107,12 @@
5932
6107
  const flag = vNode[VNodeProps.flags];
5933
6108
  return (flag & VNodeFlags.Virtual) === VNodeFlags.Virtual;
5934
6109
  };
6110
+ const vnode_isProjection = (vNode) => {
6111
+ assertDefined(vNode, 'Missing vNode');
6112
+ const flag = vNode[VNodeProps.flags];
6113
+ return ((flag & VNodeFlags.Virtual) === VNodeFlags.Virtual &&
6114
+ vnode_getProp(vNode, QSlot, null) !== null);
6115
+ };
5935
6116
  const ensureTextVNode = (vNode) => {
5936
6117
  assertTrue(vnode_isTextVNode(vNode), 'Expecting TextVNode was: ' + vnode_getNodeTypeName(vNode));
5937
6118
  return vNode;
@@ -5965,6 +6146,7 @@
5965
6146
  elementVNode[VNodeProps.flags] ^= VNodeFlags.Inflated;
5966
6147
  const element = elementVNode[ElementVNodeProps.element];
5967
6148
  const attributes = element.attributes;
6149
+ const props = vnode_getProps(elementVNode);
5968
6150
  for (let idx = 0; idx < attributes.length; idx++) {
5969
6151
  const attr = attributes[idx];
5970
6152
  const key = attr.name;
@@ -5975,15 +6157,15 @@
5975
6157
  }
5976
6158
  else if (key.startsWith(QContainerAttr)) {
5977
6159
  if (attr.value === QContainerValue.HTML) {
5978
- mapArray_set(elementVNode, dangerouslySetInnerHTML, element.innerHTML, ElementVNodeProps.PROPS_OFFSET);
6160
+ mapArray_set(props, dangerouslySetInnerHTML, element.innerHTML, 0);
5979
6161
  }
5980
6162
  else if (attr.value === QContainerValue.TEXT && 'value' in element) {
5981
- mapArray_set(elementVNode, 'value', element.value, ElementVNodeProps.PROPS_OFFSET);
6163
+ mapArray_set(props, 'value', element.value, 0);
5982
6164
  }
5983
6165
  }
5984
6166
  else if (!key.startsWith('on:')) {
5985
6167
  const value = attr.value;
5986
- mapArray_set(elementVNode, key, value, ElementVNodeProps.PROPS_OFFSET);
6168
+ mapArray_set(props, key, value, 0);
5987
6169
  }
5988
6170
  }
5989
6171
  }
@@ -6375,7 +6557,7 @@
6375
6557
  element[key] = parseBoolean(value);
6376
6558
  }
6377
6559
  else if (key === 'value' && key in element) {
6378
- element.value = escapeHTML(String(value));
6560
+ element.value = String(value);
6379
6561
  }
6380
6562
  else if (key === dangerouslySetInnerHTML) {
6381
6563
  element.innerHTML = value;
@@ -6419,54 +6601,16 @@
6419
6601
  journal.length = 0;
6420
6602
  };
6421
6603
  //////////////////////////////////////////////////////////////////////////////////////////////////////
6422
- const mapApp_findIndx = (elementVNode, key, start) => {
6423
- assertTrue(start % 2 === 0, 'Expecting even number.');
6424
- let bottom = start >> 1;
6425
- let top = (elementVNode.length - 2) >> 1;
6426
- while (bottom <= top) {
6427
- const mid = bottom + ((top - bottom) >> 1);
6428
- const midKey = elementVNode[mid << 1];
6429
- if (midKey === key) {
6430
- return mid << 1;
6431
- }
6432
- if (midKey < key) {
6433
- bottom = mid + 1;
6434
- }
6435
- else {
6436
- top = mid - 1;
6437
- }
6438
- }
6439
- return (bottom << 1) ^ -1;
6440
- };
6441
- const mapArray_set = (elementVNode, key, value, start) => {
6442
- const indx = mapApp_findIndx(elementVNode, key, start);
6443
- if (indx >= 0) {
6444
- if (value == null) {
6445
- elementVNode.splice(indx, 2);
6446
- }
6447
- else {
6448
- elementVNode[indx + 1] = value;
6449
- }
6450
- }
6451
- else if (value != null) {
6452
- elementVNode.splice(indx ^ -1, 0, key, value);
6453
- }
6454
- };
6455
- const mapArray_get = (elementVNode, key, start) => {
6456
- const indx = mapApp_findIndx(elementVNode, key, start);
6457
- if (indx >= 0) {
6458
- return elementVNode[indx + 1];
6459
- }
6460
- else {
6461
- return null;
6462
- }
6463
- };
6464
- //////////////////////////////////////////////////////////////////////////////////////////////////////
6465
6604
  const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
6466
6605
  ensureElementOrVirtualVNode(parent);
6467
6606
  if (vnode_isElementVNode(parent)) {
6468
6607
  ensureMaterialized(parent);
6469
6608
  }
6609
+ if (newChild === insertBefore) {
6610
+ // invalid insertBefore. We can't insert before self reference
6611
+ // prevent infinity loop and putting self reference to next sibling
6612
+ insertBefore = null;
6613
+ }
6470
6614
  let adjustedInsertBefore = null;
6471
6615
  if (insertBefore == null) {
6472
6616
  if (vnode_isVirtualVNode(parent)) {
@@ -6590,9 +6734,10 @@
6590
6734
  const elementVNode = ensureElementVNode(vnode);
6591
6735
  let elementName = elementVNode[ElementVNodeProps.elementName];
6592
6736
  if (elementName === undefined) {
6593
- elementName = elementVNode[ElementVNodeProps.elementName] =
6594
- elementVNode[ElementVNodeProps.element].nodeName.toLowerCase();
6595
- elementVNode[VNodeProps.flags] |= vnode_getElementNamespaceFlags(elementName);
6737
+ const element = elementVNode[ElementVNodeProps.element];
6738
+ const nodeName = build.isDev ? fastNodeName(element).toLowerCase() : fastNodeName(element);
6739
+ elementName = elementVNode[ElementVNodeProps.elementName] = nodeName;
6740
+ elementVNode[VNodeProps.flags] |= vnode_getElementNamespaceFlags(element);
6596
6741
  }
6597
6742
  return elementName;
6598
6743
  };
@@ -6774,6 +6919,20 @@
6774
6919
  }
6775
6920
  return node;
6776
6921
  };
6922
+ let _fastNamespaceURI = null;
6923
+ const fastNamespaceURI = (element) => {
6924
+ if (!_fastNamespaceURI) {
6925
+ _fastNamespaceURI = fastGetter(element, 'namespaceURI');
6926
+ }
6927
+ return _fastNamespaceURI.call(element);
6928
+ };
6929
+ let _fastNodeName = null;
6930
+ const fastNodeName = (element) => {
6931
+ if (!_fastNodeName) {
6932
+ _fastNodeName = fastGetter(element, 'nodeName');
6933
+ }
6934
+ return _fastNodeName.call(element);
6935
+ };
6777
6936
  const fastGetter = (prototype, name) => {
6778
6937
  let getter;
6779
6938
  while (prototype && !(getter = Object.getOwnPropertyDescriptor(prototype, name)?.get)) {
@@ -6840,8 +6999,11 @@
6840
6999
  container.$setRawState$(parseInt(id), vParent);
6841
7000
  build.isDev && vnode_setAttr(null, vParent, ELEMENT_ID, id);
6842
7001
  }
6843
- else if (peek() === VNodeDataChar.SUBS) {
6844
- vnode_setProp(vParent, QSubscribers, consumeValue());
7002
+ else if (peek() === VNodeDataChar.BACK_REFS) {
7003
+ if (!container) {
7004
+ container = getDomContainer(vParent[ElementVNodeProps.element]);
7005
+ }
7006
+ setEffectBackRefFromVNodeData(vParent, consumeValue(), container);
6845
7007
  }
6846
7008
  else {
6847
7009
  // prevent infinity loop if there are some characters outside the range
@@ -6851,6 +7013,18 @@
6851
7013
  }
6852
7014
  return vFirstChild;
6853
7015
  };
7016
+ function setEffectBackRefFromVNodeData(vParent, value, container) {
7017
+ const deserializedSubMap = container.$getObjectById$(value);
7018
+ if (!vParent[_EFFECT_BACK_REF]) {
7019
+ Object.defineProperty(vParent, _EFFECT_BACK_REF, {
7020
+ value: deserializedSubMap,
7021
+ });
7022
+ }
7023
+ else {
7024
+ const subMap = vParent[_EFFECT_BACK_REF];
7025
+ mergeMaps(subMap, deserializedSubMap);
7026
+ }
7027
+ }
6854
7028
  const processVNodeData$1 = (vData, callback) => {
6855
7029
  let nextToConsumeIdx = 0;
6856
7030
  let ch = 0;
@@ -6895,8 +7069,9 @@
6895
7069
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6896
7070
  vnode_ensureElementInflated(vnode);
6897
7071
  const keys = [];
6898
- for (let i = vnode_getPropStartIndex(vnode); i < vnode.length; i = i + 2) {
6899
- const key = vnode[i];
7072
+ const props = vnode_getProps(vnode);
7073
+ for (let i = 0; i < props.length; i = i + 2) {
7074
+ const key = props[i];
6900
7075
  if (!key.startsWith(Q_PROPS_SEPARATOR)) {
6901
7076
  keys.push(key);
6902
7077
  }
@@ -6909,22 +7084,23 @@
6909
7084
  const type = vnode[VNodeProps.flags];
6910
7085
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6911
7086
  vnode_ensureElementInflated(vnode);
6912
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7087
+ const props = vnode_getProps(vnode);
7088
+ const idx = mapApp_findIndx(props, key, 0);
6913
7089
  if (idx >= 0) {
6914
- if (vnode[idx + 1] != value && (type & VNodeFlags.Element) !== 0) {
7090
+ if (props[idx + 1] != value && (type & VNodeFlags.Element) !== 0) {
6915
7091
  // Values are different, update DOM
6916
7092
  const element = vnode[ElementVNodeProps.element];
6917
7093
  journal && journal.push(VNodeJournalOpCode.SetAttribute, element, key, value);
6918
7094
  }
6919
7095
  if (value == null) {
6920
- vnode.splice(idx, 2);
7096
+ props.splice(idx, 2);
6921
7097
  }
6922
7098
  else {
6923
- vnode[idx + 1] = value;
7099
+ props[idx + 1] = value;
6924
7100
  }
6925
7101
  }
6926
7102
  else if (value != null) {
6927
- vnode.splice(idx ^ -1, 0, key, value);
7103
+ props.splice(idx ^ -1, 0, key, value);
6928
7104
  if ((type & VNodeFlags.Element) !== 0) {
6929
7105
  // New value, update DOM
6930
7106
  const element = vnode[ElementVNodeProps.element];
@@ -6937,7 +7113,8 @@
6937
7113
  const type = vnode[VNodeProps.flags];
6938
7114
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6939
7115
  vnode_ensureElementInflated(vnode);
6940
- return mapArray_get(vnode, key, vnode_getPropStartIndex(vnode));
7116
+ const props = vnode_getProps(vnode);
7117
+ return mapArray_get(props, key, 0);
6941
7118
  }
6942
7119
  return null;
6943
7120
  };
@@ -6945,11 +7122,12 @@
6945
7122
  const type = vnode[VNodeProps.flags];
6946
7123
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6947
7124
  type & VNodeFlags.Element && vnode_ensureElementInflated(vnode);
6948
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7125
+ const props = vnode_getProps(vnode);
7126
+ const idx = mapApp_findIndx(props, key, 0);
6949
7127
  if (idx >= 0) {
6950
- let value = vnode[idx + 1];
7128
+ let value = props[idx + 1];
6951
7129
  if (typeof value === 'string' && getObject) {
6952
- vnode[idx + 1] = value = getObject(value);
7130
+ props[idx + 1] = value = getObject(value);
6953
7131
  }
6954
7132
  return value;
6955
7133
  }
@@ -6958,12 +7136,13 @@
6958
7136
  };
6959
7137
  const vnode_setProp = (vnode, key, value) => {
6960
7138
  ensureElementOrVirtualVNode(vnode);
6961
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7139
+ const props = vnode_getProps(vnode);
7140
+ const idx = mapApp_findIndx(props, key, 0);
6962
7141
  if (idx >= 0) {
6963
- vnode[idx + 1] = value;
7142
+ props[idx + 1] = value;
6964
7143
  }
6965
7144
  else if (value != null) {
6966
- vnode.splice(idx ^ -1, 0, key, value);
7145
+ props.splice(idx ^ -1, 0, key, value);
6967
7146
  }
6968
7147
  };
6969
7148
  const vnode_getPropStartIndex = (vnode) => {
@@ -6976,6 +7155,9 @@
6976
7155
  }
6977
7156
  throw qError(QError.invalidVNodeType, [type]);
6978
7157
  };
7158
+ const vnode_getProps = (vnode) => {
7159
+ return vnode[vnode_getPropStartIndex(vnode)];
7160
+ };
6979
7161
  const vnode_getParent = (vnode) => {
6980
7162
  return vnode[VNodeProps.parent] || null;
6981
7163
  };
@@ -7127,9 +7309,6 @@
7127
7309
  else if (peek() === VNodeDataChar.PROPS) {
7128
7310
  vnode_setAttr(null, vParent, ELEMENT_PROPS, consumeValue());
7129
7311
  }
7130
- else if (peek() === VNodeDataChar.SLOT_REF) {
7131
- vnode_setAttr(null, vParent, QSlotRef, consumeValue());
7132
- }
7133
7312
  else if (peek() === VNodeDataChar.KEY) {
7134
7313
  vnode_setAttr(null, vParent, ELEMENT_KEY, consumeValue());
7135
7314
  }
@@ -7139,8 +7318,14 @@
7139
7318
  else if (peek() === VNodeDataChar.SEQ_IDX) {
7140
7319
  vnode_setAttr(null, vParent, ELEMENT_SEQ_IDX, consumeValue());
7141
7320
  }
7142
- else if (peek() === VNodeDataChar.SUBS) {
7143
- vnode_setProp(vParent, QSubscribers, consumeValue());
7321
+ else if (peek() === VNodeDataChar.BACK_REFS) {
7322
+ if (!container) {
7323
+ container = getDomContainer(element);
7324
+ }
7325
+ setEffectBackRefFromVNodeData(vParent, consumeValue(), container);
7326
+ }
7327
+ else if (peek() === VNodeDataChar.SLOT_PARENT) {
7328
+ vnode_setProp(vParent, QSlotParent, consumeValue());
7144
7329
  }
7145
7330
  else if (peek() === VNodeDataChar.CONTEXT) {
7146
7331
  vnode_setAttr(null, vParent, QCtxAttr, consumeValue());
@@ -7248,23 +7433,20 @@
7248
7433
  };
7249
7434
  const VNodeArray = class VNode extends Array {
7250
7435
  static createElement(flags, parent, previousSibling, nextSibling, firstChild, lastChild, element, elementName) {
7251
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7252
- vnode.push(firstChild, lastChild, element, elementName);
7436
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, firstChild, lastChild, element, elementName, []);
7253
7437
  return vnode;
7254
7438
  }
7255
7439
  static createText(flags, parent, previousSibling, nextSibling, textNode, text) {
7256
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7257
- vnode.push(textNode, text);
7440
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, textNode, text);
7258
7441
  return vnode;
7259
7442
  }
7260
7443
  static createVirtual(flags, parent, previousSibling, nextSibling, firstChild, lastChild) {
7261
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7262
- vnode.push(firstChild, lastChild);
7444
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, firstChild, lastChild, []);
7263
7445
  return vnode;
7264
7446
  }
7265
- constructor(flags, parent, previousSibling, nextSibling) {
7266
- super();
7267
- this.push(flags, parent, previousSibling, nextSibling);
7447
+ constructor(flags, parent, previousSibling, nextSibling, ...rest) {
7448
+ // @ts-expect-error
7449
+ super(flags, parent, previousSibling, nextSibling, ...rest);
7268
7450
  if (build.isDev) {
7269
7451
  this.toString = vnode_toString;
7270
7452
  }
@@ -7329,10 +7511,12 @@
7329
7511
  return returnValue;
7330
7512
  }
7331
7513
  const newInvokeContextFromTuple = ([element, event, url]) => {
7332
- const container = element.closest(QContainerSelector);
7514
+ const domContainer = getDomContainer(element);
7515
+ const container = domContainer.element;
7516
+ const vNode = container ? vnode_locate(domContainer.rootVNode, element) : undefined;
7333
7517
  const locale = container?.getAttribute(QLocaleAttr) || undefined;
7334
7518
  locale && setLocale(locale);
7335
- return newInvokeContext(locale, undefined, element, event, url);
7519
+ return newInvokeContext(locale, vNode, element, event, url);
7336
7520
  };
7337
7521
  // TODO how about putting url and locale (and event/custom?) in to a "static" object
7338
7522
  const newInvokeContext = (locale, hostElement, element, event, url) => {
@@ -7367,16 +7551,14 @@
7367
7551
  * @param property `true` - subscriber is component `false` - subscriber is VNode `string` -
7368
7552
  * subscriber is property
7369
7553
  * @param container
7554
+ * @param data - Additional subscription data
7370
7555
  * @returns
7371
7556
  */
7372
7557
  const trackSignal = (fn, subscriber, property, container, data) => {
7373
7558
  const previousSubscriber = trackInvocation.$effectSubscriber$;
7374
7559
  const previousContainer = trackInvocation.$container$;
7375
7560
  try {
7376
- trackInvocation.$effectSubscriber$ = [subscriber, property];
7377
- if (data) {
7378
- trackInvocation.$effectSubscriber$.push(data);
7379
- }
7561
+ trackInvocation.$effectSubscriber$ = getSubscriber(subscriber, property, data);
7380
7562
  trackInvocation.$container$ = container;
7381
7563
  return invoke(trackInvocation, fn);
7382
7564
  }
@@ -7397,8 +7579,16 @@
7397
7579
  if (iCtx) {
7398
7580
  const hostElement = iCtx.$hostElement$;
7399
7581
  let element = null;
7400
- if (vnode_isVNode(hostElement) && vnode_isElementVNode(hostElement)) {
7401
- element = vnode_getNode(hostElement);
7582
+ if (hostElement != null) {
7583
+ if (vnode_isVNode(hostElement)) {
7584
+ if (vnode_isElementVNode(hostElement)) {
7585
+ element = vnode_getNode(hostElement);
7586
+ }
7587
+ }
7588
+ else {
7589
+ // isSSRnode
7590
+ element = hostElement;
7591
+ }
7402
7592
  }
7403
7593
  return element ?? iCtx.$qrl$?.$setContainer$(undefined);
7404
7594
  }
@@ -8034,12 +8224,12 @@
8034
8224
  $storeProxyMap$ = new WeakMap();
8035
8225
  $qFuncs$;
8036
8226
  $instanceHash$;
8037
- stateData;
8227
+ vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
8228
+ $stateData$;
8038
8229
  $styleIds$ = null;
8039
- $vnodeLocate$ = (id) => vnode_locate(this.rootVNode, id);
8040
8230
  $renderCount$ = 0;
8041
8231
  constructor(element) {
8042
- super(() => this.scheduleRender(), () => vnode_applyJournal(this.$journal$), {}, element.getAttribute('q:locale'));
8232
+ super(() => this.scheduleRender(), () => vnode_applyJournal(this.$journal$), {}, element.getAttribute(QLocaleAttr));
8043
8233
  this.qContainer = element.getAttribute(QContainerAttr);
8044
8234
  if (!this.qContainer) {
8045
8235
  throw qError(QError.elementWithoutContainer);
@@ -8062,29 +8252,29 @@
8062
8252
  this.rootVNode = vnode_newUnMaterializedElement(this.element);
8063
8253
  // These are here to initialize all properties at once for single class transition
8064
8254
  this.$rawStateData$ = null;
8065
- this.stateData = null;
8255
+ this.$stateData$ = null;
8066
8256
  const document = this.element.ownerDocument;
8067
8257
  if (!document.qVNodeData) {
8068
8258
  processVNodeData(document);
8069
8259
  }
8070
8260
  this.$rawStateData$ = [];
8071
- this.stateData = [];
8261
+ this.$stateData$ = [];
8072
8262
  const qwikStates = element.querySelectorAll('script[type="qwik/state"]');
8073
8263
  if (qwikStates.length !== 0) {
8074
8264
  const lastState = qwikStates[qwikStates.length - 1];
8075
8265
  this.$rawStateData$ = JSON.parse(lastState.textContent);
8076
- this.stateData = wrapDeserializerProxy(this, this.$rawStateData$);
8266
+ this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
8077
8267
  }
8078
8268
  this.$qFuncs$ = getQFuncs(document, this.$instanceHash$) || EMPTY_ARRAY;
8079
8269
  }
8080
8270
  $setRawState$(id, vParent) {
8081
- this.stateData[id] = vParent;
8271
+ this.$stateData$[id] = vParent;
8082
8272
  }
8083
8273
  parseQRL(qrl) {
8084
8274
  return inflateQRL(this, parseQRL(qrl));
8085
8275
  }
8086
8276
  handleError(err, host) {
8087
- if (qDev) {
8277
+ if (qDev && host) {
8088
8278
  // Clean vdom
8089
8279
  if (typeof document !== 'undefined') {
8090
8280
  const vHost = host;
@@ -8108,7 +8298,7 @@
8108
8298
  throw err;
8109
8299
  }
8110
8300
  }
8111
- const errorStore = this.resolveContext(host, ERROR_CONTEXT);
8301
+ const errorStore = host && this.resolveContext(host, ERROR_CONTEXT);
8112
8302
  if (!errorStore) {
8113
8303
  throw err;
8114
8304
  }
@@ -8144,7 +8334,7 @@
8144
8334
  vNode =
8145
8335
  vnode_getParent(vNode) ||
8146
8336
  // If virtual node, than it could be a slot so we need to read its parent.
8147
- vnode_getProp(vNode, QSlotParent, this.$vnodeLocate$);
8337
+ vnode_getProp(vNode, QSlotParent, this.vNodeLocate);
8148
8338
  }
8149
8339
  else {
8150
8340
  vNode = vnode_getParent(vNode);
@@ -8164,7 +8354,7 @@
8164
8354
  case ELEMENT_PROPS:
8165
8355
  case OnRenderProp:
8166
8356
  case QCtxAttr:
8167
- case QSubscribers:
8357
+ case QBackRefs:
8168
8358
  getObjectById = this.$getObjectById$;
8169
8359
  break;
8170
8360
  case ELEMENT_SEQ_IDX:
@@ -8177,7 +8367,7 @@
8177
8367
  scheduleRender() {
8178
8368
  this.$renderCount$++;
8179
8369
  this.renderDone ||= getPlatform().nextTick(() => this.processChores());
8180
- return this.renderDone;
8370
+ return this.renderDone.finally(() => emitEvent('qrender', { instanceHash: this.$instanceHash$, renderCount: this.$renderCount$ }));
8181
8371
  }
8182
8372
  processChores() {
8183
8373
  let renderCount = this.$renderCount$;
@@ -8200,12 +8390,13 @@
8200
8390
  ensureProjectionResolved(vNode) {
8201
8391
  if ((vNode[VNodeProps.flags] & VNodeFlags.Resolved) === 0) {
8202
8392
  vNode[VNodeProps.flags] |= VNodeFlags.Resolved;
8203
- for (let i = vnode_getPropStartIndex(vNode); i < vNode.length; i = i + 2) {
8204
- const prop = vNode[i];
8393
+ const props = vnode_getProps(vNode);
8394
+ for (let i = 0; i < props.length; i = i + 2) {
8395
+ const prop = props[i];
8205
8396
  if (isSlotProp(prop)) {
8206
- const value = vNode[i + 1];
8397
+ const value = props[i + 1];
8207
8398
  if (typeof value == 'string') {
8208
- vNode[i + 1] = this.$vnodeLocate$(value);
8399
+ props[i + 1] = this.vNodeLocate(value);
8209
8400
  }
8210
8401
  }
8211
8402
  }
@@ -8216,7 +8407,7 @@
8216
8407
  id = parseFloat(id);
8217
8408
  }
8218
8409
  assertTrue(id < this.$rawStateData$.length / 2, `Invalid reference: ${id} >= ${this.$rawStateData$.length / 2}`);
8219
- return this.stateData[id];
8410
+ return this.$stateData$[id];
8220
8411
  };
8221
8412
  getSyncFn(id) {
8222
8413
  const fn = this.$qFuncs$[id];
@@ -8288,8 +8479,7 @@
8288
8479
  ? parseInt(property, 10)
8289
8480
  : NaN;
8290
8481
  if (Number.isNaN(i) || i < 0 || i >= this.$length$) {
8291
- const out = Reflect.get(target, property, receiver);
8292
- return out;
8482
+ return Reflect.get(target, property, receiver);
8293
8483
  }
8294
8484
  // The serialized data is an array with 2 values for each item
8295
8485
  const idx = i * 2;
@@ -8355,6 +8545,7 @@
8355
8545
  switch (typeId) {
8356
8546
  case TypeIds.Object:
8357
8547
  // We use getters for making complex values lazy
8548
+ // TODO scan the data for computeQRLs and schedule resolve chores
8358
8549
  for (let i = 0; i < data.length; i += 4) {
8359
8550
  const key = deserializeData(container, data[i], data[i + 1]);
8360
8551
  const valType = data[i + 2];
@@ -8394,7 +8585,7 @@
8394
8585
  task.$flags$ = v[1];
8395
8586
  task.$index$ = v[2];
8396
8587
  task.$el$ = v[3];
8397
- task.$effectDependencies$ = v[4];
8588
+ task[_EFFECT_BACK_REF] = v[4];
8398
8589
  task.$state$ = v[5];
8399
8590
  break;
8400
8591
  case TypeIds.Resource:
@@ -8417,12 +8608,9 @@
8417
8608
  break;
8418
8609
  case TypeIds.Store:
8419
8610
  case TypeIds.StoreArray: {
8420
- const [value, flags, effects, storeEffect] = data;
8611
+ const [value, flags, effects] = data;
8421
8612
  const store = getOrCreateStore(value, flags, container);
8422
8613
  const storeHandler = getStoreHandler(store);
8423
- if (storeEffect) {
8424
- effects[STORE_ARRAY_PROP] = storeEffect;
8425
- }
8426
8614
  storeHandler.$effects$ = effects;
8427
8615
  target = store;
8428
8616
  break;
@@ -8431,7 +8619,7 @@
8431
8619
  const signal = target;
8432
8620
  const d = data;
8433
8621
  signal.$untrackedValue$ = d[0];
8434
- signal.$effects$ = d.slice(1);
8622
+ signal.$effects$ = new Set(d.slice(1));
8435
8623
  break;
8436
8624
  }
8437
8625
  case TypeIds.WrappedSignal: {
@@ -8439,10 +8627,11 @@
8439
8627
  const d = data;
8440
8628
  signal.$func$ = container.getSyncFn(d[0]);
8441
8629
  signal.$args$ = d[1];
8442
- signal.$effectDependencies$ = d[2];
8630
+ signal[_EFFECT_BACK_REF] = d[2];
8443
8631
  signal.$untrackedValue$ = d[3];
8444
- signal.$hostElement$ = d[4];
8445
- signal.$effects$ = d.slice(5);
8632
+ signal.$flags$ = d[4];
8633
+ signal.$hostElement$ = d[5];
8634
+ signal.$effects$ = new Set(d.slice(6));
8446
8635
  break;
8447
8636
  }
8448
8637
  case TypeIds.ComputedSignal: {
@@ -8454,7 +8643,7 @@
8454
8643
  computed.$untrackedValue$ = d[2];
8455
8644
  }
8456
8645
  else {
8457
- computed.$invalid$ = true;
8646
+ computed.$flags$ |= SignalFlags.INVALID;
8458
8647
  /**
8459
8648
  * If we try to compute value and the qrl is not resolved, then system throws an error with
8460
8649
  * qrl promise. To prevent that we should early resolve computed qrl while computed
@@ -8562,6 +8751,7 @@
8562
8751
  EMPTY_ARRAY,
8563
8752
  EMPTY_OBJ,
8564
8753
  NEEDS_COMPUTATION,
8754
+ STORE_ALL_PROPS,
8565
8755
  Slot,
8566
8756
  Fragment,
8567
8757
  NaN,
@@ -8642,6 +8832,8 @@
8642
8832
  reject = rej;
8643
8833
  });
8644
8834
  resolvers.set(promise, [resolve, reject]);
8835
+ // Don't leave unhandled promise rejections
8836
+ promise.catch(() => { });
8645
8837
  return promise;
8646
8838
  case TypeIds.Uint8Array:
8647
8839
  const encodedLength = value.length;
@@ -8651,12 +8843,9 @@
8651
8843
  return new Uint8Array(decodedLength);
8652
8844
  case TypeIds.PropsProxy:
8653
8845
  return createPropsProxy(null, null);
8654
- case TypeIds.RefVNode:
8655
8846
  case TypeIds.VNode:
8656
- const vnodeOrDocument = retrieveVNodeOrDocument(container, value);
8657
- if (typeId === TypeIds.VNode) {
8658
- return vnodeOrDocument;
8659
- }
8847
+ return retrieveVNodeOrDocument(container, value);
8848
+ case TypeIds.RefVNode:
8660
8849
  const vNode = retrieveVNodeOrDocument(container, value);
8661
8850
  if (vnode_isVNode(vNode)) {
8662
8851
  return vnode_getNode(vNode);
@@ -8665,7 +8854,7 @@
8665
8854
  throw qError(QError.serializeErrorExpectedVNode, [typeof vNode]);
8666
8855
  }
8667
8856
  case TypeIds.EffectData:
8668
- return new EffectPropData({});
8857
+ return new SubscriptionData({});
8669
8858
  default:
8670
8859
  throw qError(QError.serializeErrorCannotAllocate, [typeId]);
8671
8860
  }
@@ -8697,7 +8886,7 @@
8697
8886
  assertDefined(backChannel, 'Missing QRL_RUNTIME_CHUNK');
8698
8887
  qrlRef = backChannel.get(symbol);
8699
8888
  }
8700
- return createQRL(chunk, symbol, qrlRef, null, captureIds, null, null);
8889
+ return createQRL(chunk, symbol, qrlRef, null, captureIds, null);
8701
8890
  }
8702
8891
  function inflateQRL(container, qrl) {
8703
8892
  const captureIds = qrl.$capture$;
@@ -8815,7 +9004,7 @@
8815
9004
  /** Visit an object, adding anything that will be serialized as to scan */
8816
9005
  const visit = (obj) => {
8817
9006
  if (typeof obj === 'function') {
8818
- if (isQrl(obj)) {
9007
+ if (isQrl$1(obj)) {
8819
9008
  if (obj.$captureRef$) {
8820
9009
  discoveredValues.push(...obj.$captureRef$);
8821
9010
  }
@@ -8834,6 +9023,7 @@
8834
9023
  obj instanceof RegExp ||
8835
9024
  obj instanceof Uint8Array ||
8836
9025
  obj instanceof URLSearchParams ||
9026
+ vnode_isVNode(obj) ||
8837
9027
  (typeof FormData !== 'undefined' && obj instanceof FormData) ||
8838
9028
  // Ignore the no serialize objects
8839
9029
  fastSkipSerialize(obj)) ;
@@ -8843,8 +9033,7 @@
8843
9033
  else if (isStore(obj)) {
8844
9034
  const target = getStoreTarget(obj);
8845
9035
  const effects = getStoreHandler(obj).$effects$;
8846
- const storeEffect = effects?.[STORE_ARRAY_PROP] ?? null;
8847
- discoveredValues.push(target, effects, storeEffect);
9036
+ discoveredValues.push(target, effects);
8848
9037
  for (const prop in target) {
8849
9038
  const propValue = target[prop];
8850
9039
  if (storeProxyMap.has(propValue)) {
@@ -8867,20 +9056,19 @@
8867
9056
  */
8868
9057
  const v = obj instanceof WrappedSignal
8869
9058
  ? obj.untrackedValue
8870
- : obj instanceof ComputedSignal && (obj.$invalid$ || fastSkipSerialize(obj))
9059
+ : obj instanceof ComputedSignal &&
9060
+ (obj.$flags$ & SignalFlags.INVALID || fastSkipSerialize(obj))
8871
9061
  ? NEEDS_COMPUTATION
8872
9062
  : obj.$untrackedValue$;
8873
9063
  if (v !== NEEDS_COMPUTATION) {
8874
9064
  discoveredValues.push(v);
8875
9065
  }
8876
9066
  if (obj.$effects$) {
8877
- discoveredValues.push(...obj.$effects$);
9067
+ discoveredValues.push(obj.$effects$);
8878
9068
  }
8879
9069
  // WrappedSignal uses syncQrl which has no captured refs
8880
9070
  if (obj instanceof WrappedSignal) {
8881
- if (obj.$effectDependencies$) {
8882
- discoveredValues.push(...obj.$effectDependencies$);
8883
- }
9071
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8884
9072
  if (obj.$args$) {
8885
9073
  discoveredValues.push(...obj.$args$);
8886
9074
  }
@@ -8889,11 +9077,13 @@
8889
9077
  }
8890
9078
  }
8891
9079
  else if (obj instanceof ComputedSignal) {
9080
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8892
9081
  discoveredValues.push(obj.$computeQrl$);
8893
9082
  }
8894
9083
  }
8895
9084
  else if (obj instanceof Task) {
8896
- discoveredValues.push(obj.$el$, obj.$qrl$, obj.$state$, obj.$effectDependencies$);
9085
+ discoveredValues.push(obj.$el$, obj.$qrl$, obj.$state$);
9086
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8897
9087
  }
8898
9088
  else if (isSsrNode(obj)) {
8899
9089
  discoverValuesForVNodeData(obj.vnodeData, discoveredValues);
@@ -8912,7 +9102,7 @@
8912
9102
  else if (Array.isArray(obj)) {
8913
9103
  discoveredValues.push(...obj);
8914
9104
  }
8915
- else if (isQrl(obj)) {
9105
+ else if (isQrl$1(obj)) {
8916
9106
  obj.$captureRef$ && obj.$captureRef$.length && discoveredValues.push(...obj.$captureRef$);
8917
9107
  }
8918
9108
  else if (isPropsProxy(obj)) {
@@ -8928,7 +9118,7 @@
8928
9118
  });
8929
9119
  promises.push(obj);
8930
9120
  }
8931
- else if (obj instanceof EffectPropData) {
9121
+ else if (obj instanceof SubscriptionData) {
8932
9122
  discoveredValues.push(obj.data);
8933
9123
  }
8934
9124
  else if (isObjectLiteral(obj)) {
@@ -8973,15 +9163,20 @@
8973
9163
  for (const value of vnodeData) {
8974
9164
  if (isSsrAttrs(value)) {
8975
9165
  for (let i = 1; i < value.length; i += 2) {
8976
- if (value[i - 1] === ELEMENT_KEY) {
9166
+ const attrValue = value[i];
9167
+ if (typeof attrValue === 'string') {
8977
9168
  continue;
8978
9169
  }
8979
- const attrValue = value[i];
8980
9170
  discoveredValues.push(attrValue);
8981
9171
  }
8982
9172
  }
8983
9173
  }
8984
9174
  };
9175
+ const discoverEffectBackRefs = (effectsBackRefs, discoveredValues) => {
9176
+ if (effectsBackRefs) {
9177
+ discoveredValues.push(effectsBackRefs);
9178
+ }
9179
+ };
8985
9180
  const promiseResults = new WeakMap();
8986
9181
  /**
8987
9182
  * Format:
@@ -9053,7 +9248,7 @@
9053
9248
  else if (value === Fragment) {
9054
9249
  output(TypeIds.Constant, Constants.Fragment);
9055
9250
  }
9056
- else if (isQrl(value)) {
9251
+ else if (isQrl$1(value)) {
9057
9252
  const qrl = qrlToString(serializationContext, value);
9058
9253
  const id = serializationContext.$addRoot$(qrl);
9059
9254
  output(TypeIds.QRL, id);
@@ -9128,6 +9323,9 @@
9128
9323
  else if (value === NEEDS_COMPUTATION) {
9129
9324
  output(TypeIds.Constant, Constants.NEEDS_COMPUTATION);
9130
9325
  }
9326
+ else if (value === STORE_ALL_PROPS) {
9327
+ output(TypeIds.Constant, Constants.STORE_ALL_PROPS);
9328
+ }
9131
9329
  else {
9132
9330
  throw qError(QError.serializeErrorUnknownType, [typeof value]);
9133
9331
  }
@@ -9164,7 +9362,7 @@
9164
9362
  : 0;
9165
9363
  output(TypeIds.PropsProxy, out);
9166
9364
  }
9167
- else if (value instanceof EffectPropData) {
9365
+ else if (value instanceof SubscriptionData) {
9168
9366
  output(TypeIds.EffectData, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
9169
9367
  }
9170
9368
  else if (isStore(value)) {
@@ -9175,6 +9373,7 @@
9175
9373
  if (!res) {
9176
9374
  throw qError(QError.serializeErrorUnvisited, ['resource']);
9177
9375
  }
9376
+ // TODO the effects include the resourcereturn which has duplicate data
9178
9377
  output(TypeIds.Resource, [...res, getStoreHandler(value).$effects$]);
9179
9378
  }
9180
9379
  else {
@@ -9182,7 +9381,6 @@
9182
9381
  const storeTarget = getStoreTarget(value);
9183
9382
  const flags = storeHandler.$flags$;
9184
9383
  const effects = storeHandler.$effects$;
9185
- const storeEffect = effects?.[STORE_ARRAY_PROP] ?? null;
9186
9384
  const innerStores = [];
9187
9385
  for (const prop in storeTarget) {
9188
9386
  const propValue = storeTarget[prop];
@@ -9192,7 +9390,7 @@
9192
9390
  serializationContext.$addRoot$(innerStore);
9193
9391
  }
9194
9392
  }
9195
- const out = [storeTarget, flags, effects, storeEffect, ...innerStores];
9393
+ const out = [storeTarget, flags, effects, ...innerStores];
9196
9394
  while (out[out.length - 1] == null) {
9197
9395
  out.pop();
9198
9396
  }
@@ -9225,14 +9423,15 @@
9225
9423
  * It can never be meant to become a vNode, because vNodes are internal only.
9226
9424
  */
9227
9425
  const v = value instanceof ComputedSignal &&
9228
- (value.$invalid$ || fastSkipSerialize(value.$untrackedValue$))
9426
+ (value.$flags$ & SignalFlags.INVALID || fastSkipSerialize(value.$untrackedValue$))
9229
9427
  ? NEEDS_COMPUTATION
9230
9428
  : value.$untrackedValue$;
9231
9429
  if (value instanceof WrappedSignal) {
9232
9430
  output(TypeIds.WrappedSignal, [
9233
9431
  ...serializeWrappingFn(serializationContext, value),
9234
- value.$effectDependencies$,
9432
+ filterEffectBackRefs(value[_EFFECT_BACK_REF]),
9235
9433
  v,
9434
+ value.$flags$,
9236
9435
  value.$hostElement$,
9237
9436
  ...(value.$effects$ || []),
9238
9437
  ]);
@@ -9338,7 +9537,7 @@
9338
9537
  value.$flags$,
9339
9538
  value.$index$,
9340
9539
  value.$el$,
9341
- value.$effectDependencies$,
9540
+ value[_EFFECT_BACK_REF],
9342
9541
  value.$state$,
9343
9542
  ];
9344
9543
  while (out[out.length - 1] == null) {
@@ -9361,12 +9560,27 @@
9361
9560
  const out = btoa(buf).replace(/=+$/, '');
9362
9561
  output(TypeIds.Uint8Array, out);
9363
9562
  }
9563
+ else if (vnode_isVNode(value)) {
9564
+ output(TypeIds.Constant, Constants.Undefined);
9565
+ }
9364
9566
  else {
9365
9567
  throw qError(QError.serializeErrorUnknownType, [typeof value]);
9366
9568
  }
9367
9569
  };
9368
9570
  writeValue(serializationContext.$roots$, -1);
9369
9571
  }
9572
+ function filterEffectBackRefs(effectBackRef) {
9573
+ let effectBackRefToSerialize = null;
9574
+ if (effectBackRef) {
9575
+ for (const [effectProp, effect] of effectBackRef) {
9576
+ if (effect[EffectSubscriptionProp.BACK_REF]) {
9577
+ effectBackRefToSerialize ||= new Map();
9578
+ effectBackRefToSerialize.set(effectProp, effect);
9579
+ }
9580
+ }
9581
+ }
9582
+ return effectBackRefToSerialize;
9583
+ }
9370
9584
  function serializeWrappingFn(serializationContext, value) {
9371
9585
  // if value is an object then we need to wrap this in ()
9372
9586
  if (value.$funcStr$ && value.$funcStr$[0] === '{') {
@@ -9379,15 +9593,12 @@
9379
9593
  function qrlToString(serializationContext, value) {
9380
9594
  let symbol = value.$symbol$;
9381
9595
  let chunk = value.$chunk$;
9382
- const refSymbol = value.$refSymbol$ ?? symbol;
9383
9596
  const platform = getPlatform();
9384
9597
  if (platform) {
9385
- const result = platform.chunkForSymbol(refSymbol, chunk, value.dev?.file);
9598
+ const result = platform.chunkForSymbol(symbol, chunk, value.dev?.file);
9386
9599
  if (result) {
9387
9600
  chunk = result[1];
9388
- if (!value.$refSymbol$) {
9389
- symbol = result[0];
9390
- }
9601
+ symbol = result[0];
9391
9602
  }
9392
9603
  }
9393
9604
  const isSync = isSyncQrl(value);
@@ -9564,9 +9775,9 @@
9564
9775
  return ((typeof obj === 'object' &&
9565
9776
  obj !== null &&
9566
9777
  (obj instanceof Signal || obj instanceof Task || isJSXNode(obj))) ||
9567
- isQrl(obj));
9778
+ isQrl$1(obj));
9568
9779
  };
9569
- const canSerialize = (value) => {
9780
+ const canSerialize = (value, seen = new WeakSet()) => {
9570
9781
  if (value == null ||
9571
9782
  typeof value === 'string' ||
9572
9783
  typeof value === 'number' ||
@@ -9575,6 +9786,10 @@
9575
9786
  return true;
9576
9787
  }
9577
9788
  else if (typeof value === 'object') {
9789
+ if (seen.has(value)) {
9790
+ return true;
9791
+ }
9792
+ seen.add(value);
9578
9793
  const proto = Object.getPrototypeOf(value);
9579
9794
  if (isStore(value)) {
9580
9795
  value = getStoreTarget(value);
@@ -9583,7 +9798,7 @@
9583
9798
  for (const key in value) {
9584
9799
  // if the value is a props proxy, then sometimes we could create a component-level subscription,
9585
9800
  // so we should call untrack here to avoid tracking the value
9586
- if (!canSerialize(untrack(() => value[key]))) {
9801
+ if (!canSerialize(untrack(() => value[key]), seen)) {
9587
9802
  return false;
9588
9803
  }
9589
9804
  }
@@ -9591,7 +9806,7 @@
9591
9806
  }
9592
9807
  else if (proto == Array.prototype) {
9593
9808
  for (let i = 0; i < value.length; i++) {
9594
- if (!canSerialize(value[i])) {
9809
+ if (!canSerialize(value[i], seen)) {
9595
9810
  return false;
9596
9811
  }
9597
9812
  }
@@ -9641,7 +9856,7 @@
9641
9856
  }
9642
9857
  }
9643
9858
  else if (typeof value === 'function') {
9644
- if (isQrl(value) || isQwikComponent(value)) {
9859
+ if (isQrl$1(value) || isQwikComponent(value)) {
9645
9860
  return true;
9646
9861
  }
9647
9862
  }
@@ -9694,20 +9909,21 @@
9694
9909
  Constants[Constants["EMPTY_ARRAY"] = 5] = "EMPTY_ARRAY";
9695
9910
  Constants[Constants["EMPTY_OBJ"] = 6] = "EMPTY_OBJ";
9696
9911
  Constants[Constants["NEEDS_COMPUTATION"] = 7] = "NEEDS_COMPUTATION";
9697
- Constants[Constants["Slot"] = 8] = "Slot";
9698
- Constants[Constants["Fragment"] = 9] = "Fragment";
9699
- Constants[Constants["NaN"] = 10] = "NaN";
9700
- Constants[Constants["PositiveInfinity"] = 11] = "PositiveInfinity";
9701
- Constants[Constants["NegativeInfinity"] = 12] = "NegativeInfinity";
9702
- Constants[Constants["MaxSafeInt"] = 13] = "MaxSafeInt";
9912
+ Constants[Constants["STORE_ALL_PROPS"] = 8] = "STORE_ALL_PROPS";
9913
+ Constants[Constants["Slot"] = 9] = "Slot";
9914
+ Constants[Constants["Fragment"] = 10] = "Fragment";
9915
+ Constants[Constants["NaN"] = 11] = "NaN";
9916
+ Constants[Constants["PositiveInfinity"] = 12] = "PositiveInfinity";
9917
+ Constants[Constants["NegativeInfinity"] = 13] = "NegativeInfinity";
9918
+ Constants[Constants["MaxSafeInt"] = 14] = "MaxSafeInt";
9703
9919
  // used for close fragment
9704
- Constants[Constants["AlmostMaxSafeInt"] = 14] = "AlmostMaxSafeInt";
9705
- Constants[Constants["MinSafeInt"] = 15] = "MinSafeInt";
9920
+ Constants[Constants["AlmostMaxSafeInt"] = 15] = "AlmostMaxSafeInt";
9921
+ Constants[Constants["MinSafeInt"] = 16] = "MinSafeInt";
9706
9922
  })(Constants || (Constants = {}));
9707
9923
 
9708
9924
  /** @internal */
9709
9925
  const verifySerializable = (value, preMessage) => {
9710
- const seen = new Set();
9926
+ const seen = new WeakSet();
9711
9927
  return _verifySerializable(value, seen, '_', preMessage);
9712
9928
  };
9713
9929
  const _verifySerializable = (value, seen, ctx, preMessage) => {
@@ -9716,10 +9932,12 @@
9716
9932
  return value;
9717
9933
  }
9718
9934
  if (shouldSerialize(unwrapped)) {
9719
- if (seen.has(unwrapped)) {
9720
- return value;
9935
+ if (typeof unwrapped === 'object') {
9936
+ if (seen.has(unwrapped)) {
9937
+ return value;
9938
+ }
9939
+ seen.add(unwrapped);
9721
9940
  }
9722
- seen.add(unwrapped);
9723
9941
  if (isSignal(unwrapped)) {
9724
9942
  return value;
9725
9943
  }
@@ -9823,16 +10041,7 @@
9823
10041
  return input;
9824
10042
  };
9825
10043
 
9826
- const isQrl = (value) => {
9827
- return typeof value === 'function' && typeof value.getSymbol === 'function';
9828
- };
9829
- // Make sure this value is same as value in `platform.ts`
9830
- const SYNC_QRL = '<sync>';
9831
- /** Sync QRL is a function which is serialized into `<script q:func="qwik/json">` tag. */
9832
- const isSyncQrl = (value) => {
9833
- return isQrl(value) && value.$symbol$ == SYNC_QRL;
9834
- };
9835
- const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refSymbol) => {
10044
+ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
9836
10045
  if (qDev && qSerialize) {
9837
10046
  if (captureRef) {
9838
10047
  for (const item of captureRef) {
@@ -9854,9 +10063,14 @@
9854
10063
  };
9855
10064
  function bindFnToContext(currentCtx, beforeFn) {
9856
10065
  // Note that we bind the current `this`
9857
- return (...args) => maybeThen(resolveLazy(), (fn) => {
9858
- if (!isFunction(fn)) {
9859
- throw qError(QError.qrlIsNotFunction);
10066
+ const bound = (...args) => {
10067
+ if (!qrl.resolved) {
10068
+ return qrl.resolve().then((fn) => {
10069
+ if (!isFunction(fn)) {
10070
+ throw qError(QError.qrlIsNotFunction);
10071
+ }
10072
+ return bound(...args);
10073
+ });
9860
10074
  }
9861
10075
  if (beforeFn && beforeFn() === false) {
9862
10076
  return;
@@ -9869,13 +10083,14 @@
9869
10083
  context.$qrl$ = qrl;
9870
10084
  context.$event$ ||= this;
9871
10085
  try {
9872
- return invoke.call(this, context, fn, ...args);
10086
+ return invoke.call(this, context, symbolRef, ...args);
9873
10087
  }
9874
10088
  finally {
9875
10089
  context.$qrl$ = prevQrl;
9876
10090
  context.$event$ = prevEvent;
9877
10091
  }
9878
- });
10092
+ };
10093
+ return bound;
9879
10094
  }
9880
10095
  const resolveLazy = (containerEl) => {
9881
10096
  return symbolRef !== null ? symbolRef : resolve(containerEl);
@@ -9887,8 +10102,20 @@
9887
10102
  }
9888
10103
  return function (...args) {
9889
10104
  let context = tryGetInvokeContext();
10105
+ // use the given qrl if it is the right one
9890
10106
  if (context) {
9891
- return fn.apply(this, args);
10107
+ // TODO check if this is necessary in production
10108
+ if (context.$qrl$?.$symbol$ === qrl.$symbol$) {
10109
+ return fn.apply(this, args);
10110
+ }
10111
+ const prevQrl = context.$qrl$;
10112
+ context.$qrl$ = qrl;
10113
+ try {
10114
+ return fn.apply(this, args);
10115
+ }
10116
+ finally {
10117
+ context.$qrl$ = prevQrl;
10118
+ }
9892
10119
  }
9893
10120
  context = newInvokeContext();
9894
10121
  context.$qrl$ = qrl;
@@ -9916,11 +10143,11 @@
9916
10143
  const start = now();
9917
10144
  const ctx = tryGetInvokeContext();
9918
10145
  if (symbolFn !== null) {
9919
- symbolRef = symbolFn().then((module) => (qrl.resolved = symbolRef = wrapFn(module[symbol])));
10146
+ symbolRef = symbolFn().then((module) => (qrl.resolved = wrapFn((symbolRef = module[symbol]))));
9920
10147
  }
9921
10148
  else {
9922
10149
  const imported = getPlatform().importSymbol(_containerEl, chunk, symbol);
9923
- symbolRef = maybeThen(imported, (ref) => (qrl.resolved = symbolRef = wrapFn(ref)));
10150
+ symbolRef = maybeThen(imported, (ref) => (qrl.resolved = wrapFn((symbolRef = ref))));
9924
10151
  }
9925
10152
  if (typeof symbolRef === 'object' && isPromise(symbolRef)) {
9926
10153
  symbolRef.then(() => emitUsedSymbol(symbol, ctx?.$element$, start), (err) => {
@@ -9943,10 +10170,9 @@
9943
10170
  return invoke;
9944
10171
  }
9945
10172
  };
9946
- const resolvedSymbol = refSymbol ?? symbol;
9947
- const hash = getSymbolHash(resolvedSymbol);
10173
+ const hash = getSymbolHash(symbol);
9948
10174
  Object.assign(qrl, {
9949
- getSymbol: () => resolvedSymbol,
10175
+ getSymbol: () => symbol,
9950
10176
  getHash: () => hash,
9951
10177
  getCaptured: () => captureRef,
9952
10178
  resolve,
@@ -9954,7 +10180,6 @@
9954
10180
  $setContainer$: setContainer,
9955
10181
  $chunk$: chunk,
9956
10182
  $symbol$: symbol,
9957
- $refSymbol$: refSymbol,
9958
10183
  $hash$: hash,
9959
10184
  getFn: bindFnToContext,
9960
10185
  $capture$: capture,
@@ -9963,8 +10188,8 @@
9963
10188
  resolved: undefined,
9964
10189
  });
9965
10190
  if (symbolRef) {
9966
- // Replace symbolRef with (a promise for) the value or wrapped function
9967
- symbolRef = maybeThen(symbolRef, (resolved) => (qrl.resolved = symbolRef = wrapFn(resolved)));
10191
+ // Unwrap any promises
10192
+ symbolRef = maybeThen(symbolRef, (resolved) => (qrl.resolved = wrapFn((symbolRef = resolved))));
9968
10193
  }
9969
10194
  if (build.isDev) {
9970
10195
  Object.defineProperty(qrl, '_devOnlySymbolRef', {
@@ -9978,20 +10203,6 @@
9978
10203
  }
9979
10204
  return qrl;
9980
10205
  };
9981
- const getSymbolHash = (symbolName) => {
9982
- const index = symbolName.lastIndexOf('_');
9983
- if (index > -1) {
9984
- return symbolName.slice(index + 1);
9985
- }
9986
- return symbolName;
9987
- };
9988
- function assertQrl(qrl) {
9989
- if (qDev) {
9990
- if (!isQrl(qrl)) {
9991
- throw new Error('Not a QRL');
9992
- }
9993
- }
9994
- }
9995
10206
  const EMITTED = /*#__PURE__*/ new Set();
9996
10207
  const emitUsedSymbol = (symbol, element, reqTime) => {
9997
10208
  if (!EMITTED.has(symbol)) {
@@ -10104,7 +10315,7 @@
10104
10315
  if (!qRuntimeQrl && qDev) {
10105
10316
  throw new Error('Optimizer should replace all usages of $() with some special syntax. If you need to create a QRL manually, use inlinedQrl() instead.');
10106
10317
  }
10107
- return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null, null);
10318
+ return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null);
10108
10319
  };
10109
10320
  /** @private Use To avoid optimizer replacement */
10110
10321
  const dollar = $;
@@ -10130,7 +10341,7 @@
10130
10341
  // eslint-disable-next-line no-new-func
10131
10342
  fn = new Function('return ' + fn.toString())();
10132
10343
  }
10133
- return createQRL('', SYNC_QRL, fn, null, null, null, null);
10344
+ return createQRL('', SYNC_QRL, fn, null, null, null);
10134
10345
  };
10135
10346
  /**
10136
10347
  * Extract function into a synchronously loadable QRL.
@@ -10147,7 +10358,7 @@
10147
10358
  serializedFn = fn.toString();
10148
10359
  }
10149
10360
  fn.serialized = serializedFn;
10150
- return createQRL('', SYNC_QRL, fn, null, null, null, null);
10361
+ return createQRL('', SYNC_QRL, fn, null, null, null);
10151
10362
  };
10152
10363
 
10153
10364
  /** @internal */
@@ -10866,6 +11077,21 @@
10866
11077
  iCtx.$container$.$scheduler$(ChoreType.VISIBLE, task);
10867
11078
  }
10868
11079
  };
11080
+ const useRunTask = (task, eagerness) => {
11081
+ if (eagerness === 'intersection-observer') {
11082
+ useOn('qvisible', getTaskHandlerQrl(task));
11083
+ }
11084
+ else if (eagerness === 'document-ready') {
11085
+ useOnDocument('qinit', getTaskHandlerQrl(task));
11086
+ }
11087
+ else if (eagerness === 'document-idle') {
11088
+ useOnDocument('qidle', getTaskHandlerQrl(task));
11089
+ }
11090
+ };
11091
+ const getTaskHandlerQrl = (task) => {
11092
+ const taskHandler = createQRL(null, '_task', scheduleTask, null, null, [task]);
11093
+ return taskHandler;
11094
+ };
10869
11095
 
10870
11096
  // <docs markdown="../readme.md#useResource">
10871
11097
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
@@ -11202,8 +11428,9 @@
11202
11428
  exports.Slot = Slot;
11203
11429
  exports._CONST_PROPS = _CONST_PROPS;
11204
11430
  exports._DomContainer = DomContainer;
11431
+ exports._EFFECT_BACK_REF = _EFFECT_BACK_REF;
11205
11432
  exports._EMPTY_ARRAY = EMPTY_ARRAY;
11206
- exports._EffectData = EffectPropData;
11433
+ exports._EffectData = SubscriptionData;
11207
11434
  exports._IMMUTABLE = _IMMUTABLE;
11208
11435
  exports._SharedContainer = _SharedContainer;
11209
11436
  exports._VAR_PROPS = _VAR_PROPS;
@@ -11213,7 +11440,6 @@
11213
11440
  exports._getContextEvent = _getContextEvent;
11214
11441
  exports._getDomContainer = getDomContainer;
11215
11442
  exports._getQContainerElement = _getQContainerElement;
11216
- exports._hW = _hW;
11217
11443
  exports._isJSXNode = isJSXNode;
11218
11444
  exports._isStringifiable = isStringifiable;
11219
11445
  exports._jsxBranch = _jsxBranch;
@@ -11227,13 +11453,16 @@
11227
11453
  exports._qrlSync = _qrlSync;
11228
11454
  exports._regSymbol = _regSymbol;
11229
11455
  exports._restProps = _restProps;
11456
+ exports._run = queueQRL;
11230
11457
  exports._serialize = _serialize;
11458
+ exports._task = scheduleTask;
11231
11459
  exports._verifySerializable = verifySerializable;
11232
11460
  exports._waitUntilRendered = _waitUntilRendered;
11233
11461
  exports._walkJSX = _walkJSX;
11234
11462
  exports._weakSerialize = _weakSerialize;
11235
11463
  exports._wrapProp = _wrapProp;
11236
11464
  exports._wrapSignal = _wrapSignal;
11465
+ exports._wrapStore = _wrapStore;
11237
11466
  exports.component$ = component$;
11238
11467
  exports.componentQrl = componentQrl;
11239
11468
  exports.createComputed$ = createComputed$;