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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) 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 -63
  8. package/dist/core-internal.d.ts +78 -77
  9. package/dist/core.cjs +1770 -1517
  10. package/dist/core.cjs.map +1 -1
  11. package/dist/core.min.mjs +1 -1
  12. package/dist/core.mjs +1768 -1517
  13. package/dist/core.mjs.map +1 -1
  14. package/dist/core.prod.cjs +985 -848
  15. package/dist/core.prod.mjs +1210 -1032
  16. package/dist/insights/index.qwik.cjs +3658 -160
  17. package/dist/insights/index.qwik.mjs +3658 -160
  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 +230 -5691
  22. package/dist/optimizer.mjs +192 -5992
  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 +754 -7067
  27. package/dist/server.mjs +771 -7062
  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/turso/src/utils/turso.ts +1 -1
  44. package/dist/starters/features/vitest/package.json +1 -1
  45. package/dist/testing/index.cjs +1445 -1252
  46. package/dist/testing/index.mjs +1455 -1256
  47. package/dist/testing/package.json +1 -1
  48. package/handlers.mjs +9 -0
  49. package/package.json +7 -5
  50. package/public.d.ts +2 -0
  51. package/dist/starters/features/tailwind/.vscode/settings.json +0 -3
  52. package/dist/starters/features/tailwind/postcss.config.cjs +0 -6
  53. 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.5-dev+cb53bbd
3
+ * @qwik.dev/core 2.0.0-alpha.7-dev+a26598a
4
4
  * Copyright QwikDev. All Rights Reserved.
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://github.com/QwikDev/qwik/blob/main/LICENSE
@@ -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,13 +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
- 'SsrError: {{0}}', // 45
169
- 'Cannot coerce a Signal, use `.value` instead', // 46
170
- 'useComputedSignal$ QRL {{0}} {{1}} returned a Promise', // 47
171
- 'ComputedSignal is read-only', // 48
172
- 'WrappedSignal is read-only', // 49
173
- 'SsrError: Promises not expected here.', // 50
174
- '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
175
173
  ];
176
174
  let text = MAP[code] ?? '';
177
175
  if (parts.length) {
@@ -193,8 +191,8 @@
193
191
  var QError;
194
192
  (function (QError) {
195
193
  QError[QError["stringifyClassOrStyle"] = 0] = "stringifyClassOrStyle";
196
- QError[QError["UNUSED_1"] = 1] = "UNUSED_1";
197
- QError[QError["UNUSED_2"] = 2] = "UNUSED_2";
194
+ QError[QError["schedulerNotFound"] = 1] = "schedulerNotFound";
195
+ QError[QError["trackObjectWithoutProp"] = 2] = "trackObjectWithoutProp";
198
196
  QError[QError["verifySerializable"] = 3] = "verifySerializable";
199
197
  QError[QError["UNUSED_4"] = 4] = "UNUSED_4";
200
198
  QError[QError["cannotRenderOverExistingContainer"] = 5] = "cannotRenderOverExistingContainer";
@@ -237,19 +235,153 @@
237
235
  QError[QError["elementWithoutContainer"] = 42] = "elementWithoutContainer";
238
236
  QError[QError["invalidVNodeType"] = 43] = "invalidVNodeType";
239
237
  QError[QError["materializeVNodeDataError"] = 44] = "materializeVNodeDataError";
240
- QError[QError["serverHostMismatch"] = 45] = "serverHostMismatch";
241
- QError[QError["cannotCoerceSignal"] = 46] = "cannotCoerceSignal";
242
- QError[QError["computedNotSync"] = 47] = "computedNotSync";
243
- QError[QError["computedReadOnly"] = 48] = "computedReadOnly";
244
- QError[QError["wrappedReadOnly"] = 49] = "wrappedReadOnly";
245
- QError[QError["promisesNotExpected"] = 50] = "promisesNotExpected";
246
- QError[QError["unsafeAttr"] = 51] = "unsafeAttr";
238
+ QError[QError["cannotCoerceSignal"] = 45] = "cannotCoerceSignal";
239
+ QError[QError["computedNotSync"] = 46] = "computedNotSync";
240
+ QError[QError["computedReadOnly"] = 47] = "computedReadOnly";
241
+ QError[QError["wrappedReadOnly"] = 48] = "wrappedReadOnly";
242
+ QError[QError["unsafeAttr"] = 49] = "unsafeAttr";
247
243
  })(QError || (QError = {}));
248
244
  const qError = (code, errorMessageArgs = []) => {
249
245
  const text = codeToText(code, ...errorMessageArgs);
250
246
  return logErrorAndStop(text, ...errorMessageArgs);
251
247
  };
252
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
+
273
+ /**
274
+ * A friendly name tag for a VirtualVNode.
275
+ *
276
+ * Theses are used to give a name to a VirtualVNode. This is useful for debugging and testing.
277
+ *
278
+ * The name is only added in development mode and is not included in production builds.
279
+ */
280
+ const DEBUG_TYPE = 'q:type';
281
+ var VirtualType;
282
+ (function (VirtualType) {
283
+ VirtualType["Virtual"] = "V";
284
+ VirtualType["Fragment"] = "F";
285
+ VirtualType["WrappedSignal"] = "S";
286
+ VirtualType["Awaited"] = "A";
287
+ VirtualType["Component"] = "C";
288
+ VirtualType["InlineComponent"] = "I";
289
+ VirtualType["Projection"] = "P";
290
+ })(VirtualType || (VirtualType = {}));
291
+ const START = '\x1b[34m';
292
+ const END = '\x1b[0m';
293
+ const VirtualTypeName = {
294
+ [VirtualType.Virtual]: /* ********* */ START + 'Virtual' + END, //
295
+ [VirtualType.Fragment]: /* ******** */ START + 'Fragment' + END, //
296
+ [VirtualType.WrappedSignal]: /* *** */ START + 'Signal' + END, //
297
+ [VirtualType.Awaited]: /* ********* */ START + 'Awaited' + END, //
298
+ [VirtualType.Component]: /* ******* */ START + 'Component' + END, //
299
+ [VirtualType.InlineComponent]: /* * */ START + 'InlineComponent' + END, //
300
+ [VirtualType.Projection]: /* ****** */ START + 'Projection' + END, //
301
+ };
302
+ var QContainerValue;
303
+ (function (QContainerValue) {
304
+ QContainerValue["PAUSED"] = "paused";
305
+ QContainerValue["RESUMED"] = "resumed";
306
+ // these values below are used in the qwik loader as a plain text for the q:container selector
307
+ // standard dangerouslySetInnerHTML
308
+ QContainerValue["HTML"] = "html";
309
+ // textarea
310
+ QContainerValue["TEXT"] = "text";
311
+ })(QContainerValue || (QContainerValue = {}));
312
+
313
+ /** State factory of the component. */
314
+ const OnRenderProp = 'q:renderFn';
315
+ /** Component style content prefix */
316
+ const ComponentStylesPrefixContent = '⚡️';
317
+ /** `<some-element q:slot="...">` */
318
+ const QSlot = 'q:slot';
319
+ const QSlotParent = ':';
320
+ const QSlotRef = 'q:sref';
321
+ const QSlotS = 'q:s';
322
+ const QStyle = 'q:style';
323
+ const QStyleSelector = 'style[q\\:style]';
324
+ const QStyleSSelector = 'style[q\\:sstyle]';
325
+ const QStylesAllSelector = QStyleSelector + ',' + QStyleSSelector;
326
+ const QScopedStyle = 'q:sstyle';
327
+ const QCtxAttr = 'q:ctx';
328
+ const QBackRefs = 'q:brefs';
329
+ const QFuncsPrefix = 'qFuncs_';
330
+ const getQFuncs = (document, hash) => {
331
+ return document[QFuncsPrefix + hash] || [];
332
+ };
333
+ const QBaseAttr = 'q:base';
334
+ const QLocaleAttr = 'q:locale';
335
+ const QManifestHashAttr = 'q:manifest-hash';
336
+ const QInstanceAttr = 'q:instance';
337
+ const QContainerIsland = 'q:container-island';
338
+ const QContainerIslandEnd = '/' + QContainerIsland;
339
+ const QIgnore = 'q:ignore';
340
+ const QIgnoreEnd = '/' + QIgnore;
341
+ const QContainerAttr = 'q:container';
342
+ const QContainerAttrEnd = '/' + QContainerAttr;
343
+ const QTemplate = 'q:template';
344
+ // the same selector should be inside the qwik loader
345
+ // and the same selector should be inside the qwik router spa-shim and spa-init
346
+ const QContainerSelector = '[q\\:container]:not([q\\:container=' +
347
+ QContainerValue.HTML +
348
+ ']):not([q\\:container=' +
349
+ QContainerValue.TEXT +
350
+ '])';
351
+ const HTML_NS = 'http://www.w3.org/1999/xhtml';
352
+ const SVG_NS = 'http://www.w3.org/2000/svg';
353
+ const MATH_NS = 'http://www.w3.org/1998/Math/MathML';
354
+ const ResourceEvent = 'qResource';
355
+ const RenderEvent = 'qRender';
356
+ const TaskEvent = 'qTask';
357
+ const QDefaultSlot = '';
358
+ /**
359
+ * Attribute to mark that this VNode has a pointer to itself from the `qwik/json` state.
360
+ *
361
+ * As the VNode get materialized the vnode now becomes eligible for mutation. Once the vnode mutates
362
+ * the `VNode` references from the `qwik/json` may become invalid. For this reason, these references
363
+ * need to be eagerly resolved. `VNODE_REF` stores a pointer to "this" vnode. This allows the system
364
+ * to eagerly resolve these pointes as the vnodes are materialized.
365
+ */
366
+ const ELEMENT_ID = 'q:id';
367
+ const ELEMENT_KEY = 'q:key';
368
+ const ELEMENT_PROPS = 'q:props';
369
+ const ELEMENT_SEQ = 'q:seq';
370
+ const ELEMENT_SEQ_IDX = 'q:seqIdx';
371
+ const Q_PREFIX = 'q:';
372
+ /** Non serializable markers - always begins with `:` character */
373
+ const NON_SERIALIZABLE_MARKER_PREFIX = ':';
374
+ const USE_ON_LOCAL = NON_SERIALIZABLE_MARKER_PREFIX + 'on';
375
+ const USE_ON_LOCAL_SEQ_IDX = NON_SERIALIZABLE_MARKER_PREFIX + 'onIdx';
376
+ const USE_ON_LOCAL_FLAGS = NON_SERIALIZABLE_MARKER_PREFIX + 'onFlags';
377
+ // comment nodes
378
+ const FLUSH_COMMENT = 'qkssr-f';
379
+ const STREAM_BLOCK_START_COMMENT = 'qkssr-pu';
380
+ const STREAM_BLOCK_END_COMMENT = 'qkssr-po';
381
+ const Q_PROPS_SEPARATOR = ':';
382
+ const dangerouslySetInnerHTML = 'dangerouslySetInnerHTML';
383
+ const qwikInspectorAttr = 'data-qwik-inspector';
384
+
253
385
  // keep this import from core/build so the cjs build works
254
386
  const createPlatform = () => {
255
387
  return {
@@ -310,7 +442,7 @@
310
442
  */
311
443
  const toUrl = (doc, containerEl, url) => {
312
444
  const baseURI = doc.baseURI;
313
- const base = new URL(containerEl.getAttribute('q:base') ?? baseURI, baseURI);
445
+ const base = new URL(containerEl.getAttribute(QBaseAttr) ?? baseURI, baseURI);
314
446
  return new URL(url, base);
315
447
  };
316
448
  let _platform = /*#__PURE__ */ createPlatform();
@@ -389,11 +521,6 @@
389
521
  ? valueOrPromise.then(thenFn, shouldNotError)
390
522
  : thenFn(valueOrPromise);
391
523
  };
392
- const maybeThenPassError = (valueOrPromise, thenFn) => {
393
- return isPromise(valueOrPromise)
394
- ? valueOrPromise.then(thenFn)
395
- : thenFn(valueOrPromise);
396
- };
397
524
  const shouldNotError = (reason) => {
398
525
  throwErrorAndStop(reason);
399
526
  };
@@ -402,15 +529,24 @@
402
529
  setTimeout(resolve, timeout);
403
530
  });
404
531
  };
532
+ // Retries a function that throws a promise.
405
533
  function retryOnPromise(fn, retryCount = 0) {
406
- try {
407
- return fn();
408
- }
409
- catch (e) {
534
+ const retryOrThrow = (e) => {
410
535
  if (isPromise(e) && retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
411
536
  return e.then(retryOnPromise.bind(null, fn, retryCount++));
412
537
  }
413
538
  throw e;
539
+ };
540
+ try {
541
+ const result = fn();
542
+ if (isPromise(result)) {
543
+ // not awaited promise is not caught by try/catch block
544
+ return result.catch((e) => retryOrThrow(e));
545
+ }
546
+ return result;
547
+ }
548
+ catch (e) {
549
+ return retryOrThrow(e);
414
550
  }
415
551
  }
416
552
 
@@ -453,118 +589,6 @@
453
589
  VNodeDataFlag[VNodeDataFlag["SERIALIZE"] = 16] = "SERIALIZE";
454
590
  })(VNodeDataFlag || (VNodeDataFlag = {}));
455
591
 
456
- /**
457
- * A friendly name tag for a VirtualVNode.
458
- *
459
- * Theses are used to give a name to a VirtualVNode. This is useful for debugging and testing.
460
- *
461
- * The name is only added in development mode and is not included in production builds.
462
- */
463
- const DEBUG_TYPE = 'q:type';
464
- var VirtualType;
465
- (function (VirtualType) {
466
- VirtualType["Virtual"] = "V";
467
- VirtualType["Fragment"] = "F";
468
- VirtualType["WrappedSignal"] = "S";
469
- VirtualType["Awaited"] = "A";
470
- VirtualType["Component"] = "C";
471
- VirtualType["InlineComponent"] = "I";
472
- VirtualType["Projection"] = "P";
473
- })(VirtualType || (VirtualType = {}));
474
- const START = '\x1b[34m';
475
- const END = '\x1b[0m';
476
- const VirtualTypeName = {
477
- [VirtualType.Virtual]: /* ********* */ START + 'Virtual' + END, //
478
- [VirtualType.Fragment]: /* ******** */ START + 'Fragment' + END, //
479
- [VirtualType.WrappedSignal]: /* *** */ START + 'Signal' + END, //
480
- [VirtualType.Awaited]: /* ********* */ START + 'Awaited' + END, //
481
- [VirtualType.Component]: /* ******* */ START + 'Component' + END, //
482
- [VirtualType.InlineComponent]: /* * */ START + 'InlineComponent' + END, //
483
- [VirtualType.Projection]: /* ****** */ START + 'Projection' + END, //
484
- };
485
- var QContainerValue;
486
- (function (QContainerValue) {
487
- QContainerValue["PAUSED"] = "paused";
488
- QContainerValue["RESUMED"] = "resumed";
489
- // these values below are used in the qwik loader as a plain text for the q:container selector
490
- // standard dangerouslySetInnerHTML
491
- QContainerValue["HTML"] = "html";
492
- // textarea
493
- QContainerValue["TEXT"] = "text";
494
- })(QContainerValue || (QContainerValue = {}));
495
-
496
- /** State factory of the component. */
497
- const OnRenderProp = 'q:renderFn';
498
- /** Component style content prefix */
499
- const ComponentStylesPrefixContent = '⭐️';
500
- /** `<some-element q:slot="...">` */
501
- const QSlot = 'q:slot';
502
- const QSlotParent = ':';
503
- const QSlotRef = 'q:sref';
504
- const QSlotS = 'q:s';
505
- const QStyle = 'q:style';
506
- const QStyleSelector = 'style[q\\:style]';
507
- const QStyleSSelector = 'style[q\\:sstyle]';
508
- const QStylesAllSelector = QStyleSelector + ',' + QStyleSSelector;
509
- const QScopedStyle = 'q:sstyle';
510
- const QCtxAttr = 'q:ctx';
511
- const QSubscribers = 'q:subs';
512
- const QFuncsPrefix = 'qFuncs_';
513
- const getQFuncs = (document, hash) => {
514
- return document[QFuncsPrefix + hash] || [];
515
- };
516
- const QBaseAttr = 'q:base';
517
- const QLocaleAttr = 'q:locale';
518
- const QManifestHashAttr = 'q:manifest-hash';
519
- const QInstanceAttr = 'q:instance';
520
- const QContainerIsland = 'q:container-island';
521
- const QContainerIslandEnd = '/' + QContainerIsland;
522
- const QIgnore = 'q:ignore';
523
- const QIgnoreEnd = '/' + QIgnore;
524
- const QContainerAttr = 'q:container';
525
- const QContainerAttrEnd = '/' + QContainerAttr;
526
- const QTemplate = 'q:template';
527
- // the same selector should be inside the qwik loader
528
- // and the same selector should be inside the qwik router spa-shim and spa-init
529
- const QContainerSelector = '[q\\:container]:not([q\\:container=' +
530
- QContainerValue.HTML +
531
- ']):not([q\\:container=' +
532
- QContainerValue.TEXT +
533
- '])';
534
- const HTML_NS = 'http://www.w3.org/1999/xhtml';
535
- const SVG_NS = 'http://www.w3.org/2000/svg';
536
- const MATH_NS = 'http://www.w3.org/1998/Math/MathML';
537
- const ResourceEvent = 'qResource';
538
- const RenderEvent = 'qRender';
539
- const TaskEvent = 'qTask';
540
- const QDefaultSlot = '';
541
- /**
542
- * Attribute to mark that this VNode has a pointer to itself from the `qwik/json` state.
543
- *
544
- * As the VNode get materialized the vnode now becomes eligible for mutation. Once the vnode mutates
545
- * the `VNode` references from the `qwik/json` may become invalid. For this reason, these references
546
- * need to be eagerly resolved. `VNODE_REF` stores a pointer to "this" vnode. This allows the system
547
- * to eagerly resolve these pointes as the vnodes are materialized.
548
- */
549
- const ELEMENT_ID = 'q:id';
550
- const ELEMENT_KEY = 'q:key';
551
- const ELEMENT_PROPS = 'q:props';
552
- const ELEMENT_SEQ = 'q:seq';
553
- const ELEMENT_SEQ_IDX = 'q:seqIdx';
554
- const Q_PREFIX = 'q:';
555
- /** Non serializable markers - always begins with `:` character */
556
- const NON_SERIALIZABLE_MARKER_PREFIX = ':';
557
- const USE_ON_LOCAL = NON_SERIALIZABLE_MARKER_PREFIX + 'on';
558
- const USE_ON_LOCAL_SEQ_IDX = NON_SERIALIZABLE_MARKER_PREFIX + 'onIdx';
559
- const USE_ON_LOCAL_FLAGS = NON_SERIALIZABLE_MARKER_PREFIX + 'onFlags';
560
- // comment nodes
561
- const FLUSH_COMMENT = 'qkssr-f';
562
- const STREAM_BLOCK_START_COMMENT = 'qkssr-pu';
563
- const STREAM_BLOCK_END_COMMENT = 'qkssr-po';
564
- const Q_PROPS_SEPARATOR = ':';
565
- const dangerouslySetInnerHTML = 'dangerouslySetInnerHTML';
566
- const qwikInspectorAttr = 'data-qwik-inspector';
567
-
568
592
  let _locale = undefined;
569
593
  /**
570
594
  * Retrieve the current locale.
@@ -614,7 +638,7 @@
614
638
  _locale = locale;
615
639
  }
616
640
 
617
- const isQrl$1 = (value) => {
641
+ const isQrl = (value) => {
618
642
  return typeof value === 'function' && typeof value.getSymbol === 'function';
619
643
  };
620
644
 
@@ -686,21 +710,21 @@
686
710
  // Emit event
687
711
  announcedQRL.add(symbol);
688
712
  emitEvent('qprefetch', {
689
- symbols: [getSymbolHash(symbol)],
713
+ symbols: [symbol],
690
714
  bundles: chunk && [chunk],
691
715
  });
692
716
  }
693
717
  // Unwrap subscribers
694
- return createQRL(chunk, symbol, null, symbolFn, null, lexicalScopeCapture, null);
718
+ return createQRL(chunk, symbol, null, symbolFn, null, lexicalScopeCapture);
695
719
  };
696
720
  /** @internal */
697
721
  const inlinedQrl = (symbol, symbolName, lexicalScopeCapture = EMPTY_ARRAY) => {
698
722
  // Unwrap subscribers
699
- return createQRL(null, symbolName, symbol, null, null, lexicalScopeCapture, null);
723
+ return createQRL(null, symbolName, symbol, null, null, lexicalScopeCapture);
700
724
  };
701
725
  /** @internal */
702
726
  const _noopQrl = (symbolName, lexicalScopeCapture = EMPTY_ARRAY) => {
703
- return createQRL(null, symbolName, null, null, null, lexicalScopeCapture, null);
727
+ return createQRL(null, symbolName, null, null, null, lexicalScopeCapture);
704
728
  };
705
729
  /** @internal */
706
730
  const _noopQrlDEV = (symbolName, opts, lexicalScopeCapture = EMPTY_ARRAY) => {
@@ -729,6 +753,80 @@
729
753
  return symbol;
730
754
  };
731
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
+
732
830
  /**
733
831
  * Allows to project the children of the current component. <Slot/> can only be used within the
734
832
  * context of a component defined with `component$`.
@@ -853,6 +951,47 @@
853
951
  return key.startsWith('preventdefault:');
854
952
  }
855
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
+
856
995
  /** CSS properties which accept numbers but are not in units of "px". */
857
996
  const unitlessNumbers = new Set([
858
997
  'animationIterationCount',
@@ -1011,36 +1150,6 @@
1011
1150
  assertQrl(qStyles);
1012
1151
  return `${hashCode(qStyles.$hash$)}-${index}`;
1013
1152
  };
1014
- const styleContent = (styleId) => {
1015
- return ComponentStylesPrefixContent + styleId;
1016
- };
1017
-
1018
- function hasClassAttr(props) {
1019
- for (const key in props) {
1020
- if (Object.prototype.hasOwnProperty.call(props, key) && isClassAttr(key)) {
1021
- return true;
1022
- }
1023
- }
1024
- return false;
1025
- }
1026
- function isClassAttr(key) {
1027
- return key === 'class' || key === 'className';
1028
- }
1029
- function convertScopedStyleIdsToArray(scopedStyleIds) {
1030
- return scopedStyleIds?.split(' ') ?? null;
1031
- }
1032
- function convertStyleIdsToString(scopedStyleIds) {
1033
- return Array.from(scopedStyleIds).join(' ');
1034
- }
1035
- const addComponentStylePrefix = (styleId) => {
1036
- if (styleId) {
1037
- let idx = 0;
1038
- do {
1039
- styleId = styleId.substring(0, idx) + styleContent(styleId.substring(idx));
1040
- } while ((idx = styleId.indexOf(' ', idx) + 1) !== 0);
1041
- }
1042
- return styleId || null;
1043
- };
1044
1153
 
1045
1154
  const STORE_TARGET = Symbol('store.target');
1046
1155
  const STORE_HANDLER = Symbol('store.handler');
@@ -1185,8 +1294,12 @@
1185
1294
  return Reflect.ownKeys(target);
1186
1295
  }
1187
1296
  getOwnPropertyDescriptor(target, prop) {
1297
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
1188
1298
  if (Array.isArray(target) || typeof prop === 'symbol') {
1189
- return Object.getOwnPropertyDescriptor(target, prop);
1299
+ return descriptor;
1300
+ }
1301
+ if (descriptor && !descriptor.configurable) {
1302
+ return descriptor;
1190
1303
  }
1191
1304
  return {
1192
1305
  enumerable: true,
@@ -1194,39 +1307,185 @@
1194
1307
  };
1195
1308
  }
1196
1309
  }
1197
- function addEffect(target, prop, store, effectSubscriber) {
1198
- const effectsMap = (store.$effects$ ||= {});
1199
- const effects = (Object.prototype.hasOwnProperty.call(effectsMap, prop) && effectsMap[prop]) ||
1200
- (effectsMap[prop] = []);
1310
+ function addEffect(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
+ }
1201
1317
  // Let's make sure that we have a reference to this effect.
1202
1318
  // Adding reference is essentially adding a subscription, so if the signal
1203
1319
  // changes we know who to notify.
1204
- ensureContainsEffect(effects, effectSubscriber);
1320
+ ensureContainsSubscription(effects, effectSubscription);
1205
1321
  // But when effect is scheduled in needs to be able to know which signals
1206
1322
  // to unsubscribe from. So we need to store the reference from the effect back
1207
1323
  // to this signal.
1208
- ensureContains(effectSubscriber, target);
1209
- // We need to add the subscriber to the effect so that we can clean it up later
1210
- ensureEffectContainsSubscriber(effectSubscriber[EffectSubscriptionsProp.EFFECT], target, store.$container$);
1324
+ ensureContainsBackRef(effectSubscription, target);
1325
+ addQrlToSerializationCtx(effectSubscription, store.$container$);
1211
1326
  }
1212
1327
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
1213
1328
  target[prop] = value;
1214
1329
  triggerEffects(currentStore.$container$, currentStore, getEffects(target, prop, currentStore.$effects$));
1215
1330
  }
1216
1331
  function getEffects(target, prop, storeEffects) {
1217
- let effectsToTrigger = storeEffects
1218
- ? Array.isArray(target)
1219
- ? Object.values(storeEffects).flatMap((effects) => effects)
1220
- : storeEffects[prop]
1221
- : null;
1222
- 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_ARRAY_PROP);
1223
1347
  if (storeArrayValue) {
1224
- effectsToTrigger ||= [];
1225
- effectsToTrigger.push(...storeArrayValue);
1348
+ effectsToTrigger ||= new Set();
1349
+ for (const effect of storeArrayValue) {
1350
+ effectsToTrigger.add(effect);
1351
+ }
1352
+ }
1353
+ return effectsToTrigger || null;
1354
+ }
1355
+
1356
+ /**
1357
+ * Special value used to mark that a given signal needs to be computed. This is essentially a
1358
+ * "marked as dirty" flag.
1359
+ */
1360
+ const NEEDS_COMPUTATION = Symbol('invalid');
1361
+ /** @internal */
1362
+ const _EFFECT_BACK_REF = Symbol('backRef');
1363
+
1364
+ /** Class for back reference to the EffectSubscription */
1365
+ class BackRef {
1366
+ [_EFFECT_BACK_REF] = null;
1367
+ }
1368
+ function clearAllEffects(container, consumer) {
1369
+ if (vnode_isVNode(consumer) && vnode_isElementVNode(consumer)) {
1370
+ ensureMaterialized(consumer);
1371
+ }
1372
+ const effects = consumer[_EFFECT_BACK_REF];
1373
+ if (!effects) {
1374
+ return;
1375
+ }
1376
+ for (const [, effect] of effects) {
1377
+ const backRefs = effect[EffectSubscriptionProp.BACK_REF];
1378
+ if (!backRefs) {
1379
+ return;
1380
+ }
1381
+ for (const producer of backRefs) {
1382
+ if (producer instanceof Signal) {
1383
+ clearSignal(container, producer, effect);
1384
+ }
1385
+ else if (container.$storeProxyMap$.has(producer)) {
1386
+ const target = container.$storeProxyMap$.get(producer);
1387
+ const storeHandler = getStoreHandler(target);
1388
+ clearStore(storeHandler, effect);
1389
+ }
1390
+ }
1391
+ }
1392
+ }
1393
+ function clearSignal(container, producer, effect) {
1394
+ const effects = producer.$effects$;
1395
+ if (effects) {
1396
+ effects.delete(effect);
1397
+ }
1398
+ if (producer instanceof WrappedSignal) {
1399
+ producer.$hostElement$ = null;
1400
+ clearAllEffects(container, producer);
1401
+ }
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);
1408
+ }
1226
1409
  }
1227
- return effectsToTrigger;
1228
1410
  }
1229
1411
 
1412
+ // <docs markdown="../../readme.md#implicit$FirstArg">
1413
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
1414
+ // (edit ../../readme.md#implicit$FirstArg instead and run `pnpm docs.sync`)
1415
+ /**
1416
+ * Create a `____$(...)` convenience method from `___(...)`.
1417
+ *
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 `$`.
1421
+ *
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.
1424
+ *
1425
+ * For example, these function calls are equivalent:
1426
+ *
1427
+ * - `component$(() => {...})` is same as `component($(() => {...}))`
1428
+ *
1429
+ * ```tsx
1430
+ * export function myApi(callback: QRL<() => void>): void {
1431
+ * // ...
1432
+ * }
1433
+ *
1434
+ * export const myApi$ = implicit$FirstArg(myApi);
1435
+ * // type of myApi$: (callback: () => void): void
1436
+ *
1437
+ * // can be used as:
1438
+ * myApi$(() => console.log('callback'));
1439
+ *
1440
+ * // will be transpiled to:
1441
+ * // FILE: <current file>
1442
+ * myApi(qrl('./chunk-abc.js', 'callback'));
1443
+ *
1444
+ * // FILE: chunk-abc.js
1445
+ * export const callback = () => console.log('callback');
1446
+ * ```
1447
+ *
1448
+ * @param fn - A function that should have its first argument automatically `$`.
1449
+ * @public
1450
+ */
1451
+ // </docs>
1452
+ const implicit$FirstArg = (fn) => {
1453
+ return function (first, ...rest) {
1454
+ return fn.call(null, dollar(first), ...rest);
1455
+ };
1456
+ };
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
+
1230
1489
  /**
1231
1490
  * @internal
1232
1491
  * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
@@ -1262,272 +1521,66 @@
1262
1521
  };
1263
1522
  };
1264
1523
 
1265
- class Subscriber {
1266
- $effectDependencies$ = null;
1267
- }
1268
- function isSubscriber(value) {
1269
- return value instanceof Subscriber || value instanceof WrappedSignal;
1270
- }
1271
- function clearVNodeEffectDependencies(container, value) {
1272
- if (vnode_isElementVNode(value)) {
1273
- ensureMaterialized(value);
1274
- }
1275
- const effects = vnode_getProp(value, QSubscribers, container.$getObjectById$);
1276
- if (!effects) {
1277
- return;
1278
- }
1279
- for (let i = effects.length - 1; i >= 0; i--) {
1280
- const subscriber = effects[i];
1281
- clearEffects(subscriber, value, effects, i, container);
1282
- }
1283
- if (effects.length === 0) {
1284
- vnode_setProp(value, QSubscribers, null);
1285
- }
1286
- }
1287
- function clearSubscriberEffectDependencies(container, value) {
1288
- if (value.$effectDependencies$) {
1289
- for (let i = value.$effectDependencies$.length - 1; i >= 0; i--) {
1290
- const subscriber = value.$effectDependencies$[i];
1291
- clearEffects(subscriber, value, value.$effectDependencies$, i, container);
1524
+ function getSubscriber(effect, prop, data) {
1525
+ if (!effect[_EFFECT_BACK_REF]) {
1526
+ if (build.isServer && isSsrNode(effect)) {
1527
+ effect.setProp(QBackRefs, new Map());
1292
1528
  }
1293
- if (value.$effectDependencies$.length === 0) {
1294
- value.$effectDependencies$ = null;
1529
+ else {
1530
+ effect[_EFFECT_BACK_REF] = new Map();
1295
1531
  }
1296
1532
  }
1297
- }
1298
- function clearEffects(subscriber, value, effectArray, indexToRemove, container) {
1299
- let subscriptionRemoved = false;
1300
- const seenSet = new Set();
1301
- if (subscriber instanceof WrappedSignal) {
1302
- subscriptionRemoved = clearSignalEffects(subscriber, value, seenSet);
1303
- }
1304
- else if (container.$storeProxyMap$.has(subscriber)) {
1305
- const store = container.$storeProxyMap$.get(subscriber);
1306
- const handler = getStoreHandler(store);
1307
- subscriptionRemoved = clearStoreEffects(handler, value);
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);
1308
1538
  }
1309
- if (subscriptionRemoved) {
1310
- effectArray.splice(indexToRemove, 1);
1539
+ if (data) {
1540
+ sub[EffectSubscriptionProp.DATA] = data;
1311
1541
  }
1542
+ return sub;
1312
1543
  }
1313
- function clearSignalEffects(subscriber, value, seenSet) {
1314
- const effectSubscriptions = subscriber.$effects$;
1315
- let subscriptionRemoved = false;
1316
- if (effectSubscriptions) {
1317
- for (let i = effectSubscriptions.length - 1; i >= 0; i--) {
1318
- const effect = effectSubscriptions[i];
1319
- if (effect[EffectSubscriptionsProp.EFFECT] === value) {
1320
- effectSubscriptions.splice(i, 1);
1321
- subscriptionRemoved = true;
1322
- }
1323
- }
1324
- }
1325
- if (subscriber instanceof WrappedSignal) {
1326
- const hostElement = subscriber.$hostElement$;
1327
- if (hostElement && hostElement === value) {
1328
- subscriber.$hostElement$ = null;
1329
- }
1330
- // clear the effects of the arguments
1331
- const args = subscriber.$args$;
1332
- if (args) {
1333
- clearArgsEffects(args, subscriber, seenSet);
1334
- }
1335
- }
1336
- return subscriptionRemoved;
1337
- }
1338
- function clearStoreEffects(storeHandler, value) {
1339
- const effectSubscriptions = storeHandler.$effects$;
1340
- if (!effectSubscriptions) {
1341
- return false;
1342
- }
1343
- let subscriptionRemoved = false;
1344
- for (const key in effectSubscriptions) {
1345
- const effects = effectSubscriptions[key];
1346
- for (let i = effects.length - 1; i >= 0; i--) {
1347
- const effect = effects[i];
1348
- if (effect[EffectSubscriptionsProp.EFFECT] === value) {
1349
- effects.splice(i, 1);
1350
- subscriptionRemoved = true;
1351
- }
1352
- }
1353
- if (effects.length === 0) {
1354
- delete effectSubscriptions[key];
1355
- }
1356
- }
1357
- return subscriptionRemoved;
1358
- }
1359
- function clearArgsEffects(args, subscriber, seenSet) {
1360
- for (let i = args.length - 1; i >= 0; i--) {
1361
- const arg = args[i];
1362
- clearArgEffect(arg, subscriber, seenSet);
1363
- }
1364
- }
1365
- function clearArgEffect(arg, subscriber, seenSet) {
1366
- if (seenSet.has(arg)) {
1367
- return;
1368
- }
1369
- seenSet.add(arg);
1370
- if (isSignal(arg)) {
1371
- clearSignalEffects(arg, subscriber, seenSet);
1372
- }
1373
- else if (typeof arg === 'object' && arg !== null) {
1374
- if (isStore(arg)) {
1375
- clearStoreEffects(getStoreHandler(arg), subscriber);
1376
- }
1377
- else {
1378
- for (const key in arg) {
1379
- clearArgEffect(arg[key], subscriber, seenSet);
1380
- }
1381
- }
1382
- }
1383
- else if (Array.isArray(arg)) {
1384
- clearArgsEffects(arg, subscriber, seenSet);
1385
- }
1386
- else ;
1544
+ function isSsrNode(value) {
1545
+ return '__brand__' in value && 'currentComponentNode' in value;
1387
1546
  }
1388
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 = {}));
1389
1555
  /** @internal */
1390
- const useResourceQrl = (qrl, opts) => {
1391
- const { val, set, i, iCtx } = useSequentialScope();
1392
- if (val != null) {
1393
- return val;
1556
+ const useTaskQrl = (qrl) => {
1557
+ const { val, set, iCtx, i } = useSequentialScope();
1558
+ if (val) {
1559
+ return;
1394
1560
  }
1395
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);
1396
1568
  const container = iCtx.$container$;
1397
- const resource = createResourceReturn(container, opts);
1398
- const el = iCtx.$hostElement$;
1399
- const task = new Task(TaskFlags.DIRTY | TaskFlags.RESOURCE, i, el, qrl, resource, null);
1400
- runResource(task, container, iCtx.$hostElement$);
1401
- set(resource);
1402
- return resource;
1403
- };
1404
- // <docs markdown="../readme.md#useResource">
1405
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
1406
- // (edit ../readme.md#useResource instead and run `pnpm docs.sync`)
1407
- /**
1408
- * This method works like an async memoized function that runs whenever some tracked value changes
1409
- * and returns some data.
1410
- *
1411
- * `useResource` however returns immediate a `ResourceReturn` object that contains the data and a
1412
- * state that indicates if the data is available or not.
1413
- *
1414
- * The status can be one of the following:
1415
- *
1416
- * - `pending` - the data is not yet available.
1417
- * - `resolved` - the data is available.
1418
- * - `rejected` - the data is not available due to an error or timeout.
1419
- *
1420
- * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
1421
- * re-throw it (or a new Error), the resource status will never be `rejected`.
1422
- *
1423
- * ### Example
1424
- *
1425
- * Example showing how `useResource` to perform a fetch to request the weather, whenever the input
1426
- * city name changes.
1427
- *
1428
- * ```tsx
1429
- * const Cmp = component$(() => {
1430
- * const cityS = useSignal('');
1431
- *
1432
- * const weatherResource = useResource$(async ({ track, cleanup }) => {
1433
- * const cityName = track(cityS);
1434
- * const abortController = new AbortController();
1435
- * cleanup(() => abortController.abort('cleanup'));
1436
- * const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
1437
- * signal: abortController.signal,
1438
- * });
1439
- * const data = await res.json();
1440
- * return data as { temp: number };
1441
- * });
1442
- *
1443
- * return (
1444
- * <div>
1445
- * <input name="city" bind:value={cityS} />
1446
- * <Resource
1447
- * value={weatherResource}
1448
- * onResolved={(weather) => {
1449
- * return <div>Temperature: {weather.temp}</div>;
1450
- * }}
1451
- * />
1452
- * </div>
1453
- * );
1454
- * });
1455
- * ```
1456
- *
1457
- * @public
1458
- * @see Resource
1459
- * @see ResourceReturn
1460
- */
1461
- // </docs>
1462
- const Resource = (props) => {
1463
- // Resource path
1464
- return _jsxSorted(Fragment, null, null, getResourceValueAsPromise(props), 0, null);
1465
- };
1466
- function getResourceValueAsPromise(props) {
1467
- const resource = props.value;
1468
- if (isResourceReturn(resource)) {
1469
- const isBrowser = !isServerPlatform();
1470
- if (isBrowser) {
1471
- // create a subscription for the resource._state changes
1472
- const state = resource._state;
1473
- if (state === 'pending' && props.onPending) {
1474
- return Promise.resolve().then(useBindInvokeContext(props.onPending));
1475
- }
1476
- else if (state === 'rejected' && props.onRejected) {
1477
- return Promise.resolve(resource._error).then(useBindInvokeContext(props.onRejected));
1478
- }
1479
- else {
1480
- const resolvedValue = untrack(() => resource._resolved);
1481
- if (resolvedValue !== undefined) {
1482
- // resolved, pending without onPending prop or rejected without onRejected prop
1483
- return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved));
1484
- }
1485
- }
1486
- }
1487
- return resource.value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1488
- }
1489
- else if (isPromise(resource)) {
1490
- return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1491
- }
1492
- else if (isSignal(resource)) {
1493
- return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1569
+ const promise = container.$scheduler$(ChoreType.TASK, task);
1570
+ if (isPromise(promise)) {
1571
+ // TODO: should we handle this differently?
1572
+ promise.catch(() => { });
1494
1573
  }
1495
- else {
1496
- return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1497
- }
1498
- }
1499
- const _createResourceReturn = (opts) => {
1500
- const resource = {
1501
- __brand: 'resource',
1502
- value: undefined,
1503
- loading: isServerPlatform() ? false : true,
1504
- _resolved: undefined,
1505
- _error: undefined,
1506
- _state: 'pending',
1507
- _timeout: opts?.timeout ?? -1,
1508
- _cache: 0,
1509
- };
1510
- return resource;
1511
1574
  };
1512
- const createResourceReturn = (container, opts, initialPromise) => {
1513
- const result = _createResourceReturn(opts);
1514
- result.value = initialPromise;
1515
- return createStore(container, result, StoreFlags.RECURSIVE);
1516
- };
1517
- const isResourceReturn = (obj) => {
1518
- return isObject(obj) && (getStoreTarget(obj) || obj).__brand === 'resource';
1519
- };
1520
- const runResource = (task, container, host) => {
1575
+ const runTask = (task, container, host) => {
1521
1576
  task.$flags$ &= ~TaskFlags.DIRTY;
1522
1577
  cleanupTask(task);
1523
- const iCtx = newInvokeContext(container.$locale$, host, undefined, ResourceEvent);
1578
+ const iCtx = newInvokeContext(container.$locale$, host, undefined, TaskEvent);
1524
1579
  iCtx.$container$ = container;
1525
- const taskFn = task.$qrl$.getFn(iCtx, () => clearSubscriberEffectDependencies(container, task));
1526
- const resource = task.$state$;
1527
- assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
1580
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
1528
1581
  const track = (obj, prop) => {
1529
1582
  const ctx = newInvokeContext();
1530
- ctx.$effectSubscriber$ = [task, EffectProperty.COMPONENT];
1583
+ ctx.$effectSubscriber$ = getSubscriber(task, EffectProperty.COMPONENT);
1531
1584
  ctx.$container$ = container;
1532
1585
  return invoke(ctx, () => {
1533
1586
  if (isFunction(obj)) {
@@ -1540,115 +1593,85 @@
1540
1593
  return obj.value;
1541
1594
  }
1542
1595
  else {
1543
- return obj;
1596
+ throw qError(QError.trackObjectWithoutProp);
1544
1597
  }
1545
1598
  });
1546
1599
  };
1547
1600
  const handleError = (reason) => container.handleError(reason, host);
1548
- const cleanups = [];
1549
- task.$destroy$ = noSerialize(() => {
1550
- cleanups.forEach((fn) => {
1551
- try {
1552
- fn();
1553
- }
1554
- catch (err) {
1555
- handleError(err);
1556
- }
1557
- });
1558
- done = true;
1559
- });
1560
- const resourceTarget = unwrapStore(resource);
1561
- const opts = {
1562
- track,
1563
- cleanup(fn) {
1564
- if (typeof fn === 'function') {
1565
- cleanups.push(fn);
1566
- }
1567
- },
1568
- cache(policy) {
1569
- let milliseconds = 0;
1570
- if (policy === 'immutable') {
1571
- milliseconds = Infinity;
1572
- }
1573
- else {
1574
- milliseconds = policy;
1575
- }
1576
- resource._cache = milliseconds;
1577
- },
1578
- previous: resourceTarget._resolved,
1579
- };
1580
- let resolve;
1581
- let reject;
1582
- let done = false;
1583
- const setState = (resolved, value) => {
1584
- if (!done) {
1585
- done = true;
1586
- if (resolved) {
1587
- done = true;
1588
- resource.loading = false;
1589
- resource._state = 'resolved';
1590
- resource._resolved = value;
1591
- resource._error = undefined;
1592
- resolve(value);
1593
- }
1594
- else {
1595
- done = true;
1596
- resource.loading = false;
1597
- resource._state = 'rejected';
1598
- resource._error = value;
1599
- reject(value);
1601
+ let cleanupFns = null;
1602
+ const cleanup = (fn) => {
1603
+ if (typeof fn == 'function') {
1604
+ if (!cleanupFns) {
1605
+ cleanupFns = [];
1606
+ task.$destroy$ = noSerialize(() => {
1607
+ task.$destroy$ = null;
1608
+ cleanupFns.forEach((fn) => {
1609
+ try {
1610
+ fn();
1611
+ }
1612
+ catch (err) {
1613
+ handleError(err);
1614
+ }
1615
+ });
1616
+ });
1600
1617
  }
1601
- return true;
1618
+ cleanupFns.push(fn);
1602
1619
  }
1603
- return false;
1604
1620
  };
1605
- /**
1606
- * Add cleanup to resolve the resource if we are trying to run the same resource again while the
1607
- * previous one is not resolved yet. The next `runResource` run will call this cleanup
1608
- */
1609
- cleanups.push(() => {
1610
- if (untrack(() => resource.loading) === true) {
1611
- const value = untrack(() => resource._resolved);
1612
- setState(true, value);
1613
- }
1614
- });
1615
- // Execute mutation inside empty invocation
1616
- invoke(iCtx, () => {
1617
- // console.log('RESOURCE.pending: ');
1618
- resource._state = 'pending';
1619
- resource.loading = !isServerPlatform();
1620
- const promise = (resource.value = new Promise((r, re) => {
1621
- resolve = r;
1622
- reject = re;
1623
- }));
1624
- promise.catch(ignoreErrorToPreventNodeFromCrashing);
1625
- });
1626
- const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
1627
- setState(true, value);
1628
- }, (err) => {
1621
+ const taskApi = { track, cleanup };
1622
+ const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
1623
+ // If a Promise is thrown, that means we need to re-run the task.
1629
1624
  if (isPromise(err)) {
1630
- return err.then(() => runResource(task, container, host));
1625
+ return err.then(() => runTask(task, container, host));
1631
1626
  }
1632
1627
  else {
1633
- setState(false, err);
1628
+ throw err;
1634
1629
  }
1635
1630
  });
1636
- const timeout = resourceTarget._timeout;
1637
- if (timeout > 0) {
1638
- return Promise.race([
1639
- promise,
1640
- delay(timeout).then(() => {
1641
- if (setState(false, new Error('timeout'))) {
1642
- cleanupTask(task);
1643
- }
1644
- }),
1645
- ]);
1631
+ return result;
1632
+ };
1633
+ const cleanupTask = (task) => {
1634
+ const destroy = task.$destroy$;
1635
+ if (destroy) {
1636
+ task.$destroy$ = null;
1637
+ try {
1638
+ destroy();
1639
+ }
1640
+ catch (err) {
1641
+ logError(err);
1642
+ }
1646
1643
  }
1647
- return promise;
1648
1644
  };
1649
- const ignoreErrorToPreventNodeFromCrashing = (err) => {
1650
- // ignore error to prevent node from crashing
1651
- // node will crash in promise is rejected and no one is listening to the rejection.
1645
+ class Task extends BackRef {
1646
+ $flags$;
1647
+ $index$;
1648
+ $el$;
1649
+ $qrl$;
1650
+ $state$;
1651
+ $destroy$;
1652
+ constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
1653
+ super();
1654
+ this.$flags$ = $flags$;
1655
+ this.$index$ = $index$;
1656
+ this.$el$ = $el$;
1657
+ this.$qrl$ = $qrl$;
1658
+ this.$state$ = $state$;
1659
+ this.$destroy$ = $destroy$;
1660
+ }
1661
+ }
1662
+ const isTask = (value) => {
1663
+ return value instanceof Task;
1664
+ };
1665
+ /**
1666
+ * Used internally as a qrl event handler to schedule a task.
1667
+ *
1668
+ * @internal
1669
+ */
1670
+ const scheduleTask = (_event, element) => {
1671
+ const [task] = useLexicalScope();
1672
+ const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK;
1673
+ const container = getDomContainer(element);
1674
+ container.$scheduler$(type, task);
1652
1675
  };
1653
1676
 
1654
1677
  /** @file Public types for the client deserialization */
@@ -1726,22 +1749,67 @@
1726
1749
  VirtualVNodeProps[VirtualVNodeProps["PROPS_OFFSET"] = 6] = "PROPS_OFFSET";
1727
1750
  })(VirtualVNodeProps || (VirtualVNodeProps = {}));
1728
1751
 
1729
- const isForeignObjectElement = (elementName) => elementName.toLowerCase() === 'foreignobject';
1752
+ const mapApp_findIndx = (elementVNode, key, start) => {
1753
+ assertTrue(start % 2 === 0, 'Expecting even number.');
1754
+ let bottom = start >> 1;
1755
+ let top = (elementVNode.length - 2) >> 1;
1756
+ while (bottom <= top) {
1757
+ const mid = bottom + ((top - bottom) >> 1);
1758
+ const midKey = elementVNode[mid << 1];
1759
+ if (midKey === key) {
1760
+ return mid << 1;
1761
+ }
1762
+ if (midKey < key) {
1763
+ bottom = mid + 1;
1764
+ }
1765
+ else {
1766
+ top = mid - 1;
1767
+ }
1768
+ }
1769
+ return (bottom << 1) ^ -1;
1770
+ };
1771
+ const mapArray_set = (elementVNode, key, value, start) => {
1772
+ const indx = mapApp_findIndx(elementVNode, key, start);
1773
+ if (indx >= 0) {
1774
+ if (value == null) {
1775
+ elementVNode.splice(indx, 2);
1776
+ }
1777
+ else {
1778
+ elementVNode[indx + 1] = value;
1779
+ }
1780
+ }
1781
+ else if (value != null) {
1782
+ elementVNode.splice(indx ^ -1, 0, key, value);
1783
+ }
1784
+ };
1785
+ const mapArray_get = (elementVNode, key, start) => {
1786
+ const indx = mapApp_findIndx(elementVNode, key, start);
1787
+ if (indx >= 0) {
1788
+ return elementVNode[indx + 1];
1789
+ }
1790
+ else {
1791
+ return null;
1792
+ }
1793
+ };
1794
+
1795
+ const isForeignObjectElement = (elementName) => {
1796
+ return build.isDev ? elementName.toLowerCase() === 'foreignobject' : elementName === 'foreignObject';
1797
+ };
1730
1798
  const isSvgElement = (elementName) => elementName === 'svg' || isForeignObjectElement(elementName);
1731
1799
  const isMathElement = (elementName) => elementName === 'math';
1732
1800
  const vnode_isDefaultNamespace = (vnode) => {
1733
1801
  const flags = vnode[VNodeProps.flags];
1734
1802
  return (flags & VNodeFlags.NAMESPACE_MASK) === 0;
1735
1803
  };
1736
- const vnode_getElementNamespaceFlags = (elementName) => {
1737
- if (isSvgElement(elementName)) {
1738
- return VNodeFlags.NS_svg;
1739
- }
1740
- else if (isMathElement(elementName)) {
1741
- return VNodeFlags.NS_math;
1742
- }
1743
- else {
1744
- return VNodeFlags.NS_html;
1804
+ const vnode_getElementNamespaceFlags = (element) => {
1805
+ const namespace = fastNamespaceURI(element);
1806
+ switch (namespace) {
1807
+ case SVG_NS:
1808
+ return VNodeFlags.NS_svg;
1809
+ case MATH_NS:
1810
+ return VNodeFlags.NS_math;
1811
+ default:
1812
+ return VNodeFlags.NS_html;
1745
1813
  }
1746
1814
  };
1747
1815
  function vnode_getDomChildrenWithCorrectNamespacesToInsert(journal, domParentVNode, newChild) {
@@ -2095,19 +2163,21 @@
2095
2163
  * @returns
2096
2164
  */
2097
2165
  const executeComponent = (container, renderHost, subscriptionHost, componentQRL, props) => {
2098
- const iCtx = newInvokeContext(container.$locale$, subscriptionHost, undefined, RenderEvent);
2099
- iCtx.$effectSubscriber$ = [subscriptionHost, EffectProperty.COMPONENT];
2100
- iCtx.$container$ = container;
2166
+ const iCtx = newInvokeContext(container.$locale$, subscriptionHost || undefined, undefined, RenderEvent);
2167
+ if (subscriptionHost) {
2168
+ iCtx.$effectSubscriber$ = getSubscriber(subscriptionHost, EffectProperty.COMPONENT);
2169
+ iCtx.$container$ = container;
2170
+ }
2101
2171
  let componentFn;
2102
2172
  container.ensureProjectionResolved(renderHost);
2103
2173
  let isInlineComponent = false;
2104
2174
  if (componentQRL === null) {
2105
- componentQRL = componentQRL || container.getHostProp(renderHost, OnRenderProp);
2175
+ componentQRL = container.getHostProp(renderHost, OnRenderProp);
2106
2176
  assertDefined(componentQRL, 'No Component found at this location');
2107
2177
  }
2108
- if (isQrl(componentQRL)) {
2178
+ if (isQrl$1(componentQRL)) {
2109
2179
  props = props || container.getHostProp(renderHost, ELEMENT_PROPS) || EMPTY_OBJ;
2110
- if (props && props.children) {
2180
+ if (props.children) {
2111
2181
  delete props.children;
2112
2182
  }
2113
2183
  componentFn = componentQRL.getFn(iCtx);
@@ -2125,18 +2195,16 @@
2125
2195
  if (!isInlineComponent) {
2126
2196
  container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
2127
2197
  container.setHostProp(renderHost, USE_ON_LOCAL_SEQ_IDX, null);
2128
- if (container.getHostProp(renderHost, ELEMENT_PROPS) !== props) {
2129
- container.setHostProp(renderHost, ELEMENT_PROPS, props);
2130
- }
2198
+ container.setHostProp(renderHost, ELEMENT_PROPS, props);
2131
2199
  }
2132
2200
  if (vnode_isVNode(renderHost)) {
2133
- clearVNodeEffectDependencies(container, renderHost);
2201
+ clearAllEffects(container, renderHost);
2134
2202
  }
2135
2203
  return componentFn(props);
2136
2204
  }, (jsx) => {
2137
2205
  const useOnEvents = container.getHostProp(renderHost, USE_ON_LOCAL);
2138
2206
  if (useOnEvents) {
2139
- return maybeThen(addUseOnEvents(jsx, useOnEvents), () => jsx);
2207
+ return addUseOnEvents(jsx, useOnEvents);
2140
2208
  }
2141
2209
  return jsx;
2142
2210
  }, (err) => {
@@ -2162,6 +2230,7 @@
2162
2230
  */
2163
2231
  function addUseOnEvents(jsx, useOnEvents) {
2164
2232
  const jsxElement = findFirstStringJSX(jsx);
2233
+ let jsxResult = jsx;
2165
2234
  return maybeThen(jsxElement, (jsxElement) => {
2166
2235
  let isInvisibleComponent = false;
2167
2236
  if (!jsxElement) {
@@ -2180,13 +2249,15 @@
2180
2249
  if (Object.prototype.hasOwnProperty.call(useOnEvents, key)) {
2181
2250
  if (isInvisibleComponent) {
2182
2251
  if (key === 'onQvisible$') {
2183
- jsxElement = addScriptNodeForInvisibleComponents(jsx);
2252
+ const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2253
+ jsxResult = jsx;
2184
2254
  if (jsxElement) {
2185
2255
  addUseOnEvent(jsxElement, 'document:onQinit$', useOnEvents[key]);
2186
2256
  }
2187
2257
  }
2188
2258
  else if (key.startsWith('document:') || key.startsWith('window:')) {
2189
- jsxElement = addScriptNodeForInvisibleComponents(jsx);
2259
+ const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2260
+ jsxResult = jsx;
2190
2261
  if (jsxElement) {
2191
2262
  addUseOnEvent(jsxElement, key, useOnEvents[key]);
2192
2263
  }
@@ -2204,7 +2275,7 @@
2204
2275
  }
2205
2276
  }
2206
2277
  }
2207
- return jsxElement;
2278
+ return jsxResult;
2208
2279
  });
2209
2280
  }
2210
2281
  function addUseOnEvent(jsxElement, key, value) {
@@ -2250,6 +2321,9 @@
2250
2321
  type: 'placeholder',
2251
2322
  hidden: '',
2252
2323
  }, null, 3);
2324
+ if (jsx.type === Slot) {
2325
+ return [jsxElement, _jsxSorted(Fragment, null, null, [jsx, jsxElement], 0, null)];
2326
+ }
2253
2327
  if (jsx.children == null) {
2254
2328
  jsx.children = jsxElement;
2255
2329
  }
@@ -2259,15 +2333,23 @@
2259
2333
  else {
2260
2334
  jsx.children = [jsx.children, jsxElement];
2261
2335
  }
2262
- return jsxElement;
2336
+ return [jsxElement, jsx];
2263
2337
  }
2264
2338
  else if (Array.isArray(jsx) && jsx.length) {
2265
2339
  // get first element
2266
- return addScriptNodeForInvisibleComponents(jsx[0]);
2340
+ const [jsxElement, _] = addScriptNodeForInvisibleComponents(jsx[0]);
2341
+ return [jsxElement, jsx];
2267
2342
  }
2268
- return null;
2343
+ return [null, null];
2269
2344
  }
2270
2345
 
2346
+ /** @internal */
2347
+ const _CONST_PROPS = Symbol('CONST');
2348
+ /** @internal */
2349
+ const _VAR_PROPS = Symbol('VAR');
2350
+ /** @internal @deprecated v1 compat */
2351
+ const _IMMUTABLE = Symbol('IMMUTABLE');
2352
+
2271
2353
  function isSlotProp(prop) {
2272
2354
  return !prop.startsWith('q:') && !prop.startsWith(NON_SERIALIZABLE_MARKER_PREFIX);
2273
2355
  }
@@ -2276,12 +2358,24 @@
2276
2358
  }
2277
2359
  /** @internal */
2278
2360
  const _restProps = (props, omit, target = {}) => {
2279
- for (const key in props) {
2361
+ let constPropsTarget = null;
2362
+ const constProps = props[_CONST_PROPS];
2363
+ if (constProps) {
2364
+ for (const key in constProps) {
2365
+ if (!omit.includes(key)) {
2366
+ constPropsTarget ||= {};
2367
+ constPropsTarget[key] = constProps[key];
2368
+ }
2369
+ }
2370
+ }
2371
+ const varPropsTarget = target;
2372
+ const varProps = props[_VAR_PROPS];
2373
+ for (const key in varProps) {
2280
2374
  if (!omit.includes(key)) {
2281
- target[key] = props[key];
2375
+ varPropsTarget[key] = varProps[key];
2282
2376
  }
2283
2377
  }
2284
- return target;
2378
+ return createPropsProxy(varPropsTarget, constPropsTarget);
2285
2379
  };
2286
2380
 
2287
2381
  function escapeHTML(html) {
@@ -2323,17 +2417,6 @@
2323
2417
  }
2324
2418
  }
2325
2419
 
2326
- function getFileLocationFromJsx(jsxDev) {
2327
- if (!jsxDev) {
2328
- return null;
2329
- }
2330
- const sanitizedFileName = jsxDev.fileName?.replace(/\\/g, '/');
2331
- if (sanitizedFileName) {
2332
- return `${sanitizedFileName}:${jsxDev.lineNumber}:${jsxDev.columnNumber}`;
2333
- }
2334
- return null;
2335
- }
2336
-
2337
2420
  const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2338
2421
  let journal = container.$journal$;
2339
2422
  /**
@@ -2352,9 +2435,9 @@
2352
2435
  /// (Node can be null, if we are at the end of the list.)
2353
2436
  let vCurrent = null;
2354
2437
  /// When we insert new node we start it here so that we can descend into it.
2355
- /// NOTE: it can't be stored in `vCurrent` because `vNewCurrent` is in journal
2438
+ /// NOTE: it can't be stored in `vCurrent` because `vNewNode` is in journal
2356
2439
  /// and is not connected to the tree.
2357
- let vNewNode = null; // TODO: delete, because journal is on vNode, the above comment no longer applies
2440
+ let vNewNode = null;
2358
2441
  /// When elements have keys they can be consumed out of order and therefore we can't use nextSibling.
2359
2442
  /// In such a case this array will contain the elements after the current location.
2360
2443
  /// The array even indices will contains keys and odd indices the vNode.
@@ -2407,7 +2490,7 @@
2407
2490
  }
2408
2491
  else if (isSignal(jsxValue)) {
2409
2492
  if (vCurrent) {
2410
- clearVNodeEffectDependencies(container, vCurrent);
2493
+ clearAllEffects(container, vCurrent);
2411
2494
  }
2412
2495
  expectVirtual(VirtualType.WrappedSignal, null);
2413
2496
  descend(trackSignalAndAssignHost(jsxValue, (vNewNode || vCurrent), EffectProperty.VNODE, container), true);
@@ -2622,9 +2705,10 @@
2622
2705
  };
2623
2706
  const projections = [];
2624
2707
  if (host) {
2708
+ const props = vnode_getProps(host);
2625
2709
  // we need to create empty projections for all the slots to remove unused slots content
2626
- for (let i = vnode_getPropStartIndex(host); i < host.length; i = i + 2) {
2627
- const prop = host[i];
2710
+ for (let i = 0; i < props.length; i = i + 2) {
2711
+ const prop = props[i];
2628
2712
  if (isSlotProp(prop)) {
2629
2713
  const slotName = prop;
2630
2714
  projections.push(slotName);
@@ -2792,10 +2876,19 @@
2792
2876
  // But we need to mark them so that they don't get pulled into the diff.
2793
2877
  const eventName = getEventNameFromJsxProp(key);
2794
2878
  const scope = getEventNameScopeFromJsxProp(key);
2795
- vnode_setProp(vNewNode, HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
2796
2879
  if (eventName) {
2880
+ vnode_setProp(vNewNode, HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
2797
2881
  registerQwikLoaderEvent(eventName);
2798
2882
  }
2883
+ if (scope) {
2884
+ // add an event attr with empty value for qwikloader element selector.
2885
+ // We don't need value here. For ssr this value is a QRL,
2886
+ // but for CSR value should be just empty
2887
+ const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
2888
+ if (htmlEvent) {
2889
+ vnode_setAttr(journal, vNewNode, htmlEvent, '');
2890
+ }
2891
+ }
2799
2892
  needsQDispatchEventPatch = true;
2800
2893
  continue;
2801
2894
  }
@@ -2808,12 +2901,15 @@
2808
2901
  value(element);
2809
2902
  continue;
2810
2903
  }
2904
+ else if (value == null) {
2905
+ continue;
2906
+ }
2811
2907
  else {
2812
2908
  throw qError(QError.invalidRefValue, [currentFile]);
2813
2909
  }
2814
2910
  }
2815
2911
  if (isSignal(value)) {
2816
- const signalData = new EffectPropData({
2912
+ const signalData = new SubscriptionData({
2817
2913
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
2818
2914
  $isConst$: true,
2819
2915
  });
@@ -2917,7 +3013,7 @@
2917
3013
  let returnValue = false;
2918
3014
  qrls.flat(2).forEach((qrl) => {
2919
3015
  if (qrl) {
2920
- const value = qrl(event, element);
3016
+ const value = container.$scheduler$(ChoreType.RUN_QRL, vNode, qrl, [event, element]);
2921
3017
  returnValue = returnValue || value === true;
2922
3018
  }
2923
3019
  });
@@ -2929,10 +3025,10 @@
2929
3025
  /** @param tag Returns true if `qDispatchEvent` needs patching */
2930
3026
  function setBulkProps(vnode, srcAttrs, currentFile) {
2931
3027
  vnode_ensureElementInflated(vnode);
2932
- const dstAttrs = vnode;
3028
+ const dstAttrs = vnode_getProps(vnode);
2933
3029
  let srcIdx = 0;
2934
3030
  const srcLength = srcAttrs.length;
2935
- let dstIdx = ElementVNodeProps.PROPS_OFFSET;
3031
+ let dstIdx = 0;
2936
3032
  let dstLength = dstAttrs.length;
2937
3033
  let srcKey = srcIdx < srcLength ? srcAttrs[srcIdx++] : null;
2938
3034
  let dstKey = dstIdx < dstLength ? dstAttrs[dstIdx++] : null;
@@ -2952,12 +3048,15 @@
2952
3048
  value(element);
2953
3049
  return;
2954
3050
  }
3051
+ else if (value == null) {
3052
+ return;
3053
+ }
2955
3054
  else {
2956
3055
  throw qError(QError.invalidRefValue, [currentFile]);
2957
3056
  }
2958
3057
  }
2959
3058
  if (isSignal(value)) {
2960
- const signalData = new EffectPropData({
3059
+ const signalData = new SubscriptionData({
2961
3060
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
2962
3061
  $isConst$: false,
2963
3062
  });
@@ -2971,21 +3070,21 @@
2971
3070
  };
2972
3071
  const recordJsxEvent = (key, value) => {
2973
3072
  const eventName = getEventNameFromJsxProp(key);
3073
+ const scope = getEventNameScopeFromJsxProp(key);
2974
3074
  if (eventName) {
2975
- const scope = getEventNameScopeFromJsxProp(key);
2976
3075
  record(':' + scope + ':' + eventName, value);
2977
- }
2978
- // add an event attr with empty value for qwikloader element selector.
2979
- // We don't need value here. For ssr this value is a QRL,
2980
- // but for CSR value should be just empty
2981
- const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
2982
- if (htmlEvent) {
2983
- record(htmlEvent, '');
2984
- }
2985
- // register an event for qwik loader
2986
- if (eventName) {
3076
+ // register an event for qwik loader
2987
3077
  registerQwikLoaderEvent(eventName);
2988
3078
  }
3079
+ if (scope) {
3080
+ // add an event attr with empty value for qwikloader element selector.
3081
+ // We don't need value here. For ssr this value is a QRL,
3082
+ // but for CSR value should be just empty
3083
+ const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
3084
+ if (htmlEvent) {
3085
+ record(htmlEvent, '');
3086
+ }
3087
+ }
2989
3088
  };
2990
3089
  while (srcKey !== null || dstKey !== null) {
2991
3090
  if (dstKey?.startsWith(HANDLER_PREFIX) || dstKey?.startsWith(Q_PREFIX)) {
@@ -3181,10 +3280,6 @@
3181
3280
  else if (!hashesAreEqual) {
3182
3281
  insertNewComponent(host, componentQRL, jsxProps);
3183
3282
  if (vNewNode) {
3184
- if (host) {
3185
- // TODO(varixo): not sure why we need to copy flags here.
3186
- vNewNode[VNodeProps.flags] = host[VNodeProps.flags];
3187
- }
3188
3283
  host = vNewNode;
3189
3284
  shouldRender = true;
3190
3285
  }
@@ -3237,7 +3332,7 @@
3237
3332
  }
3238
3333
  function insertNewComponent(host, componentQRL, jsxProps) {
3239
3334
  if (host) {
3240
- clearVNodeEffectDependencies(container, host);
3335
+ clearAllEffects(container, host);
3241
3336
  }
3242
3337
  vnode_insertBefore(journal, vParent, (vNewNode = vnode_newVirtual()), vCurrent && getInsertBefore());
3243
3338
  const jsxNode = jsxValue;
@@ -3325,8 +3420,8 @@
3325
3420
  if (!src || !dst) {
3326
3421
  return true;
3327
3422
  }
3328
- let srcKeys = removePropsKeys(Object.keys(src), ['children', QSubscribers]);
3329
- let dstKeys = removePropsKeys(Object.keys(dst), ['children', QSubscribers]);
3423
+ let srcKeys = removePropsKeys(Object.keys(src), ['children', QBackRefs]);
3424
+ let dstKeys = removePropsKeys(Object.keys(dst), ['children', QBackRefs]);
3330
3425
  if (srcKeys.length !== dstKeys.length) {
3331
3426
  return true;
3332
3427
  }
@@ -3372,7 +3467,7 @@
3372
3467
  do {
3373
3468
  const type = vCursor[VNodeProps.flags];
3374
3469
  if (type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) {
3375
- clearVNodeEffectDependencies(container, vCursor);
3470
+ clearAllEffects(container, vCursor);
3376
3471
  markVNodeAsDeleted(vCursor);
3377
3472
  // Only elements and virtual nodes need to be traversed for children
3378
3473
  if (type & VNodeFlags.Virtual) {
@@ -3382,7 +3477,7 @@
3382
3477
  const obj = seq[i];
3383
3478
  if (isTask(obj)) {
3384
3479
  const task = obj;
3385
- clearSubscriberEffectDependencies(container, task);
3480
+ clearAllEffects(container, task);
3386
3481
  if (task.$flags$ & TaskFlags.VISIBLE_TASK) {
3387
3482
  container.$scheduler$(ChoreType.CLEANUP_VISIBLE, task);
3388
3483
  }
@@ -3397,8 +3492,8 @@
3397
3492
  vnode_getProp(vCursor, OnRenderProp, null) !== null;
3398
3493
  if (isComponent) {
3399
3494
  // SPECIAL CASE: If we are a component, we need to descend into the projected content and release the content.
3400
- const attrs = vCursor;
3401
- for (let i = VirtualVNodeProps.PROPS_OFFSET; i < attrs.length; i = i + 2) {
3495
+ const attrs = vnode_getProps(vCursor);
3496
+ for (let i = 0; i < attrs.length; i = i + 2) {
3402
3497
  const key = attrs[i];
3403
3498
  if (!isParentSlotProp(key) && isSlotProp(key)) {
3404
3499
  const value = attrs[i + 1];
@@ -3458,130 +3553,420 @@
3458
3553
  // We are back where we started, we are done.
3459
3554
  return;
3460
3555
  }
3461
- const vNextParentSibling = vnode_getNextSibling(vParent);
3462
- if (vNextParentSibling) {
3463
- vCursor = vNextParentSibling;
3464
- break;
3556
+ const vNextParentSibling = vnode_getNextSibling(vParent);
3557
+ if (vNextParentSibling) {
3558
+ vCursor = vNextParentSibling;
3559
+ break;
3560
+ }
3561
+ vParent = vnode_getParent(vParent);
3562
+ }
3563
+ if (vParent == null) {
3564
+ // We are done.
3565
+ return;
3566
+ }
3567
+ } while (true);
3568
+ }
3569
+ function cleanupStaleUnclaimedProjection(journal, projection) {
3570
+ // we are removing a node where the projection would go after slot render.
3571
+ // This is not needed, so we need to cleanup still unclaimed projection
3572
+ const projectionParent = vnode_getParent(projection);
3573
+ if (projectionParent) {
3574
+ const projectionParentType = projectionParent[VNodeProps.flags];
3575
+ if (projectionParentType & VNodeFlags.Element &&
3576
+ vnode_getElementName(projectionParent) === QTemplate) {
3577
+ // if parent is the q:template element then projection is still unclaimed - remove it
3578
+ vnode_remove(journal, projectionParent, projection, true);
3579
+ }
3580
+ }
3581
+ }
3582
+ function markVNodeAsDeleted(vCursor) {
3583
+ /**
3584
+ * Marks vCursor as deleted. We need to do this to prevent chores from running after the vnode is
3585
+ * removed. (for example signal subscriptions)
3586
+ */
3587
+ vCursor[VNodeProps.flags] |= VNodeFlags.Deleted;
3588
+ }
3589
+ /**
3590
+ * This marks the property as immutable. It is needed for the QRLs so that QwikLoader can get a hold
3591
+ * of them. This character must be `:` so that the `vnode_getAttr` can ignore them.
3592
+ */
3593
+ const HANDLER_PREFIX = ':';
3594
+ let count = 0;
3595
+ var SiblingsArray;
3596
+ (function (SiblingsArray) {
3597
+ SiblingsArray[SiblingsArray["Name"] = 0] = "Name";
3598
+ SiblingsArray[SiblingsArray["Key"] = 1] = "Key";
3599
+ SiblingsArray[SiblingsArray["VNode"] = 2] = "VNode";
3600
+ SiblingsArray[SiblingsArray["Size"] = 3] = "Size";
3601
+ SiblingsArray[SiblingsArray["NextVNode"] = 5] = "NextVNode";
3602
+ })(SiblingsArray || (SiblingsArray = {}));
3603
+
3604
+ /** @internal */
3605
+ const useResourceQrl = (qrl, opts) => {
3606
+ const { val, set, i, iCtx } = useSequentialScope();
3607
+ if (val != null) {
3608
+ return val;
3609
+ }
3610
+ assertQrl(qrl);
3611
+ const container = iCtx.$container$;
3612
+ const resource = createResourceReturn(container, opts);
3613
+ const el = iCtx.$hostElement$;
3614
+ const task = new Task(TaskFlags.DIRTY | TaskFlags.RESOURCE, i, el, qrl, resource, null);
3615
+ container.$scheduler$(ChoreType.TASK, task);
3616
+ set(resource);
3617
+ return resource;
3618
+ };
3619
+ // <docs markdown="../readme.md#useResource">
3620
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
3621
+ // (edit ../readme.md#useResource instead and run `pnpm docs.sync`)
3622
+ /**
3623
+ * This method works like an async memoized function that runs whenever some tracked value changes
3624
+ * and returns some data.
3625
+ *
3626
+ * `useResource` however returns immediate a `ResourceReturn` object that contains the data and a
3627
+ * state that indicates if the data is available or not.
3628
+ *
3629
+ * The status can be one of the following:
3630
+ *
3631
+ * - `pending` - the data is not yet available.
3632
+ * - `resolved` - the data is available.
3633
+ * - `rejected` - the data is not available due to an error or timeout.
3634
+ *
3635
+ * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
3636
+ * re-throw it (or a new Error), the resource status will never be `rejected`.
3637
+ *
3638
+ * ### Example
3639
+ *
3640
+ * Example showing how `useResource` to perform a fetch to request the weather, whenever the input
3641
+ * city name changes.
3642
+ *
3643
+ * ```tsx
3644
+ * const Cmp = component$(() => {
3645
+ * const cityS = useSignal('');
3646
+ *
3647
+ * const weatherResource = useResource$(async ({ track, cleanup }) => {
3648
+ * const cityName = track(cityS);
3649
+ * const abortController = new AbortController();
3650
+ * cleanup(() => abortController.abort('cleanup'));
3651
+ * const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
3652
+ * signal: abortController.signal,
3653
+ * });
3654
+ * const data = await res.json();
3655
+ * return data as { temp: number };
3656
+ * });
3657
+ *
3658
+ * return (
3659
+ * <div>
3660
+ * <input name="city" bind:value={cityS} />
3661
+ * <Resource
3662
+ * value={weatherResource}
3663
+ * onResolved={(weather) => {
3664
+ * return <div>Temperature: {weather.temp}</div>;
3665
+ * }}
3666
+ * />
3667
+ * </div>
3668
+ * );
3669
+ * });
3670
+ * ```
3671
+ *
3672
+ * @public
3673
+ * @see Resource
3674
+ * @see ResourceReturn
3675
+ */
3676
+ // </docs>
3677
+ const Resource = (props) => {
3678
+ // Resource path
3679
+ return _jsxSorted(Fragment, null, null, getResourceValueAsPromise(props), 0, null);
3680
+ };
3681
+ function getResourceValueAsPromise(props) {
3682
+ const resource = props.value;
3683
+ if (isResourceReturn(resource) && resource.value) {
3684
+ const isBrowser = !isServerPlatform();
3685
+ if (isBrowser) {
3686
+ // create a subscription for the resource._state changes
3687
+ const state = resource._state;
3688
+ if (state === 'pending' && props.onPending) {
3689
+ return Promise.resolve().then(useBindInvokeContext(props.onPending));
3690
+ }
3691
+ else if (state === 'rejected' && props.onRejected) {
3692
+ return Promise.resolve(resource._error).then(useBindInvokeContext(props.onRejected));
3693
+ }
3694
+ else {
3695
+ const resolvedValue = untrack(() => resource._resolved);
3696
+ if (resolvedValue !== undefined) {
3697
+ // resolved, pending without onPending prop or rejected without onRejected prop
3698
+ return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved));
3699
+ }
3700
+ }
3701
+ }
3702
+ return resource.value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3703
+ }
3704
+ else if (isPromise(resource)) {
3705
+ return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3706
+ }
3707
+ else if (isSignal(resource)) {
3708
+ return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3709
+ }
3710
+ else {
3711
+ return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3712
+ }
3713
+ }
3714
+ const _createResourceReturn = (opts) => {
3715
+ const resource = {
3716
+ __brand: 'resource',
3717
+ value: undefined,
3718
+ loading: isServerPlatform() ? false : true,
3719
+ _resolved: undefined,
3720
+ _error: undefined,
3721
+ _state: 'pending',
3722
+ _timeout: opts?.timeout ?? -1,
3723
+ _cache: 0,
3724
+ };
3725
+ return resource;
3726
+ };
3727
+ const createResourceReturn = (container, opts, initialPromise) => {
3728
+ const result = _createResourceReturn(opts);
3729
+ result.value = initialPromise;
3730
+ return createStore(container, result, StoreFlags.RECURSIVE);
3731
+ };
3732
+ const isResourceReturn = (obj) => {
3733
+ return isObject(obj) && (getStoreTarget(obj) || obj).__brand === 'resource';
3734
+ };
3735
+ const runResource = (task, container, host) => {
3736
+ task.$flags$ &= ~TaskFlags.DIRTY;
3737
+ cleanupTask(task);
3738
+ const iCtx = newInvokeContext(container.$locale$, host, undefined, ResourceEvent);
3739
+ iCtx.$container$ = container;
3740
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
3741
+ const resource = task.$state$;
3742
+ assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
3743
+ const track = (obj, prop) => {
3744
+ const ctx = newInvokeContext();
3745
+ ctx.$effectSubscriber$ = getSubscriber(task, EffectProperty.COMPONENT);
3746
+ ctx.$container$ = container;
3747
+ return invoke(ctx, () => {
3748
+ if (isFunction(obj)) {
3749
+ return obj();
3750
+ }
3751
+ if (prop) {
3752
+ return obj[prop];
3753
+ }
3754
+ else if (isSignal(obj)) {
3755
+ return obj.value;
3756
+ }
3757
+ else {
3758
+ return obj;
3759
+ }
3760
+ });
3761
+ };
3762
+ const handleError = (reason) => container.handleError(reason, host);
3763
+ const cleanups = [];
3764
+ task.$destroy$ = noSerialize(() => {
3765
+ cleanups.forEach((fn) => {
3766
+ try {
3767
+ fn();
3768
+ }
3769
+ catch (err) {
3770
+ handleError(err);
3771
+ }
3772
+ });
3773
+ done = true;
3774
+ });
3775
+ const resourceTarget = unwrapStore(resource);
3776
+ const opts = {
3777
+ track,
3778
+ cleanup(fn) {
3779
+ if (typeof fn === 'function') {
3780
+ cleanups.push(fn);
3781
+ }
3782
+ },
3783
+ cache(policy) {
3784
+ let milliseconds = 0;
3785
+ if (policy === 'immutable') {
3786
+ milliseconds = Infinity;
3787
+ }
3788
+ else {
3789
+ milliseconds = policy;
3790
+ }
3791
+ resource._cache = milliseconds;
3792
+ },
3793
+ previous: resourceTarget._resolved,
3794
+ };
3795
+ let resolve;
3796
+ let reject;
3797
+ let done = false;
3798
+ const setState = (resolved, value) => {
3799
+ if (!done) {
3800
+ done = true;
3801
+ if (resolved) {
3802
+ done = true;
3803
+ resource.loading = false;
3804
+ resource._state = 'resolved';
3805
+ resource._resolved = value;
3806
+ resource._error = undefined;
3807
+ resolve(value);
3808
+ }
3809
+ else {
3810
+ done = true;
3811
+ resource.loading = false;
3812
+ resource._state = 'rejected';
3813
+ resource._error = value;
3814
+ reject(value);
3465
3815
  }
3466
- vParent = vnode_getParent(vParent);
3816
+ return true;
3467
3817
  }
3468
- if (vParent == null) {
3469
- // We are done.
3470
- return;
3818
+ return false;
3819
+ };
3820
+ /**
3821
+ * Add cleanup to resolve the resource if we are trying to run the same resource again while the
3822
+ * previous one is not resolved yet. The next `runResource` run will call this cleanup
3823
+ */
3824
+ cleanups.push(() => {
3825
+ if (untrack(() => resource.loading) === true) {
3826
+ const value = untrack(() => resource._resolved);
3827
+ setState(true, value);
3471
3828
  }
3472
- } while (true);
3473
- }
3474
- function cleanupStaleUnclaimedProjection(journal, projection) {
3475
- // we are removing a node where the projection would go after slot render.
3476
- // This is not needed, so we need to cleanup still unclaimed projection
3477
- const projectionParent = vnode_getParent(projection);
3478
- if (projectionParent) {
3479
- const projectionParentType = projectionParent[VNodeProps.flags];
3480
- if (projectionParentType & VNodeFlags.Element &&
3481
- vnode_getElementName(projectionParent) === QTemplate) {
3482
- // if parent is the q:template element then projection is still unclaimed - remove it
3483
- vnode_remove(journal, projectionParent, projection, true);
3829
+ });
3830
+ // Execute mutation inside empty invocation
3831
+ invoke(iCtx, () => {
3832
+ // console.log('RESOURCE.pending: ');
3833
+ resource._state = 'pending';
3834
+ resource.loading = !isServerPlatform();
3835
+ const promise = (resource.value = new Promise((r, re) => {
3836
+ resolve = r;
3837
+ reject = re;
3838
+ }));
3839
+ promise.catch(ignoreErrorToPreventNodeFromCrashing);
3840
+ });
3841
+ const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
3842
+ setState(true, value);
3843
+ }, (err) => {
3844
+ if (isPromise(err)) {
3845
+ return err.then(() => runResource(task, container, host));
3484
3846
  }
3847
+ else {
3848
+ setState(false, err);
3849
+ }
3850
+ });
3851
+ const timeout = resourceTarget._timeout;
3852
+ if (timeout > 0) {
3853
+ return Promise.race([
3854
+ promise,
3855
+ delay(timeout).then(() => {
3856
+ if (setState(false, new Error('timeout'))) {
3857
+ cleanupTask(task);
3858
+ }
3859
+ }),
3860
+ ]);
3485
3861
  }
3486
- }
3487
- function markVNodeAsDeleted(vCursor) {
3488
- /**
3489
- * Marks vCursor as deleted. We need to do this to prevent chores from running after the vnode is
3490
- * removed. (for example signal subscriptions)
3491
- */
3492
- vCursor[VNodeProps.flags] |= VNodeFlags.Deleted;
3493
- }
3494
- /**
3495
- * This marks the property as immutable. It is needed for the QRLs so that QwikLoader can get a hold
3496
- * of them. This character must be `:` so that the `vnode_getAttr` can ignore them.
3497
- */
3498
- const HANDLER_PREFIX = ':';
3499
- let count = 0;
3500
- var SiblingsArray;
3501
- (function (SiblingsArray) {
3502
- SiblingsArray[SiblingsArray["Name"] = 0] = "Name";
3503
- SiblingsArray[SiblingsArray["Key"] = 1] = "Key";
3504
- SiblingsArray[SiblingsArray["VNode"] = 2] = "VNode";
3505
- SiblingsArray[SiblingsArray["Size"] = 3] = "Size";
3506
- SiblingsArray[SiblingsArray["NextVNode"] = 5] = "NextVNode";
3507
- })(SiblingsArray || (SiblingsArray = {}));
3508
-
3509
- // <docs markdown="../../readme.md#implicit$FirstArg">
3510
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
3511
- // (edit ../../readme.md#implicit$FirstArg instead and run `pnpm docs.sync`)
3512
- /**
3513
- * Create a `____$(...)` convenience method from `___(...)`.
3514
- *
3515
- * It is very common for functions to take a lazy-loadable resource as a first argument. For this
3516
- * reason, the Qwik Optimizer automatically extracts the first argument from any function which ends
3517
- * in `$`.
3518
- *
3519
- * This means that `foo$(arg0)` and `foo($(arg0))` are equivalent with respect to Qwik Optimizer.
3520
- * The former is just a shorthand for the latter.
3521
- *
3522
- * For example, these function calls are equivalent:
3523
- *
3524
- * - `component$(() => {...})` is same as `component($(() => {...}))`
3525
- *
3526
- * ```tsx
3527
- * export function myApi(callback: QRL<() => void>): void {
3528
- * // ...
3529
- * }
3530
- *
3531
- * export const myApi$ = implicit$FirstArg(myApi);
3532
- * // type of myApi$: (callback: () => void): void
3533
- *
3534
- * // can be used as:
3535
- * myApi$(() => console.log('callback'));
3536
- *
3537
- * // will be transpiled to:
3538
- * // FILE: <current file>
3539
- * myApi(qrl('./chunk-abc.js', 'callback'));
3540
- *
3541
- * // FILE: chunk-abc.js
3542
- * export const callback = () => console.log('callback');
3543
- * ```
3544
- *
3545
- * @param fn - A function that should have its first argument automatically `$`.
3546
- * @public
3547
- */
3548
- // </docs>
3549
- const implicit$FirstArg = (fn) => {
3550
- return function (first, ...rest) {
3551
- return fn.call(null, dollar(first), ...rest);
3552
- };
3553
- };
3554
-
3555
- const createSignal$1 = (value) => {
3556
- return new Signal(null, value);
3862
+ return promise;
3557
3863
  };
3558
- const createComputedSignal = (qrl) => {
3559
- throwIfQRLNotResolved(qrl);
3560
- return new ComputedSignal(null, qrl);
3864
+ const ignoreErrorToPreventNodeFromCrashing = (err) => {
3865
+ // ignore error to prevent node from crashing
3866
+ // node will crash in promise is rejected and no one is listening to the rejection.
3561
3867
  };
3562
3868
 
3869
+ /// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
3870
+ const aVNodePath = [];
3871
+ const bVNodePath = [];
3563
3872
  /**
3564
- * Creates a Signal with the given value. If no value is given, the signal is created with
3565
- * `undefined`.
3873
+ * Compare two VNodes and determine their document position relative to each other.
3566
3874
  *
3567
- * @public
3875
+ * @param a VNode to compare
3876
+ * @param b VNode to compare
3877
+ * @param rootVNode - Root VNode of a container
3878
+ * @returns -1 if `a` is before `b`, 0 if `a` is the same as `b`, 1 if `a` is after `b`.
3568
3879
  */
3569
- const createSignal = createSignal$1;
3570
- /** @internal */
3571
- const createComputedQrl = createComputedSignal;
3880
+ const vnode_documentPosition = (a, b, rootVNode) => {
3881
+ if (a === b) {
3882
+ return 0;
3883
+ }
3884
+ let aDepth = -1;
3885
+ let bDepth = -1;
3886
+ while (a) {
3887
+ const vNode = (aVNodePath[++aDepth] = a);
3888
+ a = (vNode[VNodeProps.parent] ||
3889
+ (rootVNode && vnode_getProp(a, QSlotParent, (id) => vnode_locate(rootVNode, id))));
3890
+ }
3891
+ while (b) {
3892
+ const vNode = (bVNodePath[++bDepth] = b);
3893
+ b = (vNode[VNodeProps.parent] ||
3894
+ (rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))));
3895
+ }
3896
+ while (aDepth >= 0 && bDepth >= 0) {
3897
+ a = aVNodePath[aDepth];
3898
+ b = bVNodePath[bDepth];
3899
+ if (a === b) {
3900
+ // if the nodes are the same, we need to check the next level.
3901
+ aDepth--;
3902
+ bDepth--;
3903
+ }
3904
+ else {
3905
+ // We found a difference so we need to scan nodes at this level.
3906
+ let cursor = b;
3907
+ do {
3908
+ cursor = vnode_getNextSibling(cursor);
3909
+ if (cursor === a) {
3910
+ return 1;
3911
+ }
3912
+ } while (cursor);
3913
+ cursor = b;
3914
+ do {
3915
+ cursor = vnode_getPreviousSibling(cursor);
3916
+ if (cursor === a) {
3917
+ return -1;
3918
+ }
3919
+ } while (cursor);
3920
+ if (rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))) {
3921
+ // The "b" node is a projection, so we need to set it after "a" node,
3922
+ // because the "a" node could be a context provider.
3923
+ return -1;
3924
+ }
3925
+ // The node is not in the list of siblings, that means it must be disconnected.
3926
+ return 1;
3927
+ }
3928
+ }
3929
+ return aDepth < bDepth ? -1 : 1;
3930
+ };
3931
+ /// These global variables are used to avoid creating new arrays for each call to `ssrNodeDocumentPosition`.
3932
+ const aSsrNodePath = [];
3933
+ const bSsrNodePath = [];
3572
3934
  /**
3573
- * Create a computed signal which is calculated from the given QRL. A computed signal is a signal
3574
- * which is calculated from other signals. When the signals change, the computed signal is
3575
- * recalculated.
3576
- *
3577
- * The QRL must be a function which returns the value of the signal. The function must not have side
3578
- * effects, and it mus be synchronous.
3935
+ * Compare two SSR nodes and determine their document position relative to each other. Compares only
3936
+ * position between parent and child.
3579
3937
  *
3580
- * If you need the function to be async, use `useSignal` and `useTask$` instead.
3581
- *
3582
- * @public
3938
+ * @param a SSR node to compare
3939
+ * @param b SSR node to compare
3940
+ * @returns -1 if `a` is before `b`, 0 if `a` is the same as `b`, 1 if `a` is after `b`.
3583
3941
  */
3584
- const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedQrl);
3942
+ const ssrNodeDocumentPosition = (a, b) => {
3943
+ if (a === b) {
3944
+ return 0;
3945
+ }
3946
+ let aDepth = -1;
3947
+ let bDepth = -1;
3948
+ while (a) {
3949
+ const ssrNode = (aSsrNodePath[++aDepth] = a);
3950
+ a = ssrNode.currentComponentNode;
3951
+ }
3952
+ while (b) {
3953
+ const ssrNode = (bSsrNodePath[++bDepth] = b);
3954
+ b = ssrNode.currentComponentNode;
3955
+ }
3956
+ while (aDepth >= 0 && bDepth >= 0) {
3957
+ a = aSsrNodePath[aDepth];
3958
+ b = bSsrNodePath[bDepth];
3959
+ if (a === b) {
3960
+ // if the nodes are the same, we need to check the next level.
3961
+ aDepth--;
3962
+ bDepth--;
3963
+ }
3964
+ else {
3965
+ return 1;
3966
+ }
3967
+ }
3968
+ return aDepth < bDepth ? -1 : 1;
3969
+ };
3585
3970
 
3586
3971
  /**
3587
3972
  * Scheduler is responsible for running application code in predictable order.
@@ -3664,38 +4049,29 @@
3664
4049
  * - Visible Tasks are sorted afterJournalFlush, than depth first on component and finally in
3665
4050
  * declaration order within component.
3666
4051
  */
3667
- var ChoreType;
3668
- (function (ChoreType) {
3669
- /// MASKS defining three levels of sorting
3670
- ChoreType[ChoreType["MACRO"] = 240] = "MACRO";
3671
- /* order of elements (not encoded here) */
3672
- ChoreType[ChoreType["MICRO"] = 15] = "MICRO";
3673
- /** Ensure tha the QRL promise is resolved before processing next chores in the queue */
3674
- ChoreType[ChoreType["QRL_RESOLVE"] = 1] = "QRL_RESOLVE";
3675
- ChoreType[ChoreType["RESOURCE"] = 2] = "RESOURCE";
3676
- ChoreType[ChoreType["TASK"] = 3] = "TASK";
3677
- ChoreType[ChoreType["NODE_DIFF"] = 4] = "NODE_DIFF";
3678
- ChoreType[ChoreType["NODE_PROP"] = 5] = "NODE_PROP";
3679
- ChoreType[ChoreType["COMPONENT_SSR"] = 6] = "COMPONENT_SSR";
3680
- ChoreType[ChoreType["COMPONENT"] = 7] = "COMPONENT";
3681
- ChoreType[ChoreType["RECOMPUTE_AND_SCHEDULE_EFFECTS"] = 8] = "RECOMPUTE_AND_SCHEDULE_EFFECTS";
3682
- ChoreType[ChoreType["JOURNAL_FLUSH"] = 16] = "JOURNAL_FLUSH";
3683
- ChoreType[ChoreType["VISIBLE"] = 32] = "VISIBLE";
3684
- ChoreType[ChoreType["CLEANUP_VISIBLE"] = 48] = "CLEANUP_VISIBLE";
3685
- ChoreType[ChoreType["WAIT_FOR_ALL"] = 255] = "WAIT_FOR_ALL";
3686
- })(ChoreType || (ChoreType = {}));
4052
+ // Turn this on to get debug output of what the scheduler is doing.
4053
+ const DEBUG$1 = false;
4054
+ const getPromise = (chore) => (chore.$promise$ ||= new Promise((resolve) => {
4055
+ chore.$resolve$ = resolve;
4056
+ }));
3687
4057
  const createScheduler = (container, scheduleDrain, journalFlush) => {
3688
4058
  const choreQueue = [];
4059
+ const qrlRuns = [];
3689
4060
  let currentChore = null;
3690
- let journalFlushScheduled = false;
4061
+ let drainScheduled = false;
3691
4062
  return schedule;
3692
4063
  ///// IMPLEMENTATION /////
3693
4064
  function schedule(type, hostOrTask = null, targetOrQrl = null, payload = null) {
3694
- const runLater = type !== ChoreType.WAIT_FOR_ALL && type !== ChoreType.COMPONENT_SSR;
3695
- const isTask = type === ChoreType.TASK ||
3696
- type === ChoreType.VISIBLE ||
3697
- type === ChoreType.RESOURCE ||
3698
- type === ChoreType.CLEANUP_VISIBLE;
4065
+ const isServer = !isDomContainer(container);
4066
+ const isComponentSsr = isServer && type === ChoreType.COMPONENT;
4067
+ const runLater = type !== ChoreType.WAIT_FOR_ALL && !isComponentSsr && type !== ChoreType.RUN_QRL;
4068
+ const isTask = type === ChoreType.TASK || type === ChoreType.VISIBLE || type === ChoreType.CLEANUP_VISIBLE;
4069
+ const isClientOnly = type === ChoreType.JOURNAL_FLUSH ||
4070
+ type === ChoreType.NODE_DIFF ||
4071
+ type === ChoreType.NODE_PROP;
4072
+ if (isServer && isClientOnly) {
4073
+ return;
4074
+ }
3699
4075
  if (isTask) {
3700
4076
  hostOrTask.$flags$ |= TaskFlags.DIRTY;
3701
4077
  }
@@ -3714,184 +4090,234 @@
3714
4090
  $returnValue$: null,
3715
4091
  $executed$: false,
3716
4092
  };
3717
- chore.$promise$ = new Promise((resolve) => (chore.$resolve$ = resolve));
3718
4093
  chore = sortedInsert(choreQueue, chore, container.rootVNode || null);
3719
- if (!journalFlushScheduled && runLater) {
4094
+ if (!drainScheduled && runLater) {
3720
4095
  // If we are not currently draining, we need to schedule a drain.
3721
- journalFlushScheduled = true;
4096
+ drainScheduled = true;
3722
4097
  schedule(ChoreType.JOURNAL_FLUSH);
3723
- scheduleDrain();
4098
+ // Catch here to avoid unhandled promise rejection
4099
+ scheduleDrain()?.catch?.(() => { });
3724
4100
  }
4101
+ // TODO figure out what to do with chore errors
3725
4102
  if (runLater) {
3726
- return chore.$promise$;
4103
+ return getPromise(chore);
3727
4104
  }
3728
4105
  else {
3729
- return drainUpTo(chore, container.rootVNode || null);
3730
- }
3731
- }
3732
- /**
3733
- * Execute all of the chores up to and including the given chore.
3734
- *
3735
- * @param runUptoChore
3736
- */
3737
- function drainUpTo(runUptoChore, rootVNode) {
3738
- // If it already ran, it's not in the queue
3739
- if (runUptoChore.$executed$) {
3740
- return runUptoChore.$returnValue$;
3741
- }
3742
- if (currentChore) {
3743
- // Already running chore, which means we're waiting for async completion
3744
- return runUptoChore.$promise$;
4106
+ return drainUpTo(chore, isServer);
3745
4107
  }
4108
+ }
4109
+ /** Execute all of the chores up to and including the given chore. */
4110
+ function drainUpTo(runUptoChore, isServer) {
4111
+ let maxRetries = 5000;
3746
4112
  while (choreQueue.length) {
3747
- const nextChore = choreQueue.shift();
3748
- const order = choreComparator(nextChore, runUptoChore, rootVNode, false);
3749
- if (order === null) {
3750
- continue;
4113
+ if (maxRetries-- < 0) {
4114
+ throw new Error('drainUpTo: max retries reached');
3751
4115
  }
3752
- if (order > 0) {
3753
- // we have processed all of the chores up to and including the given chore.
3754
- break;
4116
+ if (currentChore) {
4117
+ // Already running chore, which means we're waiting for async completion
4118
+ return getPromise(currentChore)
4119
+ .then(() => drainUpTo(runUptoChore, isServer))
4120
+ .catch((e) => {
4121
+ container.handleError(e, currentChore?.$host$);
4122
+ });
4123
+ }
4124
+ const nextChore = choreQueue[0];
4125
+ if (nextChore.$executed$) {
4126
+ choreQueue.shift();
4127
+ if (nextChore === runUptoChore) {
4128
+ break;
4129
+ }
4130
+ continue;
3755
4131
  }
3756
- const isDeletedVNode = vNodeAlreadyDeleted(nextChore);
3757
- if (isDeletedVNode &&
4132
+ if (vNodeAlreadyDeleted(nextChore) &&
3758
4133
  // we need to process cleanup tasks for deleted nodes
3759
4134
  nextChore.$type$ !== ChoreType.CLEANUP_VISIBLE) {
4135
+ choreQueue.shift();
3760
4136
  continue;
3761
4137
  }
3762
- const returnValue = executeChore(nextChore);
3763
- if (isPromise(returnValue)) {
3764
- const promise = returnValue.then(() => drainUpTo(runUptoChore, rootVNode));
3765
- return promise;
3766
- }
4138
+ executeChore(nextChore, isServer);
3767
4139
  }
3768
4140
  return runUptoChore.$returnValue$;
3769
4141
  }
3770
- function executeChore(chore) {
4142
+ function executeChore(chore, isServer) {
3771
4143
  const host = chore.$host$;
3772
4144
  assertEqual(currentChore, null, 'Chore already running.');
3773
4145
  currentChore = chore;
3774
4146
  let returnValue = null;
3775
- switch (chore.$type$) {
3776
- case ChoreType.JOURNAL_FLUSH:
3777
- returnValue = journalFlush();
3778
- journalFlushScheduled = false;
3779
- break;
3780
- case ChoreType.COMPONENT:
3781
- case ChoreType.COMPONENT_SSR:
3782
- returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
3783
- if (chore.$type$ === ChoreType.COMPONENT) {
3784
- const styleScopedId = container.getHostProp(host, QScopedStyle);
3785
- return vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId));
4147
+ try {
4148
+ switch (chore.$type$) {
4149
+ case ChoreType.WAIT_FOR_ALL:
4150
+ {
4151
+ if (isServer) {
4152
+ drainScheduled = false;
4153
+ }
3786
4154
  }
3787
- else {
3788
- return jsx;
4155
+ break;
4156
+ case ChoreType.JOURNAL_FLUSH:
4157
+ {
4158
+ returnValue = journalFlush();
4159
+ drainScheduled = false;
4160
+ }
4161
+ break;
4162
+ case ChoreType.COMPONENT:
4163
+ {
4164
+ returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
4165
+ if (isServer) {
4166
+ return jsx;
4167
+ }
4168
+ else {
4169
+ const styleScopedId = container.getHostProp(host, QScopedStyle);
4170
+ return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
4171
+ }
4172
+ }, (err) => container.handleError(err, host));
4173
+ }
4174
+ break;
4175
+ case ChoreType.RUN_QRL:
4176
+ {
4177
+ const fn = chore.$target$.getFn();
4178
+ const result = retryOnPromise(() => fn(...chore.$payload$));
4179
+ if (isPromise(result)) {
4180
+ const handled = result
4181
+ .finally(() => {
4182
+ qrlRuns.splice(qrlRuns.indexOf(handled), 1);
4183
+ })
4184
+ .catch((error) => {
4185
+ container.handleError(error, chore.$host$);
4186
+ });
4187
+ // Don't wait for the promise to resolve
4188
+ // TODO come up with a better solution, we also want concurrent signal handling with tasks but serial tasks
4189
+ qrlRuns.push(handled);
4190
+ DEBUG$1 &&
4191
+ debugTrace('execute.DONE (but still running)', chore, currentChore, choreQueue);
4192
+ chore.$returnValue$ = handled;
4193
+ chore.$resolve$?.(handled);
4194
+ currentChore = null;
4195
+ chore.$executed$ = true;
4196
+ // early out so we don't call after()
4197
+ return;
4198
+ }
4199
+ returnValue = null;
4200
+ }
4201
+ break;
4202
+ case ChoreType.TASK:
4203
+ case ChoreType.VISIBLE:
4204
+ {
4205
+ const payload = chore.$payload$;
4206
+ if (payload.$flags$ & TaskFlags.RESOURCE) {
4207
+ const result = runResource(payload, container, host);
4208
+ // Don't await the return value of the resource, because async resources should not be awaited.
4209
+ // The reason for this is that we should be able to update for example a node with loading
4210
+ // text. If we await the resource, the loading text will not be displayed until the resource
4211
+ // is loaded.
4212
+ // Awaiting on the client also causes a deadlock.
4213
+ // In any case, the resource will never throw.
4214
+ returnValue = isServer ? result : null;
4215
+ }
4216
+ else {
4217
+ returnValue = runTask(payload, container, host);
4218
+ }
4219
+ }
4220
+ break;
4221
+ case ChoreType.CLEANUP_VISIBLE:
4222
+ {
4223
+ const task = chore.$payload$;
4224
+ cleanupTask(task);
4225
+ }
4226
+ break;
4227
+ case ChoreType.NODE_DIFF:
4228
+ {
4229
+ const parentVirtualNode = chore.$target$;
4230
+ let jsx = chore.$payload$;
4231
+ if (isSignal(jsx)) {
4232
+ jsx = jsx.value;
4233
+ }
4234
+ returnValue = retryOnPromise(() => vnode_diff(container, jsx, parentVirtualNode, null));
4235
+ }
4236
+ break;
4237
+ case ChoreType.NODE_PROP:
4238
+ {
4239
+ const virtualNode = chore.$host$;
4240
+ const payload = chore.$payload$;
4241
+ let value = payload.$value$;
4242
+ if (isSignal(value)) {
4243
+ value = value.value;
4244
+ }
4245
+ const isConst = payload.$isConst$;
4246
+ const journal = container.$journal$;
4247
+ const property = chore.$idx$;
4248
+ const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
4249
+ if (isConst) {
4250
+ const element = virtualNode[ElementVNodeProps.element];
4251
+ journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue);
4252
+ }
4253
+ else {
4254
+ vnode_setAttr(journal, virtualNode, property, serializedValue);
4255
+ }
4256
+ }
4257
+ break;
4258
+ case ChoreType.QRL_RESOLVE: {
4259
+ {
4260
+ const target = chore.$target$;
4261
+ returnValue = !target.resolved ? target.resolve() : null;
3789
4262
  }
3790
- }, (err) => container.handleError(err, host));
3791
- break;
3792
- case ChoreType.RESOURCE:
3793
- // Don't await the return value of the resource, because async resources should not be awaited.
3794
- // The reason for this is that we should be able to update for example a node with loading
3795
- // text. If we await the resource, the loading text will not be displayed until the resource
3796
- // is loaded.
3797
- const result = runResource(chore.$payload$, container, host);
3798
- returnValue = isDomContainer(container) ? null : result;
3799
- break;
3800
- case ChoreType.TASK:
3801
- returnValue = runTask(chore.$payload$, container, host);
3802
- break;
3803
- case ChoreType.VISIBLE:
3804
- returnValue = runTask(chore.$payload$, container, host);
3805
- break;
3806
- case ChoreType.CLEANUP_VISIBLE:
3807
- const task = chore.$payload$;
3808
- cleanupTask(task);
3809
- break;
3810
- case ChoreType.NODE_DIFF:
3811
- const parentVirtualNode = chore.$target$;
3812
- let jsx = chore.$payload$;
3813
- if (isSignal(jsx)) {
3814
- jsx = jsx.value;
3815
- }
3816
- returnValue = vnode_diff(container, jsx, parentVirtualNode, null);
3817
- break;
3818
- case ChoreType.NODE_PROP:
3819
- const virtualNode = chore.$host$;
3820
- const payload = chore.$payload$;
3821
- let value = payload.$value$;
3822
- if (isSignal(value)) {
3823
- value = value.value;
3824
- }
3825
- const isConst = payload.$isConst$;
3826
- const journal = container.$journal$;
3827
- const property = chore.$idx$;
3828
- const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
3829
- if (isConst) {
3830
- const element = virtualNode[ElementVNodeProps.element];
3831
- journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue);
3832
- }
3833
- else {
3834
- vnode_setAttr(journal, virtualNode, property, serializedValue);
3835
- }
3836
- break;
3837
- case ChoreType.QRL_RESOLVE: {
3838
- const target = chore.$target$;
3839
- returnValue = !target.resolved ? target.resolve() : null;
3840
- break;
3841
- }
3842
- case ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS: {
3843
- const target = chore.$target$;
3844
- const forceRunEffects = target.$forceRunEffects$;
3845
- target.$forceRunEffects$ = false;
3846
- if (!target.$effects$?.length) {
3847
4263
  break;
3848
4264
  }
3849
- returnValue = retryOnPromise(() => {
3850
- if (target.$computeIfNeeded$() || forceRunEffects) {
3851
- triggerEffects(container, target, target.$effects$);
4265
+ case ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS: {
4266
+ {
4267
+ const target = chore.$target$;
4268
+ const forceRunEffects = target.$forceRunEffects$;
4269
+ target.$forceRunEffects$ = false;
4270
+ if (!target.$effects$?.size) {
4271
+ break;
4272
+ }
4273
+ returnValue = retryOnPromise(() => {
4274
+ if (target.$computeIfNeeded$() || forceRunEffects) {
4275
+ triggerEffects(container, target, target.$effects$);
4276
+ }
4277
+ });
3852
4278
  }
3853
- });
3854
- break;
4279
+ break;
4280
+ }
3855
4281
  }
3856
4282
  }
3857
- return maybeThenPassError(returnValue, (value) => {
3858
- if (currentChore) {
3859
- currentChore.$executed$ = true;
3860
- currentChore.$resolve$?.(value);
3861
- }
4283
+ catch (e) {
4284
+ returnValue = Promise.reject(e);
4285
+ }
4286
+ const after = (value, error) => {
3862
4287
  currentChore = null;
3863
- return (chore.$returnValue$ = value);
3864
- });
3865
- }
3866
- };
3867
- const toNumber = (value) => {
3868
- return typeof value === 'number' ? value : -1;
3869
- };
3870
- /**
3871
- * When a derived signal is update we need to run vnode_diff. However the signal can update multiple
3872
- * times during component execution. For this reason it is necessary for us to update the schedule
3873
- * work with the latest result of the signal.
3874
- */
3875
- const choreUpdate = (existing, newChore) => {
3876
- if (existing.$type$ === ChoreType.NODE_DIFF) {
3877
- existing.$payload$ = newChore.$payload$;
3878
- }
3879
- };
3880
- function vNodeAlreadyDeleted(chore) {
3881
- return !!(chore.$host$ &&
3882
- vnode_isVNode(chore.$host$) &&
3883
- chore.$host$[VNodeProps.flags] & VNodeFlags.Deleted);
3884
- }
3885
- function choreComparator(a, b, rootVNode, shouldThrowOnHostMismatch) {
3886
- const macroTypeDiff = (a.$type$ & ChoreType.MACRO) - (b.$type$ & ChoreType.MACRO);
3887
- if (macroTypeDiff !== 0) {
3888
- return macroTypeDiff;
4288
+ chore.$executed$ = true;
4289
+ if (error) {
4290
+ container.handleError(error, host);
4291
+ }
4292
+ else {
4293
+ chore.$returnValue$ = value;
4294
+ chore.$resolve$?.(value);
4295
+ }
4296
+ };
4297
+ if (isPromise(returnValue)) {
4298
+ chore.$promise$ = returnValue.then(after, (error) => after(undefined, error));
4299
+ chore.$resolve$?.(chore.$promise$);
4300
+ chore.$resolve$ = undefined;
4301
+ }
4302
+ else {
4303
+ after(returnValue);
4304
+ }
3889
4305
  }
3890
- // JOURNAL_FLUSH does not have a host or $idx$, so we can't compare it.
3891
- if (a.$type$ !== ChoreType.JOURNAL_FLUSH) {
4306
+ /**
4307
+ * Compares two chores to determine their execution order in the scheduler's queue.
4308
+ *
4309
+ * @param a - The first chore to compare
4310
+ * @param b - The second chore to compare
4311
+ * @returns A number indicating the relative order of the chores. A negative number means `a` runs
4312
+ * before `b`.
4313
+ */
4314
+ function choreComparator(a, b, rootVNode) {
4315
+ const macroTypeDiff = (a.$type$ & ChoreType.MACRO) - (b.$type$ & ChoreType.MACRO);
4316
+ if (macroTypeDiff !== 0) {
4317
+ return macroTypeDiff;
4318
+ }
3892
4319
  const aHost = a.$host$;
3893
4320
  const bHost = b.$host$;
3894
- // QRL_RESOLVE does not have a host.
3895
4321
  if (aHost !== bHost && aHost !== null && bHost !== null) {
3896
4322
  if (vnode_isVNode(aHost) && vnode_isVNode(bHost)) {
3897
4323
  // we are running on the client.
@@ -3901,6 +4327,8 @@
3901
4327
  }
3902
4328
  }
3903
4329
  else {
4330
+ assertFalse(vnode_isVNode(aHost), 'expected aHost to be SSRNode but it is a VNode');
4331
+ assertFalse(vnode_isVNode(bHost), 'expected bHost to be SSRNode but it is a VNode');
3904
4332
  // we are running on the server.
3905
4333
  // On server we can't schedule task for a different host!
3906
4334
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -3909,258 +4337,125 @@
3909
4337
  You are attempting to change a state that has already been streamed to the client.
3910
4338
  This can lead to inconsistencies between Server-Side Rendering (SSR) and Client-Side Rendering (CSR).
3911
4339
  Problematic Node: ${aHost.toString()}`;
3912
- if (shouldThrowOnHostMismatch) {
3913
- throw qError(QError.serverHostMismatch, [errorMessage]);
3914
- }
3915
4340
  logWarn(errorMessage);
3916
- return null;
4341
+ const hostDiff = ssrNodeDocumentPosition(aHost, bHost);
4342
+ if (hostDiff !== 0) {
4343
+ return hostDiff;
4344
+ }
3917
4345
  }
3918
4346
  }
3919
4347
  const microTypeDiff = (a.$type$ & ChoreType.MICRO) - (b.$type$ & ChoreType.MICRO);
3920
4348
  if (microTypeDiff !== 0) {
3921
4349
  return microTypeDiff;
3922
4350
  }
4351
+ // types are the same
3923
4352
  const idxDiff = toNumber(a.$idx$) - toNumber(b.$idx$);
3924
4353
  if (idxDiff !== 0) {
3925
4354
  return idxDiff;
3926
4355
  }
3927
- // If the host is the same, we need to compare the target.
3928
- if (a.$target$ !== b.$target$ &&
3929
- ((a.$type$ === ChoreType.QRL_RESOLVE && b.$type$ === ChoreType.QRL_RESOLVE) ||
3930
- (a.$type$ === ChoreType.NODE_PROP && b.$type$ === ChoreType.NODE_PROP) ||
3931
- (a.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS &&
3932
- b.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS))) {
4356
+ // If the host is the same (or missing), and the type is the same, we need to compare the target.
4357
+ if (a.$target$ !== b.$target$ || a.$payload$ !== b.$payload$) {
3933
4358
  // 1 means that we are going to process chores as FIFO
3934
4359
  return 1;
3935
4360
  }
3936
- }
3937
- return 0;
3938
- }
3939
- function sortedFindIndex(sortedArray, value, rootVNode) {
3940
- /// We need to ensure that the `queue` is sorted by priority.
3941
- /// 1. Find a place where to insert into.
3942
- let bottom = 0;
3943
- let top = sortedArray.length;
3944
- while (bottom < top) {
3945
- const middle = bottom + ((top - bottom) >> 1);
3946
- const midChore = sortedArray[middle];
3947
- const comp = choreComparator(value, midChore, rootVNode, true);
3948
- if (comp < 0) {
3949
- top = middle;
3950
- }
3951
- else if (comp > 0) {
3952
- bottom = middle + 1;
3953
- }
3954
- else {
3955
- // We already have the host in the queue.
3956
- return middle;
4361
+ // If the chore is the same as the current chore, we will run it again
4362
+ if (b === currentChore) {
4363
+ return 1;
3957
4364
  }
4365
+ // The chores are the same and will run only once
4366
+ return 0;
3958
4367
  }
3959
- return ~bottom;
3960
- }
3961
- function sortedInsert(sortedArray, value, rootVNode) {
3962
- /// We need to ensure that the `queue` is sorted by priority.
3963
- /// 1. Find a place where to insert into.
3964
- const idx = sortedFindIndex(sortedArray, value, rootVNode);
3965
- if (idx < 0) {
3966
- /// 2. Insert the chore into the queue.
3967
- sortedArray.splice(~idx, 0, value);
3968
- return value;
3969
- }
3970
- const existing = sortedArray[idx];
3971
- choreUpdate(existing, value);
3972
- return existing;
3973
- }
3974
-
3975
- // <docs markdown="../readme.md#useLexicalScope">
3976
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
3977
- // (edit ../readme.md#useLexicalScope instead and run `pnpm docs.sync`)
3978
- /**
3979
- * Used by the Qwik Optimizer to restore the lexically scoped variables.
3980
- *
3981
- * This method should not be present in the application source code.
3982
- *
3983
- * NOTE: `useLexicalScope` method can only be used in the synchronous portion of the callback
3984
- * (before any `await` statements.)
3985
- *
3986
- * @internal
3987
- */
3988
- // </docs>
3989
- const useLexicalScope = () => {
3990
- const context = getInvokeContext();
3991
- let qrl = context.$qrl$;
3992
- if (!qrl) {
3993
- const el = context.$element$;
3994
- assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
3995
- const containerElement = _getQContainerElement(el);
3996
- assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
3997
- const container = getDomContainer(containerElement);
3998
- qrl = container.parseQRL(decodeURIComponent(String(context.$url$)));
3999
- }
4000
- else {
4001
- assertQrl(qrl);
4002
- assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
4003
- }
4004
- return qrl.$captureRef$;
4005
- };
4006
-
4007
- var TaskFlags;
4008
- (function (TaskFlags) {
4009
- TaskFlags[TaskFlags["VISIBLE_TASK"] = 1] = "VISIBLE_TASK";
4010
- TaskFlags[TaskFlags["TASK"] = 2] = "TASK";
4011
- TaskFlags[TaskFlags["RESOURCE"] = 4] = "RESOURCE";
4012
- TaskFlags[TaskFlags["DIRTY"] = 8] = "DIRTY";
4013
- })(TaskFlags || (TaskFlags = {}));
4014
- /** @internal */
4015
- const useTaskQrl = (qrl, opts) => {
4016
- const { val, set, iCtx, i } = useSequentialScope();
4017
- if (val) {
4018
- return;
4019
- }
4020
- assertQrl(qrl);
4021
- set(1);
4022
- const host = iCtx.$hostElement$;
4023
- const task = new Task(TaskFlags.DIRTY | TaskFlags.TASK, i, iCtx.$hostElement$, qrl, undefined, null);
4024
- // In V2 we add the task to the sequential scope. We need to do this
4025
- // in order to be able to retrieve it later when the parent element is
4026
- // deleted and we need to be able to release the task subscriptions.
4027
- set(task);
4028
- const result = runTask(task, iCtx.$container$, host);
4029
- if (isPromise(result)) {
4030
- throw result;
4031
- }
4032
- qrl.$resolveLazy$(iCtx.$element$);
4033
- if (isServerPlatform()) {
4034
- useRunTask(task, opts?.eagerness);
4035
- }
4036
- };
4037
- const runTask = (task, container, host) => {
4038
- task.$flags$ &= ~TaskFlags.DIRTY;
4039
- cleanupTask(task);
4040
- const iCtx = newInvokeContext(container.$locale$, host, undefined, TaskEvent);
4041
- iCtx.$container$ = container;
4042
- const taskFn = task.$qrl$.getFn(iCtx, () => clearSubscriberEffectDependencies(container, task));
4043
- const track = (obj, prop) => {
4044
- const ctx = newInvokeContext();
4045
- ctx.$effectSubscriber$ = [task, EffectProperty.COMPONENT];
4046
- ctx.$container$ = container;
4047
- return invoke(ctx, () => {
4048
- if (isFunction(obj)) {
4049
- return obj();
4050
- }
4051
- if (prop) {
4052
- return obj[prop];
4053
- }
4054
- else if (isSignal(obj)) {
4055
- return obj.value;
4368
+ function sortedFindIndex(sortedArray, value, rootVNode) {
4369
+ /// We need to ensure that the `queue` is sorted by priority.
4370
+ /// 1. Find a place where to insert into.
4371
+ let bottom = 0;
4372
+ let top = sortedArray.length;
4373
+ while (bottom < top) {
4374
+ const middle = bottom + ((top - bottom) >> 1);
4375
+ const midChore = sortedArray[middle];
4376
+ const comp = choreComparator(value, midChore, rootVNode);
4377
+ if (comp < 0) {
4378
+ top = middle;
4379
+ }
4380
+ else if (comp > 0) {
4381
+ bottom = middle + 1;
4056
4382
  }
4057
4383
  else {
4058
- return obj;
4059
- }
4060
- });
4061
- };
4062
- const handleError = (reason) => container.handleError(reason, host);
4063
- let cleanupFns = null;
4064
- const cleanup = (fn) => {
4065
- if (typeof fn == 'function') {
4066
- if (!cleanupFns) {
4067
- cleanupFns = [];
4068
- task.$destroy$ = noSerialize(() => {
4069
- task.$destroy$ = null;
4070
- cleanupFns.forEach((fn) => {
4071
- try {
4072
- fn();
4073
- }
4074
- catch (err) {
4075
- handleError(err);
4076
- }
4077
- });
4078
- });
4384
+ // We already have the host in the queue.
4385
+ return middle;
4079
4386
  }
4080
- cleanupFns.push(fn);
4081
- }
4082
- };
4083
- const taskApi = { track, cleanup };
4084
- const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
4085
- if (isPromise(err)) {
4086
- return err.then(() => runTask(task, container, host));
4087
- }
4088
- else {
4089
- return handleError(err);
4090
4387
  }
4091
- });
4092
- return result;
4093
- };
4094
- const cleanupTask = (task) => {
4095
- const destroy = task.$destroy$;
4096
- if (destroy) {
4097
- task.$destroy$ = null;
4098
- try {
4099
- destroy();
4388
+ return ~bottom;
4389
+ }
4390
+ function sortedInsert(sortedArray, value, rootVNode) {
4391
+ /// We need to ensure that the `queue` is sorted by priority.
4392
+ /// 1. Find a place where to insert into.
4393
+ const idx = sortedFindIndex(sortedArray, value, rootVNode);
4394
+ if (idx < 0) {
4395
+ /// 2. Insert the chore into the queue.
4396
+ sortedArray.splice(~idx, 0, value);
4397
+ return value;
4100
4398
  }
4101
- catch (err) {
4102
- logError(err);
4399
+ const existing = sortedArray[idx];
4400
+ /**
4401
+ * When a derived signal is updated we need to run vnode_diff. However the signal can update
4402
+ * multiple times during component execution. For this reason it is necessary for us to update
4403
+ * the chore with the latest result of the signal.
4404
+ */
4405
+ if (existing.$type$ === ChoreType.NODE_DIFF) {
4406
+ existing.$payload$ = value.$payload$;
4103
4407
  }
4408
+ if (existing.$executed$) {
4409
+ existing.$executed$ = false;
4410
+ }
4411
+ return existing;
4104
4412
  }
4105
4413
  };
4106
- const useRunTask = (task, eagerness) => {
4107
- if (eagerness === 'visible' || eagerness === 'intersection-observer') {
4108
- useOn('qvisible', getTaskHandlerQrl(task));
4109
- }
4110
- else if (eagerness === 'load' || eagerness === 'document-ready') {
4111
- useOnDocument('qinit', getTaskHandlerQrl(task));
4112
- }
4113
- else if (eagerness === 'idle' || eagerness === 'document-idle') {
4114
- useOnDocument('qidle', getTaskHandlerQrl(task));
4115
- }
4116
- };
4117
- const getTaskHandlerQrl = (task) => {
4118
- const taskQrl = task.$qrl$;
4119
- const taskHandler = createQRL(taskQrl.$chunk$, '_hW', _hW, null, null, [task], taskQrl.$symbol$);
4120
- // Needed for chunk lookup in dev mode
4121
- if (taskQrl.dev) {
4122
- taskHandler.dev = taskQrl.dev;
4123
- }
4124
- return taskHandler;
4125
- };
4126
- class Task extends Subscriber {
4127
- $flags$;
4128
- $index$;
4129
- $el$;
4130
- $qrl$;
4131
- $state$;
4132
- $destroy$;
4133
- constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
4134
- super();
4135
- this.$flags$ = $flags$;
4136
- this.$index$ = $index$;
4137
- this.$el$ = $el$;
4138
- this.$qrl$ = $qrl$;
4139
- this.$state$ = $state$;
4140
- this.$destroy$ = $destroy$;
4141
- }
4142
- }
4143
- const isTask = (value) => {
4144
- return value instanceof Task;
4145
- };
4146
- /**
4147
- * Low-level API used by the Optimizer to process `useTask$()` API. This method is not intended to
4148
- * be used by developers.
4149
- *
4150
- * @internal
4151
- */
4152
- const _hW = () => {
4153
- const [task] = useLexicalScope();
4154
- const container = getDomContainer(task.$el$);
4155
- const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK;
4156
- container.$scheduler$(type, task);
4414
+ const toNumber = (value) => {
4415
+ return typeof value === 'number' ? value : -1;
4157
4416
  };
4158
-
4159
- /**
4160
- * Special value used to mark that a given signal needs to be computed. This is essentially a
4161
- * "marked as dirty" flag.
4162
- */
4163
- const NEEDS_COMPUTATION = Symbol('invalid');
4417
+ function vNodeAlreadyDeleted(chore) {
4418
+ return !!(chore.$host$ &&
4419
+ vnode_isVNode(chore.$host$) &&
4420
+ chore.$host$[VNodeProps.flags] & VNodeFlags.Deleted);
4421
+ }
4422
+ function debugChoreTypeToString(type) {
4423
+ return ({
4424
+ [ChoreType.QRL_RESOLVE]: 'QRL_RESOLVE',
4425
+ [ChoreType.RUN_QRL]: 'RUN_QRL',
4426
+ [ChoreType.TASK]: 'TASK',
4427
+ [ChoreType.NODE_DIFF]: 'NODE_DIFF',
4428
+ [ChoreType.NODE_PROP]: 'NODE_PROP',
4429
+ [ChoreType.COMPONENT]: 'COMPONENT',
4430
+ [ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS]: 'RECOMPUTE_SIGNAL',
4431
+ [ChoreType.JOURNAL_FLUSH]: 'JOURNAL_FLUSH',
4432
+ [ChoreType.VISIBLE]: 'VISIBLE',
4433
+ [ChoreType.CLEANUP_VISIBLE]: 'CLEANUP_VISIBLE',
4434
+ [ChoreType.WAIT_FOR_ALL]: 'WAIT_FOR_ALL',
4435
+ }[type] || 'UNKNOWN: ' + type);
4436
+ }
4437
+ function debugChoreToString(chore) {
4438
+ const type = debugChoreTypeToString(chore.$type$);
4439
+ const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
4440
+ const qrlTarget = chore.$target$?.$symbol$;
4441
+ return `Chore(${type} ${chore.$type$ === ChoreType.QRL_RESOLVE || chore.$type$ === ChoreType.RUN_QRL ? qrlTarget : host} ${chore.$idx$})`;
4442
+ }
4443
+ function debugTrace(action, arg, currentChore, queue) {
4444
+ const lines = ['===========================\nScheduler: ' + action];
4445
+ if (arg && !('$type$' in arg)) {
4446
+ lines.push(' arg: ' + String(arg).replaceAll(/\n.*/gim, ''));
4447
+ }
4448
+ if (queue) {
4449
+ queue.forEach((chore) => {
4450
+ const active = chore === arg ? '>>>' : ' ';
4451
+ lines.push(` ${active} > ` +
4452
+ (chore === currentChore ? '[running] ' : '') +
4453
+ debugChoreToString(chore));
4454
+ });
4455
+ }
4456
+ // eslint-disable-next-line no-console
4457
+ console.log(lines.join('\n') + '\n');
4458
+ }
4164
4459
 
4165
4460
  /**
4166
4461
  * @file
@@ -4194,18 +4489,19 @@
4194
4489
  return value instanceof Signal;
4195
4490
  };
4196
4491
  /** @internal */
4197
- class EffectPropData {
4492
+ class SubscriptionData {
4198
4493
  data;
4199
4494
  constructor(data) {
4200
4495
  this.data = data;
4201
4496
  }
4202
4497
  }
4203
- var EffectSubscriptionsProp;
4204
- (function (EffectSubscriptionsProp) {
4205
- EffectSubscriptionsProp[EffectSubscriptionsProp["EFFECT"] = 0] = "EFFECT";
4206
- EffectSubscriptionsProp[EffectSubscriptionsProp["PROPERTY"] = 1] = "PROPERTY";
4207
- EffectSubscriptionsProp[EffectSubscriptionsProp["FIRST_BACK_REF_OR_DATA"] = 2] = "FIRST_BACK_REF_OR_DATA";
4208
- })(EffectSubscriptionsProp || (EffectSubscriptionsProp = {}));
4498
+ var EffectSubscriptionProp;
4499
+ (function (EffectSubscriptionProp) {
4500
+ EffectSubscriptionProp[EffectSubscriptionProp["CONSUMER"] = 0] = "CONSUMER";
4501
+ EffectSubscriptionProp[EffectSubscriptionProp["PROPERTY"] = 1] = "PROPERTY";
4502
+ EffectSubscriptionProp[EffectSubscriptionProp["BACK_REF"] = 2] = "BACK_REF";
4503
+ EffectSubscriptionProp[EffectSubscriptionProp["DATA"] = 3] = "DATA";
4504
+ })(EffectSubscriptionProp || (EffectSubscriptionProp = {}));
4209
4505
  var EffectProperty;
4210
4506
  (function (EffectProperty) {
4211
4507
  EffectProperty["COMPONENT"] = ":";
@@ -4242,19 +4538,16 @@
4242
4538
  }
4243
4539
  const effectSubscriber = ctx.$effectSubscriber$;
4244
4540
  if (effectSubscriber) {
4245
- const effects = (this.$effects$ ||= []);
4541
+ const effects = (this.$effects$ ||= new Set());
4246
4542
  // Let's make sure that we have a reference to this effect.
4247
4543
  // Adding reference is essentially adding a subscription, so if the signal
4248
4544
  // changes we know who to notify.
4249
- ensureContainsEffect(effects, effectSubscriber);
4545
+ ensureContainsSubscription(effects, effectSubscriber);
4250
4546
  // But when effect is scheduled in needs to be able to know which signals
4251
4547
  // to unsubscribe from. So we need to store the reference from the effect back
4252
4548
  // to this signal.
4253
- ensureContains(effectSubscriber, this);
4254
- if (isSubscriber(this)) {
4255
- // We need to add the subscriber to the effect so that we can clean it up later
4256
- ensureEffectContainsSubscriber(effectSubscriber[EffectSubscriptionsProp.EFFECT], this, this.$container$);
4257
- }
4549
+ ensureContainsBackRef(effectSubscriber, this);
4550
+ addQrlToSerializationCtx(effectSubscriber, this.$container$);
4258
4551
  }
4259
4552
  }
4260
4553
  return this.untrackedValue;
@@ -4273,122 +4566,97 @@
4273
4566
  }
4274
4567
  toString() {
4275
4568
  return (`[${this.constructor.name}${this.$invalid$ ? ' INVALID' : ''} ${String(this.$untrackedValue$)}]` +
4276
- (this.$effects$?.map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' ')).join('\n') || ''));
4569
+ (Array.from(this.$effects$ || [])
4570
+ .map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' '))
4571
+ .join('\n') || ''));
4277
4572
  }
4278
4573
  toJSON() {
4279
4574
  return { value: this.$untrackedValue$ };
4280
4575
  }
4281
4576
  }
4282
- /** Ensure the item is in array (do nothing if already there) */
4283
- const ensureContains = (array, value) => {
4284
- const isMissing = array.indexOf(value) === -1;
4285
- if (isMissing) {
4286
- array.push(value);
4287
- }
4288
- };
4289
- const ensureContainsEffect = (array, effectSubscriptions) => {
4290
- for (let i = 0; i < array.length; i++) {
4291
- const existingEffect = array[i];
4292
- if (existingEffect[0] === effectSubscriptions[0] &&
4293
- existingEffect[1] === effectSubscriptions[1]) {
4294
- return;
4295
- }
4296
- }
4297
- array.push(effectSubscriptions);
4577
+ const ensureContainsSubscription = (array, effectSubscription) => {
4578
+ array.add(effectSubscription);
4298
4579
  };
4299
- const ensureEffectContainsSubscriber = (effect, subscriber, container) => {
4300
- if (isSubscriber(effect)) {
4301
- effect.$effectDependencies$ ||= [];
4302
- if (subscriberExistInSubscribers(effect.$effectDependencies$, subscriber)) {
4303
- return;
4580
+ /** Ensure the item is in back refs set */
4581
+ const ensureContainsBackRef = (array, value) => {
4582
+ array[EffectSubscriptionProp.BACK_REF] ||= new Set();
4583
+ array[EffectSubscriptionProp.BACK_REF].add(value);
4584
+ };
4585
+ const addQrlToSerializationCtx = (effectSubscriber, container) => {
4586
+ if (!!container && !isDomContainer(container)) {
4587
+ const effect = effectSubscriber[EffectSubscriptionProp.CONSUMER];
4588
+ const property = effectSubscriber[EffectSubscriptionProp.PROPERTY];
4589
+ let qrl = null;
4590
+ if (isTask(effect)) {
4591
+ qrl = effect.$qrl$;
4304
4592
  }
4305
- effect.$effectDependencies$.push(subscriber);
4306
- }
4307
- else if (vnode_isVNode(effect) && !vnode_isTextVNode(effect)) {
4308
- let subscribers = vnode_getProp(effect, QSubscribers, container ? container.$getObjectById$ : null);
4309
- subscribers ||= [];
4310
- if (subscriberExistInSubscribers(subscribers, subscriber)) {
4311
- return;
4593
+ else if (effect instanceof ComputedSignal) {
4594
+ qrl = effect.$computeQrl$;
4312
4595
  }
4313
- subscribers.push(subscriber);
4314
- vnode_setProp(effect, QSubscribers, subscribers);
4315
- }
4316
- else if (isSSRNode(effect)) {
4317
- let subscribers = effect.getProp(QSubscribers);
4318
- subscribers ||= [];
4319
- if (subscriberExistInSubscribers(subscribers, subscriber)) {
4320
- return;
4596
+ else if (property === EffectProperty.COMPONENT) {
4597
+ qrl = container.getHostProp(effect, OnRenderProp);
4321
4598
  }
4322
- subscribers.push(subscriber);
4323
- effect.setProp(QSubscribers, subscribers);
4324
- }
4325
- };
4326
- const isSSRNode = (effect) => {
4327
- return 'setProp' in effect && 'getProp' in effect && 'removeProp' in effect && 'id' in effect;
4328
- };
4329
- const subscriberExistInSubscribers = (subscribers, subscriber) => {
4330
- for (let i = 0; i < subscribers.length; i++) {
4331
- if (subscribers[i] === subscriber) {
4332
- return true;
4599
+ if (qrl) {
4600
+ container.serializationCtx.$eventQrls$.add(qrl);
4333
4601
  }
4334
4602
  }
4335
- return false;
4336
4603
  };
4337
4604
  const triggerEffects = (container, signal, effects) => {
4605
+ const isBrowser = isDomContainer(container);
4338
4606
  if (effects) {
4339
- const scheduleEffect = (effectSubscriptions) => {
4340
- const effect = effectSubscriptions[EffectSubscriptionsProp.EFFECT];
4341
- const property = effectSubscriptions[EffectSubscriptionsProp.PROPERTY];
4607
+ const scheduleEffect = (effectSubscription) => {
4608
+ const consumer = effectSubscription[EffectSubscriptionProp.CONSUMER];
4609
+ const property = effectSubscription[EffectSubscriptionProp.PROPERTY];
4342
4610
  assertDefined(container, 'Container must be defined.');
4343
- if (isTask(effect)) {
4344
- effect.$flags$ |= TaskFlags.DIRTY;
4611
+ if (isTask(consumer)) {
4612
+ consumer.$flags$ |= TaskFlags.DIRTY;
4345
4613
  let choreType = ChoreType.TASK;
4346
- if (effect.$flags$ & TaskFlags.VISIBLE_TASK) {
4614
+ if (consumer.$flags$ & TaskFlags.VISIBLE_TASK) {
4347
4615
  choreType = ChoreType.VISIBLE;
4348
4616
  }
4349
- else if (effect.$flags$ & TaskFlags.RESOURCE) {
4350
- choreType = ChoreType.RESOURCE;
4351
- }
4352
- container.$scheduler$(choreType, effect);
4617
+ container.$scheduler$(choreType, consumer);
4353
4618
  }
4354
- else if (effect instanceof Signal) {
4619
+ else if (consumer instanceof Signal) {
4355
4620
  // we don't schedule ComputedSignal/DerivedSignal directly, instead we invalidate it and
4356
4621
  // and schedule the signals effects (recursively)
4357
- if (effect instanceof ComputedSignal) {
4622
+ if (consumer instanceof ComputedSignal) {
4358
4623
  // Ensure that the computed signal's QRL is resolved.
4359
4624
  // If not resolved schedule it to be resolved.
4360
- if (!effect.$computeQrl$.resolved) {
4361
- container.$scheduler$(ChoreType.QRL_RESOLVE, null, effect.$computeQrl$);
4625
+ if (!consumer.$computeQrl$.resolved) {
4626
+ container.$scheduler$(ChoreType.QRL_RESOLVE, null, consumer.$computeQrl$);
4362
4627
  }
4363
4628
  }
4364
- effect.$invalidate$();
4629
+ consumer.$invalidate$();
4365
4630
  }
4366
4631
  else if (property === EffectProperty.COMPONENT) {
4367
- const host = effect;
4632
+ const host = consumer;
4368
4633
  const qrl = container.getHostProp(host, OnRenderProp);
4369
4634
  assertDefined(qrl, 'Component must have QRL');
4370
4635
  const props = container.getHostProp(host, ELEMENT_PROPS);
4371
4636
  container.$scheduler$(ChoreType.COMPONENT, host, qrl, props);
4372
4637
  }
4373
- else if (property === EffectProperty.VNODE) {
4374
- const host = effect;
4375
- const target = host;
4376
- container.$scheduler$(ChoreType.NODE_DIFF, host, target, signal);
4377
- }
4378
- else {
4379
- const host = effect;
4380
- const effectData = effectSubscriptions[EffectSubscriptionsProp.FIRST_BACK_REF_OR_DATA];
4381
- if (effectData instanceof EffectPropData) {
4382
- const data = effectData.data;
4383
- const payload = {
4384
- ...data,
4385
- $value$: signal,
4386
- };
4387
- container.$scheduler$(ChoreType.NODE_PROP, host, property, payload);
4638
+ else if (isBrowser) {
4639
+ if (property === EffectProperty.VNODE) {
4640
+ const host = consumer;
4641
+ container.$scheduler$(ChoreType.NODE_DIFF, host, host, signal);
4642
+ }
4643
+ else {
4644
+ const host = consumer;
4645
+ const effectData = effectSubscription[EffectSubscriptionProp.DATA];
4646
+ if (effectData instanceof SubscriptionData) {
4647
+ const data = effectData.data;
4648
+ const payload = {
4649
+ ...data,
4650
+ $value$: signal,
4651
+ };
4652
+ container.$scheduler$(ChoreType.NODE_PROP, host, property, payload);
4653
+ }
4388
4654
  }
4389
4655
  }
4390
4656
  };
4391
- effects.forEach(scheduleEffect);
4657
+ for (const effect of effects) {
4658
+ scheduleEffect(effect);
4659
+ }
4392
4660
  }
4393
4661
  };
4394
4662
  /**
@@ -4446,7 +4714,7 @@
4446
4714
  throwIfQRLNotResolved(computeQrl);
4447
4715
  const ctx = tryGetInvokeContext();
4448
4716
  const previousEffectSubscription = ctx?.$effectSubscriber$;
4449
- ctx && (ctx.$effectSubscriber$ = [this, EffectProperty.VNODE]);
4717
+ ctx && (ctx.$effectSubscriber$ = getSubscriber(this, EffectProperty.VNODE));
4450
4718
  try {
4451
4719
  const untrackedValue = computeQrl.getFn(ctx)();
4452
4720
  if (isPromise(untrackedValue)) {
@@ -4484,9 +4752,9 @@
4484
4752
  // We need a separate flag to know when the computation needs running because
4485
4753
  // we need the old value to know if effects need running after computation
4486
4754
  $invalid$ = true;
4487
- $effectDependencies$ = null;
4488
4755
  $hostElement$ = null;
4489
4756
  $forceRunEffects$ = false;
4757
+ [_EFFECT_BACK_REF] = null;
4490
4758
  constructor(container, fn, args, fnStr) {
4491
4759
  super(container, NEEDS_COMPUTATION);
4492
4760
  this.$args$ = args;
@@ -4554,7 +4822,7 @@
4554
4822
  if (jsx.key !== null) {
4555
4823
  host.setProp(ELEMENT_KEY, jsx.key);
4556
4824
  }
4557
- return scheduler(ChoreType.COMPONENT_SSR, host, componentQrl, srcProps);
4825
+ return scheduler(ChoreType.COMPONENT, host, componentQrl, srcProps);
4558
4826
  };
4559
4827
 
4560
4828
  class ParentComponentData {
@@ -4566,21 +4834,10 @@
4566
4834
  }
4567
4835
  }
4568
4836
  /** @internal */
4569
- function _walkJSX(ssr, value, options) {
4837
+ async function _walkJSX(ssr, value, options) {
4570
4838
  const stack = [value];
4571
- let resolveDrain;
4572
- let rejectDrain;
4573
- const drained = options.allowPromises &&
4574
- new Promise((res, rej) => {
4575
- resolveDrain = res;
4576
- rejectDrain = rej;
4577
- });
4578
4839
  const enqueue = (value) => stack.push(value);
4579
- const resolveValue = (value) => {
4580
- stack.push(value);
4581
- drain();
4582
- };
4583
- const drain = () => {
4840
+ const drain = async () => {
4584
4841
  while (stack.length) {
4585
4842
  const value = stack.pop();
4586
4843
  if (value instanceof ParentComponentData) {
@@ -4590,20 +4847,10 @@
4590
4847
  }
4591
4848
  else if (typeof value === 'function') {
4592
4849
  if (value === Promise) {
4593
- if (!options.allowPromises) {
4594
- throw qError(QError.promisesNotExpected);
4595
- }
4596
- stack.pop().then(resolveValue, rejectDrain);
4597
- return;
4598
- }
4599
- const waitOn = value.apply(ssr);
4600
- if (waitOn) {
4601
- if (!options.allowPromises) {
4602
- throw qError(QError.promisesNotExpected);
4603
- }
4604
- waitOn.then(drain, rejectDrain);
4605
- return;
4850
+ stack.push(await stack.pop());
4851
+ continue;
4606
4852
  }
4853
+ await value.apply(ssr);
4607
4854
  continue;
4608
4855
  }
4609
4856
  processJSXNode(ssr, enqueue, value, {
@@ -4611,12 +4858,8 @@
4611
4858
  parentComponentFrame: options.parentComponentFrame,
4612
4859
  });
4613
4860
  }
4614
- if (stack.length === 0 && options.allowPromises) {
4615
- resolveDrain();
4616
- }
4617
4861
  };
4618
- drain();
4619
- return drained;
4862
+ await drain();
4620
4863
  }
4621
4864
  function processJSXNode(ssr, enqueue, value, options) {
4622
4865
  // console.log('processJSXNode', value);
@@ -4655,7 +4898,6 @@
4655
4898
  enqueue(async () => {
4656
4899
  for await (const chunk of value) {
4657
4900
  await _walkJSX(ssr, chunk, {
4658
- allowPromises: true,
4659
4901
  currentStyleScoped: options.styleScoped,
4660
4902
  parentComponentFrame: options.parentComponentFrame,
4661
4903
  });
@@ -4742,7 +4984,6 @@
4742
4984
  value = generator({
4743
4985
  async write(chunk) {
4744
4986
  await _walkJSX(ssr, chunk, {
4745
- allowPromises: true,
4746
4987
  currentStyleScoped: options.styleScoped,
4747
4988
  parentComponentFrame: options.parentComponentFrame,
4748
4989
  });
@@ -4899,11 +5140,23 @@
4899
5140
  const appendToValue = (valueToAppend) => {
4900
5141
  value = (value == null ? '' : value + '\n') + valueToAppend;
4901
5142
  };
5143
+ const getQrlString = (qrl) => {
5144
+ /**
5145
+ * If there are captures we need to schedule so everything is executed in the right order + qrls
5146
+ * are resolved.
5147
+ *
5148
+ * For internal qrls (starting with `_`) we assume that they do the right thing.
5149
+ */
5150
+ if (!qrl.$symbol$.startsWith('_') && (qrl.$captureRef$ || qrl.$capture$)) {
5151
+ qrl = createQRL(null, '_run', queueQRL, null, null, [qrl]);
5152
+ }
5153
+ return qrlToString(serializationCtx, qrl);
5154
+ };
4902
5155
  if (Array.isArray(qrls)) {
4903
5156
  for (let i = 0; i <= qrls.length; i++) {
4904
5157
  const qrl = qrls[i];
4905
- if (isQrl(qrl)) {
4906
- appendToValue(qrlToString(serializationCtx, qrl));
5158
+ if (isQrl$1(qrl)) {
5159
+ appendToValue(getQrlString(qrl));
4907
5160
  addQwikEventToSerializationContext(serializationCtx, key, qrl);
4908
5161
  }
4909
5162
  else if (qrl != null) {
@@ -4915,8 +5168,8 @@
4915
5168
  }
4916
5169
  }
4917
5170
  }
4918
- else if (isQrl(qrls)) {
4919
- value = qrlToString(serializationCtx, qrls);
5171
+ else if (isQrl$1(qrls)) {
5172
+ value = getQrlString(qrls);
4920
5173
  addQwikEventToSerializationContext(serializationCtx, key, qrls);
4921
5174
  }
4922
5175
  return value;
@@ -4966,7 +5219,7 @@
4966
5219
  *
4967
5220
  * @public
4968
5221
  */
4969
- const version = "2.0.0-alpha.5-dev+cb53bbd";
5222
+ const version = "2.0.0-alpha.7-dev+a26598a";
4970
5223
 
4971
5224
  /** @internal */
4972
5225
  class _SharedContainer {
@@ -4998,13 +5251,6 @@
4998
5251
  }
4999
5252
  }
5000
5253
 
5001
- /** @internal */
5002
- const _CONST_PROPS = Symbol('CONST');
5003
- /** @internal */
5004
- const _VAR_PROPS = Symbol('VAR');
5005
- /** @internal @deprecated v1 compat */
5006
- const _IMMUTABLE = Symbol('IMMUTABLE');
5007
-
5008
5254
  // Keep these properties named like this so they're the same as from wrapSignal
5009
5255
  const getValueProp = (p0) => p0.value;
5010
5256
  const getProp = (p0, p1) => p0[p1];
@@ -5416,7 +5662,7 @@
5416
5662
  else if (isTask(value)) {
5417
5663
  return `Task(${qwikDebugToString(value.$qrl$)})`;
5418
5664
  }
5419
- else if (isQrl$1(value)) {
5665
+ else if (isQrl(value)) {
5420
5666
  return `Qrl(${value.$symbol$})`;
5421
5667
  }
5422
5668
  else if (typeof value === 'object' || typeof value === 'function') {
@@ -5570,14 +5816,21 @@
5570
5816
  CONTEXT_CHAR: /* **** */ ']',
5571
5817
  SEQ_IDX: /* ************ */ 94, // `^` - `q:seqIdx' - Sequential scope id
5572
5818
  SEQ_IDX_CHAR: /* **** */ '^',
5573
- SUBS: /* *************** */ 96, // '`' - `q:subs' - Effect dependencies/subscriptions
5574
- SUBS_CHAR: /* ******* */ '`',
5819
+ BACK_REFS: /* ********** */ 96, // '`' - `q:brefs' - Effect dependencies/subscriptions
5820
+ BACK_REFS_CHAR: /* ** */ '`',
5575
5821
  SEPARATOR: /* ********* */ 124, // `|` - Separator char to encode any key/value pairs.
5576
5822
  SEPARATOR_CHAR: /* ** */ '|',
5577
5823
  SLOT: /* ************** */ 126, // `~` - `q:slot' - Slot name
5578
5824
  SLOT_CHAR: /* ******* */ '~',
5579
5825
  };
5580
5826
 
5827
+ const mergeMaps = (map1, map2) => {
5828
+ for (const [k, v] of map2) {
5829
+ map1.set(k, v);
5830
+ }
5831
+ return map1;
5832
+ };
5833
+
5581
5834
  /**
5582
5835
  * @file
5583
5836
  *
@@ -5833,6 +6086,7 @@
5833
6086
  elementVNode[VNodeProps.flags] ^= VNodeFlags.Inflated;
5834
6087
  const element = elementVNode[ElementVNodeProps.element];
5835
6088
  const attributes = element.attributes;
6089
+ const props = vnode_getProps(elementVNode);
5836
6090
  for (let idx = 0; idx < attributes.length; idx++) {
5837
6091
  const attr = attributes[idx];
5838
6092
  const key = attr.name;
@@ -5843,15 +6097,15 @@
5843
6097
  }
5844
6098
  else if (key.startsWith(QContainerAttr)) {
5845
6099
  if (attr.value === QContainerValue.HTML) {
5846
- mapArray_set(elementVNode, dangerouslySetInnerHTML, element.innerHTML, ElementVNodeProps.PROPS_OFFSET);
6100
+ mapArray_set(props, dangerouslySetInnerHTML, element.innerHTML, 0);
5847
6101
  }
5848
6102
  else if (attr.value === QContainerValue.TEXT && 'value' in element) {
5849
- mapArray_set(elementVNode, 'value', element.value, ElementVNodeProps.PROPS_OFFSET);
6103
+ mapArray_set(props, 'value', element.value, 0);
5850
6104
  }
5851
6105
  }
5852
6106
  else if (!key.startsWith('on:')) {
5853
6107
  const value = attr.value;
5854
- mapArray_set(elementVNode, key, value, ElementVNodeProps.PROPS_OFFSET);
6108
+ mapArray_set(props, key, value, 0);
5855
6109
  }
5856
6110
  }
5857
6111
  }
@@ -6287,54 +6541,16 @@
6287
6541
  journal.length = 0;
6288
6542
  };
6289
6543
  //////////////////////////////////////////////////////////////////////////////////////////////////////
6290
- const mapApp_findIndx = (elementVNode, key, start) => {
6291
- assertTrue(start % 2 === 0, 'Expecting even number.');
6292
- let bottom = start >> 1;
6293
- let top = (elementVNode.length - 2) >> 1;
6294
- while (bottom <= top) {
6295
- const mid = bottom + ((top - bottom) >> 1);
6296
- const midKey = elementVNode[mid << 1];
6297
- if (midKey === key) {
6298
- return mid << 1;
6299
- }
6300
- if (midKey < key) {
6301
- bottom = mid + 1;
6302
- }
6303
- else {
6304
- top = mid - 1;
6305
- }
6306
- }
6307
- return (bottom << 1) ^ -1;
6308
- };
6309
- const mapArray_set = (elementVNode, key, value, start) => {
6310
- const indx = mapApp_findIndx(elementVNode, key, start);
6311
- if (indx >= 0) {
6312
- if (value == null) {
6313
- elementVNode.splice(indx, 2);
6314
- }
6315
- else {
6316
- elementVNode[indx + 1] = value;
6317
- }
6318
- }
6319
- else if (value != null) {
6320
- elementVNode.splice(indx ^ -1, 0, key, value);
6321
- }
6322
- };
6323
- const mapArray_get = (elementVNode, key, start) => {
6324
- const indx = mapApp_findIndx(elementVNode, key, start);
6325
- if (indx >= 0) {
6326
- return elementVNode[indx + 1];
6327
- }
6328
- else {
6329
- return null;
6330
- }
6331
- };
6332
- //////////////////////////////////////////////////////////////////////////////////////////////////////
6333
6544
  const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
6334
6545
  ensureElementOrVirtualVNode(parent);
6335
6546
  if (vnode_isElementVNode(parent)) {
6336
6547
  ensureMaterialized(parent);
6337
6548
  }
6549
+ if (newChild === insertBefore) {
6550
+ // invalid insertBefore. We can't insert before self reference
6551
+ // prevent infinity loop and putting self reference to next sibling
6552
+ insertBefore = null;
6553
+ }
6338
6554
  let adjustedInsertBefore = null;
6339
6555
  if (insertBefore == null) {
6340
6556
  if (vnode_isVirtualVNode(parent)) {
@@ -6458,9 +6674,10 @@
6458
6674
  const elementVNode = ensureElementVNode(vnode);
6459
6675
  let elementName = elementVNode[ElementVNodeProps.elementName];
6460
6676
  if (elementName === undefined) {
6461
- elementName = elementVNode[ElementVNodeProps.elementName] =
6462
- elementVNode[ElementVNodeProps.element].nodeName.toLowerCase();
6463
- elementVNode[VNodeProps.flags] |= vnode_getElementNamespaceFlags(elementName);
6677
+ const element = elementVNode[ElementVNodeProps.element];
6678
+ const nodeName = build.isDev ? fastNodeName(element).toLowerCase() : fastNodeName(element);
6679
+ elementName = elementVNode[ElementVNodeProps.elementName] = nodeName;
6680
+ elementVNode[VNodeProps.flags] |= vnode_getElementNamespaceFlags(element);
6464
6681
  }
6465
6682
  return elementName;
6466
6683
  };
@@ -6642,6 +6859,20 @@
6642
6859
  }
6643
6860
  return node;
6644
6861
  };
6862
+ let _fastNamespaceURI = null;
6863
+ const fastNamespaceURI = (element) => {
6864
+ if (!_fastNamespaceURI) {
6865
+ _fastNamespaceURI = fastGetter(element, 'namespaceURI');
6866
+ }
6867
+ return _fastNamespaceURI.call(element);
6868
+ };
6869
+ let _fastNodeName = null;
6870
+ const fastNodeName = (element) => {
6871
+ if (!_fastNodeName) {
6872
+ _fastNodeName = fastGetter(element, 'nodeName');
6873
+ }
6874
+ return _fastNodeName.call(element);
6875
+ };
6645
6876
  const fastGetter = (prototype, name) => {
6646
6877
  let getter;
6647
6878
  while (prototype && !(getter = Object.getOwnPropertyDescriptor(prototype, name)?.get)) {
@@ -6708,8 +6939,11 @@
6708
6939
  container.$setRawState$(parseInt(id), vParent);
6709
6940
  build.isDev && vnode_setAttr(null, vParent, ELEMENT_ID, id);
6710
6941
  }
6711
- else if (peek() === VNodeDataChar.SUBS) {
6712
- vnode_setProp(vParent, QSubscribers, consumeValue());
6942
+ else if (peek() === VNodeDataChar.BACK_REFS) {
6943
+ if (!container) {
6944
+ container = getDomContainer(vParent[ElementVNodeProps.element]);
6945
+ }
6946
+ setEffectBackRefFromVNodeData(vParent, consumeValue(), container);
6713
6947
  }
6714
6948
  else {
6715
6949
  // prevent infinity loop if there are some characters outside the range
@@ -6719,6 +6953,18 @@
6719
6953
  }
6720
6954
  return vFirstChild;
6721
6955
  };
6956
+ function setEffectBackRefFromVNodeData(vParent, value, container) {
6957
+ const deserializedSubMap = container.$getObjectById$(value);
6958
+ if (!vParent[_EFFECT_BACK_REF]) {
6959
+ Object.defineProperty(vParent, _EFFECT_BACK_REF, {
6960
+ value: deserializedSubMap,
6961
+ });
6962
+ }
6963
+ else {
6964
+ const subMap = vParent[_EFFECT_BACK_REF];
6965
+ mergeMaps(subMap, deserializedSubMap);
6966
+ }
6967
+ }
6722
6968
  const processVNodeData$1 = (vData, callback) => {
6723
6969
  let nextToConsumeIdx = 0;
6724
6970
  let ch = 0;
@@ -6763,8 +7009,9 @@
6763
7009
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6764
7010
  vnode_ensureElementInflated(vnode);
6765
7011
  const keys = [];
6766
- for (let i = vnode_getPropStartIndex(vnode); i < vnode.length; i = i + 2) {
6767
- const key = vnode[i];
7012
+ const props = vnode_getProps(vnode);
7013
+ for (let i = 0; i < props.length; i = i + 2) {
7014
+ const key = props[i];
6768
7015
  if (!key.startsWith(Q_PROPS_SEPARATOR)) {
6769
7016
  keys.push(key);
6770
7017
  }
@@ -6777,22 +7024,23 @@
6777
7024
  const type = vnode[VNodeProps.flags];
6778
7025
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6779
7026
  vnode_ensureElementInflated(vnode);
6780
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7027
+ const props = vnode_getProps(vnode);
7028
+ const idx = mapApp_findIndx(props, key, 0);
6781
7029
  if (idx >= 0) {
6782
- if (vnode[idx + 1] != value && (type & VNodeFlags.Element) !== 0) {
7030
+ if (props[idx + 1] != value && (type & VNodeFlags.Element) !== 0) {
6783
7031
  // Values are different, update DOM
6784
7032
  const element = vnode[ElementVNodeProps.element];
6785
7033
  journal && journal.push(VNodeJournalOpCode.SetAttribute, element, key, value);
6786
7034
  }
6787
7035
  if (value == null) {
6788
- vnode.splice(idx, 2);
7036
+ props.splice(idx, 2);
6789
7037
  }
6790
7038
  else {
6791
- vnode[idx + 1] = value;
7039
+ props[idx + 1] = value;
6792
7040
  }
6793
7041
  }
6794
7042
  else if (value != null) {
6795
- vnode.splice(idx ^ -1, 0, key, value);
7043
+ props.splice(idx ^ -1, 0, key, value);
6796
7044
  if ((type & VNodeFlags.Element) !== 0) {
6797
7045
  // New value, update DOM
6798
7046
  const element = vnode[ElementVNodeProps.element];
@@ -6805,7 +7053,8 @@
6805
7053
  const type = vnode[VNodeProps.flags];
6806
7054
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6807
7055
  vnode_ensureElementInflated(vnode);
6808
- return mapArray_get(vnode, key, vnode_getPropStartIndex(vnode));
7056
+ const props = vnode_getProps(vnode);
7057
+ return mapArray_get(props, key, 0);
6809
7058
  }
6810
7059
  return null;
6811
7060
  };
@@ -6813,11 +7062,12 @@
6813
7062
  const type = vnode[VNodeProps.flags];
6814
7063
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6815
7064
  type & VNodeFlags.Element && vnode_ensureElementInflated(vnode);
6816
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7065
+ const props = vnode_getProps(vnode);
7066
+ const idx = mapApp_findIndx(props, key, 0);
6817
7067
  if (idx >= 0) {
6818
- let value = vnode[idx + 1];
7068
+ let value = props[idx + 1];
6819
7069
  if (typeof value === 'string' && getObject) {
6820
- vnode[idx + 1] = value = getObject(value);
7070
+ props[idx + 1] = value = getObject(value);
6821
7071
  }
6822
7072
  return value;
6823
7073
  }
@@ -6826,12 +7076,13 @@
6826
7076
  };
6827
7077
  const vnode_setProp = (vnode, key, value) => {
6828
7078
  ensureElementOrVirtualVNode(vnode);
6829
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7079
+ const props = vnode_getProps(vnode);
7080
+ const idx = mapApp_findIndx(props, key, 0);
6830
7081
  if (idx >= 0) {
6831
- vnode[idx + 1] = value;
7082
+ props[idx + 1] = value;
6832
7083
  }
6833
7084
  else if (value != null) {
6834
- vnode.splice(idx ^ -1, 0, key, value);
7085
+ props.splice(idx ^ -1, 0, key, value);
6835
7086
  }
6836
7087
  };
6837
7088
  const vnode_getPropStartIndex = (vnode) => {
@@ -6844,6 +7095,9 @@
6844
7095
  }
6845
7096
  throw qError(QError.invalidVNodeType, [type]);
6846
7097
  };
7098
+ const vnode_getProps = (vnode) => {
7099
+ return vnode[vnode_getPropStartIndex(vnode)];
7100
+ };
6847
7101
  const vnode_getParent = (vnode) => {
6848
7102
  return vnode[VNodeProps.parent] || null;
6849
7103
  };
@@ -7007,8 +7261,11 @@
7007
7261
  else if (peek() === VNodeDataChar.SEQ_IDX) {
7008
7262
  vnode_setAttr(null, vParent, ELEMENT_SEQ_IDX, consumeValue());
7009
7263
  }
7010
- else if (peek() === VNodeDataChar.SUBS) {
7011
- vnode_setProp(vParent, QSubscribers, consumeValue());
7264
+ else if (peek() === VNodeDataChar.BACK_REFS) {
7265
+ if (!container) {
7266
+ container = getDomContainer(element);
7267
+ }
7268
+ setEffectBackRefFromVNodeData(vParent, consumeValue(), container);
7012
7269
  }
7013
7270
  else if (peek() === VNodeDataChar.CONTEXT) {
7014
7271
  vnode_setAttr(null, vParent, QCtxAttr, consumeValue());
@@ -7074,60 +7331,6 @@
7074
7331
  throw qError(QError.invalidVNodeType, [type]);
7075
7332
  };
7076
7333
  const isElement = (node) => node && typeof node == 'object' && fastNodeType(node) === /** Node.ELEMENT_NODE* */ 1;
7077
- /// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
7078
- const aPath = [];
7079
- const bPath = [];
7080
- const vnode_documentPosition = (a, b, rootVNode) => {
7081
- if (a === b) {
7082
- return 0;
7083
- }
7084
- let aDepth = -1;
7085
- let bDepth = -1;
7086
- while (a) {
7087
- const vNode = (aPath[++aDepth] = a);
7088
- a = (vNode[VNodeProps.parent] ||
7089
- (rootVNode && vnode_getProp(a, QSlotParent, (id) => vnode_locate(rootVNode, id))));
7090
- }
7091
- while (b) {
7092
- const vNode = (bPath[++bDepth] = b);
7093
- b = (vNode[VNodeProps.parent] ||
7094
- (rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))));
7095
- }
7096
- while (aDepth >= 0 && bDepth >= 0) {
7097
- a = aPath[aDepth];
7098
- b = bPath[bDepth];
7099
- if (a === b) {
7100
- // if the nodes are the same, we need to check the next level.
7101
- aDepth--;
7102
- bDepth--;
7103
- }
7104
- else {
7105
- // We found a difference so we need to scan nodes at this level.
7106
- let cursor = b;
7107
- do {
7108
- cursor = vnode_getNextSibling(cursor);
7109
- if (cursor === a) {
7110
- return 1;
7111
- }
7112
- } while (cursor);
7113
- cursor = b;
7114
- do {
7115
- cursor = vnode_getPreviousSibling(cursor);
7116
- if (cursor === a) {
7117
- return -1;
7118
- }
7119
- } while (cursor);
7120
- if (rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))) {
7121
- // The "b" node is a projection, so we need to set it after "a" node,
7122
- // because the "a" node could be a context provider.
7123
- return -1;
7124
- }
7125
- // The node is not in the list of siblings, that means it must be disconnected.
7126
- return 1;
7127
- }
7128
- }
7129
- return aDepth < bDepth ? -1 : 1;
7130
- };
7131
7334
  /**
7132
7335
  * Use this method to find the parent component for projection.
7133
7336
  *
@@ -7170,23 +7373,20 @@
7170
7373
  };
7171
7374
  const VNodeArray = class VNode extends Array {
7172
7375
  static createElement(flags, parent, previousSibling, nextSibling, firstChild, lastChild, element, elementName) {
7173
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7174
- vnode.push(firstChild, lastChild, element, elementName);
7376
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, firstChild, lastChild, element, elementName, []);
7175
7377
  return vnode;
7176
7378
  }
7177
7379
  static createText(flags, parent, previousSibling, nextSibling, textNode, text) {
7178
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7179
- vnode.push(textNode, text);
7380
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, textNode, text);
7180
7381
  return vnode;
7181
7382
  }
7182
7383
  static createVirtual(flags, parent, previousSibling, nextSibling, firstChild, lastChild) {
7183
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7184
- vnode.push(firstChild, lastChild);
7384
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, firstChild, lastChild, []);
7185
7385
  return vnode;
7186
7386
  }
7187
- constructor(flags, parent, previousSibling, nextSibling) {
7188
- super();
7189
- this.push(flags, parent, previousSibling, nextSibling);
7387
+ constructor(flags, parent, previousSibling, nextSibling, ...rest) {
7388
+ // @ts-expect-error
7389
+ super(flags, parent, previousSibling, nextSibling, ...rest);
7190
7390
  if (build.isDev) {
7191
7391
  this.toString = vnode_toString;
7192
7392
  }
@@ -7251,10 +7451,12 @@
7251
7451
  return returnValue;
7252
7452
  }
7253
7453
  const newInvokeContextFromTuple = ([element, event, url]) => {
7254
- const container = element.closest(QContainerSelector);
7454
+ const domContainer = getDomContainer(element);
7455
+ const container = domContainer.element;
7456
+ const vNode = container ? vnode_locate(domContainer.rootVNode, element) : undefined;
7255
7457
  const locale = container?.getAttribute(QLocaleAttr) || undefined;
7256
7458
  locale && setLocale(locale);
7257
- return newInvokeContext(locale, undefined, element, event, url);
7459
+ return newInvokeContext(locale, vNode, element, event, url);
7258
7460
  };
7259
7461
  // TODO how about putting url and locale (and event/custom?) in to a "static" object
7260
7462
  const newInvokeContext = (locale, hostElement, element, event, url) => {
@@ -7289,16 +7491,14 @@
7289
7491
  * @param property `true` - subscriber is component `false` - subscriber is VNode `string` -
7290
7492
  * subscriber is property
7291
7493
  * @param container
7494
+ * @param data - Additional subscription data
7292
7495
  * @returns
7293
7496
  */
7294
7497
  const trackSignal = (fn, subscriber, property, container, data) => {
7295
7498
  const previousSubscriber = trackInvocation.$effectSubscriber$;
7296
7499
  const previousContainer = trackInvocation.$container$;
7297
7500
  try {
7298
- trackInvocation.$effectSubscriber$ = [subscriber, property];
7299
- if (data) {
7300
- trackInvocation.$effectSubscriber$.push(data);
7301
- }
7501
+ trackInvocation.$effectSubscriber$ = getSubscriber(subscriber, property, data);
7302
7502
  trackInvocation.$container$ = container;
7303
7503
  return invoke(trackInvocation, fn);
7304
7504
  }
@@ -7319,8 +7519,16 @@
7319
7519
  if (iCtx) {
7320
7520
  const hostElement = iCtx.$hostElement$;
7321
7521
  let element = null;
7322
- if (vnode_isVNode(hostElement) && vnode_isElementVNode(hostElement)) {
7323
- element = vnode_getNode(hostElement);
7522
+ if (hostElement != null) {
7523
+ if (vnode_isVNode(hostElement)) {
7524
+ if (vnode_isElementVNode(hostElement)) {
7525
+ element = vnode_getNode(hostElement);
7526
+ }
7527
+ }
7528
+ else {
7529
+ // isSSRnode
7530
+ element = hostElement;
7531
+ }
7324
7532
  }
7325
7533
  return element ?? iCtx.$qrl$?.$setContainer$(undefined);
7326
7534
  }
@@ -7956,9 +8164,9 @@
7956
8164
  $storeProxyMap$ = new WeakMap();
7957
8165
  $qFuncs$;
7958
8166
  $instanceHash$;
7959
- stateData;
8167
+ vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
8168
+ $stateData$;
7960
8169
  $styleIds$ = null;
7961
- $vnodeLocate$ = (id) => vnode_locate(this.rootVNode, id);
7962
8170
  $renderCount$ = 0;
7963
8171
  constructor(element) {
7964
8172
  super(() => this.scheduleRender(), () => vnode_applyJournal(this.$journal$), {}, element.getAttribute('q:locale'));
@@ -7984,29 +8192,29 @@
7984
8192
  this.rootVNode = vnode_newUnMaterializedElement(this.element);
7985
8193
  // These are here to initialize all properties at once for single class transition
7986
8194
  this.$rawStateData$ = null;
7987
- this.stateData = null;
8195
+ this.$stateData$ = null;
7988
8196
  const document = this.element.ownerDocument;
7989
8197
  if (!document.qVNodeData) {
7990
8198
  processVNodeData(document);
7991
8199
  }
7992
8200
  this.$rawStateData$ = [];
7993
- this.stateData = [];
8201
+ this.$stateData$ = [];
7994
8202
  const qwikStates = element.querySelectorAll('script[type="qwik/state"]');
7995
8203
  if (qwikStates.length !== 0) {
7996
8204
  const lastState = qwikStates[qwikStates.length - 1];
7997
8205
  this.$rawStateData$ = JSON.parse(lastState.textContent);
7998
- this.stateData = wrapDeserializerProxy(this, this.$rawStateData$);
8206
+ this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
7999
8207
  }
8000
8208
  this.$qFuncs$ = getQFuncs(document, this.$instanceHash$) || EMPTY_ARRAY;
8001
8209
  }
8002
8210
  $setRawState$(id, vParent) {
8003
- this.stateData[id] = vParent;
8211
+ this.$stateData$[id] = vParent;
8004
8212
  }
8005
8213
  parseQRL(qrl) {
8006
8214
  return inflateQRL(this, parseQRL(qrl));
8007
8215
  }
8008
8216
  handleError(err, host) {
8009
- if (qDev) {
8217
+ if (qDev && host) {
8010
8218
  // Clean vdom
8011
8219
  if (typeof document !== 'undefined') {
8012
8220
  const vHost = host;
@@ -8030,7 +8238,7 @@
8030
8238
  throw err;
8031
8239
  }
8032
8240
  }
8033
- const errorStore = this.resolveContext(host, ERROR_CONTEXT);
8241
+ const errorStore = host && this.resolveContext(host, ERROR_CONTEXT);
8034
8242
  if (!errorStore) {
8035
8243
  throw err;
8036
8244
  }
@@ -8066,7 +8274,7 @@
8066
8274
  vNode =
8067
8275
  vnode_getParent(vNode) ||
8068
8276
  // If virtual node, than it could be a slot so we need to read its parent.
8069
- vnode_getProp(vNode, QSlotParent, this.$vnodeLocate$);
8277
+ vnode_getProp(vNode, QSlotParent, this.vNodeLocate);
8070
8278
  }
8071
8279
  else {
8072
8280
  vNode = vnode_getParent(vNode);
@@ -8086,7 +8294,7 @@
8086
8294
  case ELEMENT_PROPS:
8087
8295
  case OnRenderProp:
8088
8296
  case QCtxAttr:
8089
- case QSubscribers:
8297
+ case QBackRefs:
8090
8298
  getObjectById = this.$getObjectById$;
8091
8299
  break;
8092
8300
  case ELEMENT_SEQ_IDX:
@@ -8099,7 +8307,7 @@
8099
8307
  scheduleRender() {
8100
8308
  this.$renderCount$++;
8101
8309
  this.renderDone ||= getPlatform().nextTick(() => this.processChores());
8102
- return this.renderDone;
8310
+ return this.renderDone.finally(() => emitEvent('qrender', { instanceHash: this.$instanceHash$, renderCount: this.$renderCount$ }));
8103
8311
  }
8104
8312
  processChores() {
8105
8313
  let renderCount = this.$renderCount$;
@@ -8122,12 +8330,13 @@
8122
8330
  ensureProjectionResolved(vNode) {
8123
8331
  if ((vNode[VNodeProps.flags] & VNodeFlags.Resolved) === 0) {
8124
8332
  vNode[VNodeProps.flags] |= VNodeFlags.Resolved;
8125
- for (let i = vnode_getPropStartIndex(vNode); i < vNode.length; i = i + 2) {
8126
- const prop = vNode[i];
8333
+ const props = vnode_getProps(vNode);
8334
+ for (let i = 0; i < props.length; i = i + 2) {
8335
+ const prop = props[i];
8127
8336
  if (isSlotProp(prop)) {
8128
- const value = vNode[i + 1];
8337
+ const value = props[i + 1];
8129
8338
  if (typeof value == 'string') {
8130
- vNode[i + 1] = this.$vnodeLocate$(value);
8339
+ props[i + 1] = this.vNodeLocate(value);
8131
8340
  }
8132
8341
  }
8133
8342
  }
@@ -8138,7 +8347,7 @@
8138
8347
  id = parseFloat(id);
8139
8348
  }
8140
8349
  assertTrue(id < this.$rawStateData$.length / 2, `Invalid reference: ${id} >= ${this.$rawStateData$.length / 2}`);
8141
- return this.stateData[id];
8350
+ return this.$stateData$[id];
8142
8351
  };
8143
8352
  getSyncFn(id) {
8144
8353
  const fn = this.$qFuncs$[id];
@@ -8210,8 +8419,7 @@
8210
8419
  ? parseInt(property, 10)
8211
8420
  : NaN;
8212
8421
  if (Number.isNaN(i) || i < 0 || i >= this.$length$) {
8213
- const out = Reflect.get(target, property, receiver);
8214
- return out;
8422
+ return Reflect.get(target, property, receiver);
8215
8423
  }
8216
8424
  // The serialized data is an array with 2 values for each item
8217
8425
  const idx = i * 2;
@@ -8277,6 +8485,7 @@
8277
8485
  switch (typeId) {
8278
8486
  case TypeIds.Object:
8279
8487
  // We use getters for making complex values lazy
8488
+ // TODO scan the data for computeQRLs and schedule resolve chores
8280
8489
  for (let i = 0; i < data.length; i += 4) {
8281
8490
  const key = deserializeData(container, data[i], data[i + 1]);
8282
8491
  const valType = data[i + 2];
@@ -8316,7 +8525,7 @@
8316
8525
  task.$flags$ = v[1];
8317
8526
  task.$index$ = v[2];
8318
8527
  task.$el$ = v[3];
8319
- task.$effectDependencies$ = v[4];
8528
+ task[_EFFECT_BACK_REF] = v[4];
8320
8529
  task.$state$ = v[5];
8321
8530
  break;
8322
8531
  case TypeIds.Resource:
@@ -8339,12 +8548,9 @@
8339
8548
  break;
8340
8549
  case TypeIds.Store:
8341
8550
  case TypeIds.StoreArray: {
8342
- const [value, flags, effects, storeEffect] = data;
8551
+ const [value, flags, effects] = data;
8343
8552
  const store = getOrCreateStore(value, flags, container);
8344
8553
  const storeHandler = getStoreHandler(store);
8345
- if (storeEffect) {
8346
- effects[STORE_ARRAY_PROP] = storeEffect;
8347
- }
8348
8554
  storeHandler.$effects$ = effects;
8349
8555
  target = store;
8350
8556
  break;
@@ -8353,7 +8559,7 @@
8353
8559
  const signal = target;
8354
8560
  const d = data;
8355
8561
  signal.$untrackedValue$ = d[0];
8356
- signal.$effects$ = d.slice(1);
8562
+ signal.$effects$ = new Set(d.slice(1));
8357
8563
  break;
8358
8564
  }
8359
8565
  case TypeIds.WrappedSignal: {
@@ -8361,10 +8567,10 @@
8361
8567
  const d = data;
8362
8568
  signal.$func$ = container.getSyncFn(d[0]);
8363
8569
  signal.$args$ = d[1];
8364
- signal.$effectDependencies$ = d[2];
8570
+ signal[_EFFECT_BACK_REF] = d[2];
8365
8571
  signal.$untrackedValue$ = d[3];
8366
8572
  signal.$hostElement$ = d[4];
8367
- signal.$effects$ = d.slice(5);
8573
+ signal.$effects$ = new Set(d.slice(5));
8368
8574
  break;
8369
8575
  }
8370
8576
  case TypeIds.ComputedSignal: {
@@ -8484,6 +8690,7 @@
8484
8690
  EMPTY_ARRAY,
8485
8691
  EMPTY_OBJ,
8486
8692
  NEEDS_COMPUTATION,
8693
+ STORE_ARRAY_PROP,
8487
8694
  Slot,
8488
8695
  Fragment,
8489
8696
  NaN,
@@ -8564,6 +8771,8 @@
8564
8771
  reject = rej;
8565
8772
  });
8566
8773
  resolvers.set(promise, [resolve, reject]);
8774
+ // Don't leave unhandled promise rejections
8775
+ promise.catch(() => { });
8567
8776
  return promise;
8568
8777
  case TypeIds.Uint8Array:
8569
8778
  const encodedLength = value.length;
@@ -8573,12 +8782,9 @@
8573
8782
  return new Uint8Array(decodedLength);
8574
8783
  case TypeIds.PropsProxy:
8575
8784
  return createPropsProxy(null, null);
8576
- case TypeIds.RefVNode:
8577
8785
  case TypeIds.VNode:
8578
- const vnodeOrDocument = retrieveVNodeOrDocument(container, value);
8579
- if (typeId === TypeIds.VNode) {
8580
- return vnodeOrDocument;
8581
- }
8786
+ return retrieveVNodeOrDocument(container, value);
8787
+ case TypeIds.RefVNode:
8582
8788
  const vNode = retrieveVNodeOrDocument(container, value);
8583
8789
  if (vnode_isVNode(vNode)) {
8584
8790
  return vnode_getNode(vNode);
@@ -8587,7 +8793,7 @@
8587
8793
  throw qError(QError.serializeErrorExpectedVNode, [typeof vNode]);
8588
8794
  }
8589
8795
  case TypeIds.EffectData:
8590
- return new EffectPropData({});
8796
+ return new SubscriptionData({});
8591
8797
  default:
8592
8798
  throw qError(QError.serializeErrorCannotAllocate, [typeId]);
8593
8799
  }
@@ -8619,7 +8825,7 @@
8619
8825
  assertDefined(backChannel, 'Missing QRL_RUNTIME_CHUNK');
8620
8826
  qrlRef = backChannel.get(symbol);
8621
8827
  }
8622
- return createQRL(chunk, symbol, qrlRef, null, captureIds, null, null);
8828
+ return createQRL(chunk, symbol, qrlRef, null, captureIds, null);
8623
8829
  }
8624
8830
  function inflateQRL(container, qrl) {
8625
8831
  const captureIds = qrl.$capture$;
@@ -8737,7 +8943,7 @@
8737
8943
  /** Visit an object, adding anything that will be serialized as to scan */
8738
8944
  const visit = (obj) => {
8739
8945
  if (typeof obj === 'function') {
8740
- if (isQrl(obj)) {
8946
+ if (isQrl$1(obj)) {
8741
8947
  if (obj.$captureRef$) {
8742
8948
  discoveredValues.push(...obj.$captureRef$);
8743
8949
  }
@@ -8756,6 +8962,7 @@
8756
8962
  obj instanceof RegExp ||
8757
8963
  obj instanceof Uint8Array ||
8758
8964
  obj instanceof URLSearchParams ||
8965
+ vnode_isVNode(obj) ||
8759
8966
  (typeof FormData !== 'undefined' && obj instanceof FormData) ||
8760
8967
  // Ignore the no serialize objects
8761
8968
  fastSkipSerialize(obj)) ;
@@ -8765,8 +8972,7 @@
8765
8972
  else if (isStore(obj)) {
8766
8973
  const target = getStoreTarget(obj);
8767
8974
  const effects = getStoreHandler(obj).$effects$;
8768
- const storeEffect = effects?.[STORE_ARRAY_PROP] ?? null;
8769
- discoveredValues.push(target, effects, storeEffect);
8975
+ discoveredValues.push(target, effects);
8770
8976
  for (const prop in target) {
8771
8977
  const propValue = target[prop];
8772
8978
  if (storeProxyMap.has(propValue)) {
@@ -8800,9 +9006,7 @@
8800
9006
  }
8801
9007
  // WrappedSignal uses syncQrl which has no captured refs
8802
9008
  if (obj instanceof WrappedSignal) {
8803
- if (obj.$effectDependencies$) {
8804
- discoveredValues.push(...obj.$effectDependencies$);
8805
- }
9009
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8806
9010
  if (obj.$args$) {
8807
9011
  discoveredValues.push(...obj.$args$);
8808
9012
  }
@@ -8815,7 +9019,8 @@
8815
9019
  }
8816
9020
  }
8817
9021
  else if (obj instanceof Task) {
8818
- discoveredValues.push(obj.$el$, obj.$qrl$, obj.$state$, obj.$effectDependencies$);
9022
+ discoveredValues.push(obj.$el$, obj.$qrl$, obj.$state$);
9023
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8819
9024
  }
8820
9025
  else if (isSsrNode(obj)) {
8821
9026
  discoverValuesForVNodeData(obj.vnodeData, discoveredValues);
@@ -8834,7 +9039,7 @@
8834
9039
  else if (Array.isArray(obj)) {
8835
9040
  discoveredValues.push(...obj);
8836
9041
  }
8837
- else if (isQrl(obj)) {
9042
+ else if (isQrl$1(obj)) {
8838
9043
  obj.$captureRef$ && obj.$captureRef$.length && discoveredValues.push(...obj.$captureRef$);
8839
9044
  }
8840
9045
  else if (isPropsProxy(obj)) {
@@ -8850,7 +9055,7 @@
8850
9055
  });
8851
9056
  promises.push(obj);
8852
9057
  }
8853
- else if (obj instanceof EffectPropData) {
9058
+ else if (obj instanceof SubscriptionData) {
8854
9059
  discoveredValues.push(obj.data);
8855
9060
  }
8856
9061
  else if (isObjectLiteral(obj)) {
@@ -8895,15 +9100,31 @@
8895
9100
  for (const value of vnodeData) {
8896
9101
  if (isSsrAttrs(value)) {
8897
9102
  for (let i = 1; i < value.length; i += 2) {
8898
- if (value[i - 1] === ELEMENT_KEY) {
9103
+ const attrValue = value[i];
9104
+ if (typeof attrValue === 'string') {
8899
9105
  continue;
8900
9106
  }
8901
- const attrValue = value[i];
8902
9107
  discoveredValues.push(attrValue);
8903
9108
  }
8904
9109
  }
8905
9110
  }
8906
9111
  };
9112
+ const discoverEffectBackRefs = (effectsBackRefs, discoveredValues) => {
9113
+ if (effectsBackRefs) {
9114
+ // We need serialize effect subscriptions with back refs
9115
+ let hasBackRefs = false;
9116
+ for (const [, effect] of effectsBackRefs) {
9117
+ const backRefs = effect[EffectSubscriptionProp.BACK_REF];
9118
+ if (backRefs) {
9119
+ hasBackRefs = true;
9120
+ break;
9121
+ }
9122
+ }
9123
+ if (hasBackRefs) {
9124
+ discoveredValues.push(effectsBackRefs);
9125
+ }
9126
+ }
9127
+ };
8907
9128
  const promiseResults = new WeakMap();
8908
9129
  /**
8909
9130
  * Format:
@@ -8975,7 +9196,7 @@
8975
9196
  else if (value === Fragment) {
8976
9197
  output(TypeIds.Constant, Constants.Fragment);
8977
9198
  }
8978
- else if (isQrl(value)) {
9199
+ else if (isQrl$1(value)) {
8979
9200
  const qrl = qrlToString(serializationContext, value);
8980
9201
  const id = serializationContext.$addRoot$(qrl);
8981
9202
  output(TypeIds.QRL, id);
@@ -9050,6 +9271,9 @@
9050
9271
  else if (value === NEEDS_COMPUTATION) {
9051
9272
  output(TypeIds.Constant, Constants.NEEDS_COMPUTATION);
9052
9273
  }
9274
+ else if (value === STORE_ARRAY_PROP) {
9275
+ output(TypeIds.Constant, Constants.STORE_ARRAY_PROP);
9276
+ }
9053
9277
  else {
9054
9278
  throw qError(QError.serializeErrorUnknownType, [typeof value]);
9055
9279
  }
@@ -9086,7 +9310,7 @@
9086
9310
  : 0;
9087
9311
  output(TypeIds.PropsProxy, out);
9088
9312
  }
9089
- else if (value instanceof EffectPropData) {
9313
+ else if (value instanceof SubscriptionData) {
9090
9314
  output(TypeIds.EffectData, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
9091
9315
  }
9092
9316
  else if (isStore(value)) {
@@ -9097,6 +9321,7 @@
9097
9321
  if (!res) {
9098
9322
  throw qError(QError.serializeErrorUnvisited, ['resource']);
9099
9323
  }
9324
+ // TODO the effects include the resourcereturn which has duplicate data
9100
9325
  output(TypeIds.Resource, [...res, getStoreHandler(value).$effects$]);
9101
9326
  }
9102
9327
  else {
@@ -9104,7 +9329,6 @@
9104
9329
  const storeTarget = getStoreTarget(value);
9105
9330
  const flags = storeHandler.$flags$;
9106
9331
  const effects = storeHandler.$effects$;
9107
- const storeEffect = effects?.[STORE_ARRAY_PROP] ?? null;
9108
9332
  const innerStores = [];
9109
9333
  for (const prop in storeTarget) {
9110
9334
  const propValue = storeTarget[prop];
@@ -9114,7 +9338,7 @@
9114
9338
  serializationContext.$addRoot$(innerStore);
9115
9339
  }
9116
9340
  }
9117
- const out = [storeTarget, flags, effects, storeEffect, ...innerStores];
9341
+ const out = [storeTarget, flags, effects, ...innerStores];
9118
9342
  while (out[out.length - 1] == null) {
9119
9343
  out.pop();
9120
9344
  }
@@ -9153,7 +9377,7 @@
9153
9377
  if (value instanceof WrappedSignal) {
9154
9378
  output(TypeIds.WrappedSignal, [
9155
9379
  ...serializeWrappingFn(serializationContext, value),
9156
- value.$effectDependencies$,
9380
+ filterEffectBackRefs(value[_EFFECT_BACK_REF]),
9157
9381
  v,
9158
9382
  value.$hostElement$,
9159
9383
  ...(value.$effects$ || []),
@@ -9260,7 +9484,7 @@
9260
9484
  value.$flags$,
9261
9485
  value.$index$,
9262
9486
  value.$el$,
9263
- value.$effectDependencies$,
9487
+ value[_EFFECT_BACK_REF],
9264
9488
  value.$state$,
9265
9489
  ];
9266
9490
  while (out[out.length - 1] == null) {
@@ -9283,12 +9507,27 @@
9283
9507
  const out = btoa(buf).replace(/=+$/, '');
9284
9508
  output(TypeIds.Uint8Array, out);
9285
9509
  }
9510
+ else if (vnode_isVNode(value)) {
9511
+ output(TypeIds.Constant, Constants.Undefined);
9512
+ }
9286
9513
  else {
9287
9514
  throw qError(QError.serializeErrorUnknownType, [typeof value]);
9288
9515
  }
9289
9516
  };
9290
9517
  writeValue(serializationContext.$roots$, -1);
9291
9518
  }
9519
+ function filterEffectBackRefs(effectBackRef) {
9520
+ let effectBackRefToSerialize = null;
9521
+ if (effectBackRef) {
9522
+ for (const [effectProp, effect] of effectBackRef) {
9523
+ if (effect[EffectSubscriptionProp.BACK_REF]) {
9524
+ effectBackRefToSerialize ||= new Map();
9525
+ effectBackRefToSerialize.set(effectProp, effect);
9526
+ }
9527
+ }
9528
+ }
9529
+ return effectBackRefToSerialize;
9530
+ }
9292
9531
  function serializeWrappingFn(serializationContext, value) {
9293
9532
  // if value is an object then we need to wrap this in ()
9294
9533
  if (value.$funcStr$ && value.$funcStr$[0] === '{') {
@@ -9301,15 +9540,12 @@
9301
9540
  function qrlToString(serializationContext, value) {
9302
9541
  let symbol = value.$symbol$;
9303
9542
  let chunk = value.$chunk$;
9304
- const refSymbol = value.$refSymbol$ ?? symbol;
9305
9543
  const platform = getPlatform();
9306
9544
  if (platform) {
9307
- const result = platform.chunkForSymbol(refSymbol, chunk, value.dev?.file);
9545
+ const result = platform.chunkForSymbol(symbol, chunk, value.dev?.file);
9308
9546
  if (result) {
9309
9547
  chunk = result[1];
9310
- if (!value.$refSymbol$) {
9311
- symbol = result[0];
9312
- }
9548
+ symbol = result[0];
9313
9549
  }
9314
9550
  }
9315
9551
  const isSync = isSyncQrl(value);
@@ -9486,9 +9722,9 @@
9486
9722
  return ((typeof obj === 'object' &&
9487
9723
  obj !== null &&
9488
9724
  (obj instanceof Signal || obj instanceof Task || isJSXNode(obj))) ||
9489
- isQrl(obj));
9725
+ isQrl$1(obj));
9490
9726
  };
9491
- const canSerialize = (value) => {
9727
+ const canSerialize = (value, seen = new WeakSet()) => {
9492
9728
  if (value == null ||
9493
9729
  typeof value === 'string' ||
9494
9730
  typeof value === 'number' ||
@@ -9497,6 +9733,10 @@
9497
9733
  return true;
9498
9734
  }
9499
9735
  else if (typeof value === 'object') {
9736
+ if (seen.has(value)) {
9737
+ return true;
9738
+ }
9739
+ seen.add(value);
9500
9740
  const proto = Object.getPrototypeOf(value);
9501
9741
  if (isStore(value)) {
9502
9742
  value = getStoreTarget(value);
@@ -9505,7 +9745,7 @@
9505
9745
  for (const key in value) {
9506
9746
  // if the value is a props proxy, then sometimes we could create a component-level subscription,
9507
9747
  // so we should call untrack here to avoid tracking the value
9508
- if (!canSerialize(untrack(() => value[key]))) {
9748
+ if (!canSerialize(untrack(() => value[key]), seen)) {
9509
9749
  return false;
9510
9750
  }
9511
9751
  }
@@ -9513,7 +9753,7 @@
9513
9753
  }
9514
9754
  else if (proto == Array.prototype) {
9515
9755
  for (let i = 0; i < value.length; i++) {
9516
- if (!canSerialize(value[i])) {
9756
+ if (!canSerialize(value[i], seen)) {
9517
9757
  return false;
9518
9758
  }
9519
9759
  }
@@ -9563,7 +9803,7 @@
9563
9803
  }
9564
9804
  }
9565
9805
  else if (typeof value === 'function') {
9566
- if (isQrl(value) || isQwikComponent(value)) {
9806
+ if (isQrl$1(value) || isQwikComponent(value)) {
9567
9807
  return true;
9568
9808
  }
9569
9809
  }
@@ -9616,20 +9856,21 @@
9616
9856
  Constants[Constants["EMPTY_ARRAY"] = 5] = "EMPTY_ARRAY";
9617
9857
  Constants[Constants["EMPTY_OBJ"] = 6] = "EMPTY_OBJ";
9618
9858
  Constants[Constants["NEEDS_COMPUTATION"] = 7] = "NEEDS_COMPUTATION";
9619
- Constants[Constants["Slot"] = 8] = "Slot";
9620
- Constants[Constants["Fragment"] = 9] = "Fragment";
9621
- Constants[Constants["NaN"] = 10] = "NaN";
9622
- Constants[Constants["PositiveInfinity"] = 11] = "PositiveInfinity";
9623
- Constants[Constants["NegativeInfinity"] = 12] = "NegativeInfinity";
9624
- Constants[Constants["MaxSafeInt"] = 13] = "MaxSafeInt";
9859
+ Constants[Constants["STORE_ARRAY_PROP"] = 8] = "STORE_ARRAY_PROP";
9860
+ Constants[Constants["Slot"] = 9] = "Slot";
9861
+ Constants[Constants["Fragment"] = 10] = "Fragment";
9862
+ Constants[Constants["NaN"] = 11] = "NaN";
9863
+ Constants[Constants["PositiveInfinity"] = 12] = "PositiveInfinity";
9864
+ Constants[Constants["NegativeInfinity"] = 13] = "NegativeInfinity";
9865
+ Constants[Constants["MaxSafeInt"] = 14] = "MaxSafeInt";
9625
9866
  // used for close fragment
9626
- Constants[Constants["AlmostMaxSafeInt"] = 14] = "AlmostMaxSafeInt";
9627
- Constants[Constants["MinSafeInt"] = 15] = "MinSafeInt";
9867
+ Constants[Constants["AlmostMaxSafeInt"] = 15] = "AlmostMaxSafeInt";
9868
+ Constants[Constants["MinSafeInt"] = 16] = "MinSafeInt";
9628
9869
  })(Constants || (Constants = {}));
9629
9870
 
9630
9871
  /** @internal */
9631
9872
  const verifySerializable = (value, preMessage) => {
9632
- const seen = new Set();
9873
+ const seen = new WeakSet();
9633
9874
  return _verifySerializable(value, seen, '_', preMessage);
9634
9875
  };
9635
9876
  const _verifySerializable = (value, seen, ctx, preMessage) => {
@@ -9638,10 +9879,12 @@
9638
9879
  return value;
9639
9880
  }
9640
9881
  if (shouldSerialize(unwrapped)) {
9641
- if (seen.has(unwrapped)) {
9642
- return value;
9882
+ if (typeof unwrapped === 'object') {
9883
+ if (seen.has(unwrapped)) {
9884
+ return value;
9885
+ }
9886
+ seen.add(unwrapped);
9643
9887
  }
9644
- seen.add(unwrapped);
9645
9888
  if (isSignal(unwrapped)) {
9646
9889
  return value;
9647
9890
  }
@@ -9745,16 +9988,7 @@
9745
9988
  return input;
9746
9989
  };
9747
9990
 
9748
- const isQrl = (value) => {
9749
- return typeof value === 'function' && typeof value.getSymbol === 'function';
9750
- };
9751
- // Make sure this value is same as value in `platform.ts`
9752
- const SYNC_QRL = '<sync>';
9753
- /** Sync QRL is a function which is serialized into `<script q:func="qwik/json">` tag. */
9754
- const isSyncQrl = (value) => {
9755
- return isQrl(value) && value.$symbol$ == SYNC_QRL;
9756
- };
9757
- const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refSymbol) => {
9991
+ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
9758
9992
  if (qDev && qSerialize) {
9759
9993
  if (captureRef) {
9760
9994
  for (const item of captureRef) {
@@ -9776,9 +10010,14 @@
9776
10010
  };
9777
10011
  function bindFnToContext(currentCtx, beforeFn) {
9778
10012
  // Note that we bind the current `this`
9779
- return (...args) => maybeThen(resolveLazy(), (fn) => {
9780
- if (!isFunction(fn)) {
9781
- throw qError(QError.qrlIsNotFunction);
10013
+ const bound = (...args) => {
10014
+ if (!qrl.resolved) {
10015
+ return qrl.resolve().then((fn) => {
10016
+ if (!isFunction(fn)) {
10017
+ throw qError(QError.qrlIsNotFunction);
10018
+ }
10019
+ return bound(...args);
10020
+ });
9782
10021
  }
9783
10022
  if (beforeFn && beforeFn() === false) {
9784
10023
  return;
@@ -9791,13 +10030,14 @@
9791
10030
  context.$qrl$ = qrl;
9792
10031
  context.$event$ ||= this;
9793
10032
  try {
9794
- return invoke.call(this, context, fn, ...args);
10033
+ return invoke.call(this, context, symbolRef, ...args);
9795
10034
  }
9796
10035
  finally {
9797
10036
  context.$qrl$ = prevQrl;
9798
10037
  context.$event$ = prevEvent;
9799
10038
  }
9800
- });
10039
+ };
10040
+ return bound;
9801
10041
  }
9802
10042
  const resolveLazy = (containerEl) => {
9803
10043
  return symbolRef !== null ? symbolRef : resolve(containerEl);
@@ -9809,8 +10049,20 @@
9809
10049
  }
9810
10050
  return function (...args) {
9811
10051
  let context = tryGetInvokeContext();
10052
+ // use the given qrl if it is the right one
9812
10053
  if (context) {
9813
- return fn.apply(this, args);
10054
+ // TODO check if this is necessary in production
10055
+ if (context.$qrl$?.$symbol$ === qrl.$symbol$) {
10056
+ return fn.apply(this, args);
10057
+ }
10058
+ const prevQrl = context.$qrl$;
10059
+ context.$qrl$ = qrl;
10060
+ try {
10061
+ return fn.apply(this, args);
10062
+ }
10063
+ finally {
10064
+ context.$qrl$ = prevQrl;
10065
+ }
9814
10066
  }
9815
10067
  context = newInvokeContext();
9816
10068
  context.$qrl$ = qrl;
@@ -9838,11 +10090,11 @@
9838
10090
  const start = now();
9839
10091
  const ctx = tryGetInvokeContext();
9840
10092
  if (symbolFn !== null) {
9841
- symbolRef = symbolFn().then((module) => (qrl.resolved = symbolRef = wrapFn(module[symbol])));
10093
+ symbolRef = symbolFn().then((module) => (qrl.resolved = wrapFn((symbolRef = module[symbol]))));
9842
10094
  }
9843
10095
  else {
9844
10096
  const imported = getPlatform().importSymbol(_containerEl, chunk, symbol);
9845
- symbolRef = maybeThen(imported, (ref) => (qrl.resolved = symbolRef = wrapFn(ref)));
10097
+ symbolRef = maybeThen(imported, (ref) => (qrl.resolved = wrapFn((symbolRef = ref))));
9846
10098
  }
9847
10099
  if (typeof symbolRef === 'object' && isPromise(symbolRef)) {
9848
10100
  symbolRef.then(() => emitUsedSymbol(symbol, ctx?.$element$, start), (err) => {
@@ -9865,10 +10117,9 @@
9865
10117
  return invoke;
9866
10118
  }
9867
10119
  };
9868
- const resolvedSymbol = refSymbol ?? symbol;
9869
- const hash = getSymbolHash(resolvedSymbol);
10120
+ const hash = getSymbolHash(symbol);
9870
10121
  Object.assign(qrl, {
9871
- getSymbol: () => resolvedSymbol,
10122
+ getSymbol: () => symbol,
9872
10123
  getHash: () => hash,
9873
10124
  getCaptured: () => captureRef,
9874
10125
  resolve,
@@ -9876,7 +10127,6 @@
9876
10127
  $setContainer$: setContainer,
9877
10128
  $chunk$: chunk,
9878
10129
  $symbol$: symbol,
9879
- $refSymbol$: refSymbol,
9880
10130
  $hash$: hash,
9881
10131
  getFn: bindFnToContext,
9882
10132
  $capture$: capture,
@@ -9885,8 +10135,8 @@
9885
10135
  resolved: undefined,
9886
10136
  });
9887
10137
  if (symbolRef) {
9888
- // Replace symbolRef with (a promise for) the value or wrapped function
9889
- symbolRef = maybeThen(symbolRef, (resolved) => (qrl.resolved = symbolRef = wrapFn(resolved)));
10138
+ // Unwrap any promises
10139
+ symbolRef = maybeThen(symbolRef, (resolved) => (qrl.resolved = wrapFn((symbolRef = resolved))));
9890
10140
  }
9891
10141
  if (build.isDev) {
9892
10142
  Object.defineProperty(qrl, '_devOnlySymbolRef', {
@@ -9900,20 +10150,6 @@
9900
10150
  }
9901
10151
  return qrl;
9902
10152
  };
9903
- const getSymbolHash = (symbolName) => {
9904
- const index = symbolName.lastIndexOf('_');
9905
- if (index > -1) {
9906
- return symbolName.slice(index + 1);
9907
- }
9908
- return symbolName;
9909
- };
9910
- function assertQrl(qrl) {
9911
- if (qDev) {
9912
- if (!isQrl(qrl)) {
9913
- throw new Error('Not a QRL');
9914
- }
9915
- }
9916
- }
9917
10153
  const EMITTED = /*#__PURE__*/ new Set();
9918
10154
  const emitUsedSymbol = (symbol, element, reqTime) => {
9919
10155
  if (!EMITTED.has(symbol)) {
@@ -10026,7 +10262,7 @@
10026
10262
  if (!qRuntimeQrl && qDev) {
10027
10263
  throw new Error('Optimizer should replace all usages of $() with some special syntax. If you need to create a QRL manually, use inlinedQrl() instead.');
10028
10264
  }
10029
- return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null, null);
10265
+ return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null);
10030
10266
  };
10031
10267
  /** @private Use To avoid optimizer replacement */
10032
10268
  const dollar = $;
@@ -10052,7 +10288,7 @@
10052
10288
  // eslint-disable-next-line no-new-func
10053
10289
  fn = new Function('return ' + fn.toString())();
10054
10290
  }
10055
- return createQRL('', SYNC_QRL, fn, null, null, null, null);
10291
+ return createQRL('', SYNC_QRL, fn, null, null, null);
10056
10292
  };
10057
10293
  /**
10058
10294
  * Extract function into a synchronously loadable QRL.
@@ -10069,7 +10305,7 @@
10069
10305
  serializedFn = fn.toString();
10070
10306
  }
10071
10307
  fn.serialized = serializedFn;
10072
- return createQRL('', SYNC_QRL, fn, null, null, null, null);
10308
+ return createQRL('', SYNC_QRL, fn, null, null, null);
10073
10309
  };
10074
10310
 
10075
10311
  /** @internal */
@@ -10788,6 +11024,21 @@
10788
11024
  iCtx.$container$.$scheduler$(ChoreType.VISIBLE, task);
10789
11025
  }
10790
11026
  };
11027
+ const useRunTask = (task, eagerness) => {
11028
+ if (eagerness === 'intersection-observer') {
11029
+ useOn('qvisible', getTaskHandlerQrl(task));
11030
+ }
11031
+ else if (eagerness === 'document-ready') {
11032
+ useOnDocument('qinit', getTaskHandlerQrl(task));
11033
+ }
11034
+ else if (eagerness === 'document-idle') {
11035
+ useOnDocument('qidle', getTaskHandlerQrl(task));
11036
+ }
11037
+ };
11038
+ const getTaskHandlerQrl = (task) => {
11039
+ const taskHandler = createQRL(null, '_task', scheduleTask, null, null, [task]);
11040
+ return taskHandler;
11041
+ };
10791
11042
 
10792
11043
  // <docs markdown="../readme.md#useResource">
10793
11044
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
@@ -11124,8 +11375,9 @@
11124
11375
  exports.Slot = Slot;
11125
11376
  exports._CONST_PROPS = _CONST_PROPS;
11126
11377
  exports._DomContainer = DomContainer;
11378
+ exports._EFFECT_BACK_REF = _EFFECT_BACK_REF;
11127
11379
  exports._EMPTY_ARRAY = EMPTY_ARRAY;
11128
- exports._EffectData = EffectPropData;
11380
+ exports._EffectData = SubscriptionData;
11129
11381
  exports._IMMUTABLE = _IMMUTABLE;
11130
11382
  exports._SharedContainer = _SharedContainer;
11131
11383
  exports._VAR_PROPS = _VAR_PROPS;
@@ -11135,7 +11387,6 @@
11135
11387
  exports._getContextEvent = _getContextEvent;
11136
11388
  exports._getDomContainer = getDomContainer;
11137
11389
  exports._getQContainerElement = _getQContainerElement;
11138
- exports._hW = _hW;
11139
11390
  exports._isJSXNode = isJSXNode;
11140
11391
  exports._isStringifiable = isStringifiable;
11141
11392
  exports._jsxBranch = _jsxBranch;
@@ -11149,7 +11400,9 @@
11149
11400
  exports._qrlSync = _qrlSync;
11150
11401
  exports._regSymbol = _regSymbol;
11151
11402
  exports._restProps = _restProps;
11403
+ exports._run = queueQRL;
11152
11404
  exports._serialize = _serialize;
11405
+ exports._task = scheduleTask;
11153
11406
  exports._verifySerializable = verifySerializable;
11154
11407
  exports._waitUntilRendered = _waitUntilRendered;
11155
11408
  exports._walkJSX = _walkJSX;