@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.mjs CHANGED
@@ -1,11 +1,11 @@
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
7
7
  */
8
- import { isServer, isDev } from '@qwik.dev/core/build';
8
+ import { isDev, isServer } from '@qwik.dev/core/build';
9
9
  export { isBrowser, isDev, isServer } from '@qwik.dev/core/build';
10
10
 
11
11
  // same as isDev but separate so we can test
@@ -118,8 +118,8 @@ const codeToText = (code, ...parts) => {
118
118
  // Keep one error, one line to make it easier to search for the error message.
119
119
  const MAP = [
120
120
  'Error while serializing class or style attributes', // 0
121
- '', // 1 unused
122
- '', // 2 unused
121
+ 'Scheduler not found', // 1
122
+ 'track() received object, without prop to track', // 2
123
123
  'Only primitive and object literals can be serialized. {{0}}', // 3
124
124
  '', // 4 unused
125
125
  'You can render over a existing q:container. Skipping render().', // 5
@@ -162,13 +162,11 @@ const codeToText = (code, ...parts) => {
162
162
  "Element must have 'q:container' attribute.", // 42
163
163
  'Unknown vnode type {{0}}.', // 43
164
164
  'Materialize error: missing element: {{0}} {{1}} {{2}}', // 44
165
- 'SsrError: {{0}}', // 45
166
- 'Cannot coerce a Signal, use `.value` instead', // 46
167
- 'useComputedSignal$ QRL {{0}} {{1}} returned a Promise', // 47
168
- 'ComputedSignal is read-only', // 48
169
- 'WrappedSignal is read-only', // 49
170
- 'SsrError: Promises not expected here.', // 50
171
- 'Attribute value is unsafe for SSR', // 51
165
+ 'Cannot coerce a Signal, use `.value` instead', // 45
166
+ 'useComputedSignal$ QRL {{0}} {{1}} returned a Promise', // 46
167
+ 'ComputedSignal is read-only', // 47
168
+ 'WrappedSignal is read-only', // 48
169
+ 'Attribute value is unsafe for SSR', // 49
172
170
  ];
173
171
  let text = MAP[code] ?? '';
174
172
  if (parts.length) {
@@ -190,8 +188,8 @@ const codeToText = (code, ...parts) => {
190
188
  var QError;
191
189
  (function (QError) {
192
190
  QError[QError["stringifyClassOrStyle"] = 0] = "stringifyClassOrStyle";
193
- QError[QError["UNUSED_1"] = 1] = "UNUSED_1";
194
- QError[QError["UNUSED_2"] = 2] = "UNUSED_2";
191
+ QError[QError["schedulerNotFound"] = 1] = "schedulerNotFound";
192
+ QError[QError["trackObjectWithoutProp"] = 2] = "trackObjectWithoutProp";
195
193
  QError[QError["verifySerializable"] = 3] = "verifySerializable";
196
194
  QError[QError["UNUSED_4"] = 4] = "UNUSED_4";
197
195
  QError[QError["cannotRenderOverExistingContainer"] = 5] = "cannotRenderOverExistingContainer";
@@ -234,19 +232,153 @@ var QError;
234
232
  QError[QError["elementWithoutContainer"] = 42] = "elementWithoutContainer";
235
233
  QError[QError["invalidVNodeType"] = 43] = "invalidVNodeType";
236
234
  QError[QError["materializeVNodeDataError"] = 44] = "materializeVNodeDataError";
237
- QError[QError["serverHostMismatch"] = 45] = "serverHostMismatch";
238
- QError[QError["cannotCoerceSignal"] = 46] = "cannotCoerceSignal";
239
- QError[QError["computedNotSync"] = 47] = "computedNotSync";
240
- QError[QError["computedReadOnly"] = 48] = "computedReadOnly";
241
- QError[QError["wrappedReadOnly"] = 49] = "wrappedReadOnly";
242
- QError[QError["promisesNotExpected"] = 50] = "promisesNotExpected";
243
- QError[QError["unsafeAttr"] = 51] = "unsafeAttr";
235
+ QError[QError["cannotCoerceSignal"] = 45] = "cannotCoerceSignal";
236
+ QError[QError["computedNotSync"] = 46] = "computedNotSync";
237
+ QError[QError["computedReadOnly"] = 47] = "computedReadOnly";
238
+ QError[QError["wrappedReadOnly"] = 48] = "wrappedReadOnly";
239
+ QError[QError["unsafeAttr"] = 49] = "unsafeAttr";
244
240
  })(QError || (QError = {}));
245
241
  const qError = (code, errorMessageArgs = []) => {
246
242
  const text = codeToText(code, ...errorMessageArgs);
247
243
  return logErrorAndStop(text, ...errorMessageArgs);
248
244
  };
249
245
 
246
+ /** QRL related utilities that you can import without importing all of Qwik. */
247
+ const SYNC_QRL = '<sync>';
248
+ /** Sync QRL is a function which is serialized into `<script q:func="qwik/json">` tag. */
249
+ const isSyncQrl = (value) => {
250
+ return isQrl$1(value) && value.$symbol$ == SYNC_QRL;
251
+ };
252
+ const isQrl$1 = (value) => {
253
+ return typeof value === 'function' && typeof value.getSymbol === 'function';
254
+ };
255
+ function assertQrl(qrl) {
256
+ if (isDev) {
257
+ if (!isQrl$1(qrl)) {
258
+ throw new Error('Not a QRL');
259
+ }
260
+ }
261
+ }
262
+ const getSymbolHash = (symbolName) => {
263
+ const index = symbolName.lastIndexOf('_');
264
+ if (index > -1) {
265
+ return symbolName.slice(index + 1);
266
+ }
267
+ return symbolName;
268
+ };
269
+
270
+ /**
271
+ * A friendly name tag for a VirtualVNode.
272
+ *
273
+ * Theses are used to give a name to a VirtualVNode. This is useful for debugging and testing.
274
+ *
275
+ * The name is only added in development mode and is not included in production builds.
276
+ */
277
+ const DEBUG_TYPE = 'q:type';
278
+ var VirtualType;
279
+ (function (VirtualType) {
280
+ VirtualType["Virtual"] = "V";
281
+ VirtualType["Fragment"] = "F";
282
+ VirtualType["WrappedSignal"] = "S";
283
+ VirtualType["Awaited"] = "A";
284
+ VirtualType["Component"] = "C";
285
+ VirtualType["InlineComponent"] = "I";
286
+ VirtualType["Projection"] = "P";
287
+ })(VirtualType || (VirtualType = {}));
288
+ const START = '\x1b[34m';
289
+ const END = '\x1b[0m';
290
+ const VirtualTypeName = {
291
+ [VirtualType.Virtual]: /* ********* */ START + 'Virtual' + END, //
292
+ [VirtualType.Fragment]: /* ******** */ START + 'Fragment' + END, //
293
+ [VirtualType.WrappedSignal]: /* *** */ START + 'Signal' + END, //
294
+ [VirtualType.Awaited]: /* ********* */ START + 'Awaited' + END, //
295
+ [VirtualType.Component]: /* ******* */ START + 'Component' + END, //
296
+ [VirtualType.InlineComponent]: /* * */ START + 'InlineComponent' + END, //
297
+ [VirtualType.Projection]: /* ****** */ START + 'Projection' + END, //
298
+ };
299
+ var QContainerValue;
300
+ (function (QContainerValue) {
301
+ QContainerValue["PAUSED"] = "paused";
302
+ QContainerValue["RESUMED"] = "resumed";
303
+ // these values below are used in the qwik loader as a plain text for the q:container selector
304
+ // standard dangerouslySetInnerHTML
305
+ QContainerValue["HTML"] = "html";
306
+ // textarea
307
+ QContainerValue["TEXT"] = "text";
308
+ })(QContainerValue || (QContainerValue = {}));
309
+
310
+ /** State factory of the component. */
311
+ const OnRenderProp = 'q:renderFn';
312
+ /** Component style content prefix */
313
+ const ComponentStylesPrefixContent = '⚡️';
314
+ /** `<some-element q:slot="...">` */
315
+ const QSlot = 'q:slot';
316
+ const QSlotParent = ':';
317
+ const QSlotRef = 'q:sref';
318
+ const QSlotS = 'q:s';
319
+ const QStyle = 'q:style';
320
+ const QStyleSelector = 'style[q\\:style]';
321
+ const QStyleSSelector = 'style[q\\:sstyle]';
322
+ const QStylesAllSelector = QStyleSelector + ',' + QStyleSSelector;
323
+ const QScopedStyle = 'q:sstyle';
324
+ const QCtxAttr = 'q:ctx';
325
+ const QBackRefs = 'q:brefs';
326
+ const QFuncsPrefix = 'qFuncs_';
327
+ const getQFuncs = (document, hash) => {
328
+ return document[QFuncsPrefix + hash] || [];
329
+ };
330
+ const QBaseAttr = 'q:base';
331
+ const QLocaleAttr = 'q:locale';
332
+ const QManifestHashAttr = 'q:manifest-hash';
333
+ const QInstanceAttr = 'q:instance';
334
+ const QContainerIsland = 'q:container-island';
335
+ const QContainerIslandEnd = '/' + QContainerIsland;
336
+ const QIgnore = 'q:ignore';
337
+ const QIgnoreEnd = '/' + QIgnore;
338
+ const QContainerAttr = 'q:container';
339
+ const QContainerAttrEnd = '/' + QContainerAttr;
340
+ const QTemplate = 'q:template';
341
+ // the same selector should be inside the qwik loader
342
+ // and the same selector should be inside the qwik router spa-shim and spa-init
343
+ const QContainerSelector = '[q\\:container]:not([q\\:container=' +
344
+ QContainerValue.HTML +
345
+ ']):not([q\\:container=' +
346
+ QContainerValue.TEXT +
347
+ '])';
348
+ const HTML_NS = 'http://www.w3.org/1999/xhtml';
349
+ const SVG_NS = 'http://www.w3.org/2000/svg';
350
+ const MATH_NS = 'http://www.w3.org/1998/Math/MathML';
351
+ const ResourceEvent = 'qResource';
352
+ const RenderEvent = 'qRender';
353
+ const TaskEvent = 'qTask';
354
+ const QDefaultSlot = '';
355
+ /**
356
+ * Attribute to mark that this VNode has a pointer to itself from the `qwik/json` state.
357
+ *
358
+ * As the VNode get materialized the vnode now becomes eligible for mutation. Once the vnode mutates
359
+ * the `VNode` references from the `qwik/json` may become invalid. For this reason, these references
360
+ * need to be eagerly resolved. `VNODE_REF` stores a pointer to "this" vnode. This allows the system
361
+ * to eagerly resolve these pointes as the vnodes are materialized.
362
+ */
363
+ const ELEMENT_ID = 'q:id';
364
+ const ELEMENT_KEY = 'q:key';
365
+ const ELEMENT_PROPS = 'q:props';
366
+ const ELEMENT_SEQ = 'q:seq';
367
+ const ELEMENT_SEQ_IDX = 'q:seqIdx';
368
+ const Q_PREFIX = 'q:';
369
+ /** Non serializable markers - always begins with `:` character */
370
+ const NON_SERIALIZABLE_MARKER_PREFIX = ':';
371
+ const USE_ON_LOCAL = NON_SERIALIZABLE_MARKER_PREFIX + 'on';
372
+ const USE_ON_LOCAL_SEQ_IDX = NON_SERIALIZABLE_MARKER_PREFIX + 'onIdx';
373
+ const USE_ON_LOCAL_FLAGS = NON_SERIALIZABLE_MARKER_PREFIX + 'onFlags';
374
+ // comment nodes
375
+ const FLUSH_COMMENT = 'qkssr-f';
376
+ const STREAM_BLOCK_START_COMMENT = 'qkssr-pu';
377
+ const STREAM_BLOCK_END_COMMENT = 'qkssr-po';
378
+ const Q_PROPS_SEPARATOR = ':';
379
+ const dangerouslySetInnerHTML = 'dangerouslySetInnerHTML';
380
+ const qwikInspectorAttr = 'data-qwik-inspector';
381
+
250
382
  // keep this import from core/build so the cjs build works
251
383
  const createPlatform = () => {
252
384
  return {
@@ -307,7 +439,7 @@ const createPlatform = () => {
307
439
  */
308
440
  const toUrl = (doc, containerEl, url) => {
309
441
  const baseURI = doc.baseURI;
310
- const base = new URL(containerEl.getAttribute('q:base') ?? baseURI, baseURI);
442
+ const base = new URL(containerEl.getAttribute(QBaseAttr) ?? baseURI, baseURI);
311
443
  return new URL(url, base);
312
444
  };
313
445
  let _platform = /*#__PURE__ */ createPlatform();
@@ -386,11 +518,6 @@ const maybeThen = (valueOrPromise, thenFn) => {
386
518
  ? valueOrPromise.then(thenFn, shouldNotError)
387
519
  : thenFn(valueOrPromise);
388
520
  };
389
- const maybeThenPassError = (valueOrPromise, thenFn) => {
390
- return isPromise(valueOrPromise)
391
- ? valueOrPromise.then(thenFn)
392
- : thenFn(valueOrPromise);
393
- };
394
521
  const shouldNotError = (reason) => {
395
522
  throwErrorAndStop(reason);
396
523
  };
@@ -399,15 +526,24 @@ const delay = (timeout) => {
399
526
  setTimeout(resolve, timeout);
400
527
  });
401
528
  };
529
+ // Retries a function that throws a promise.
402
530
  function retryOnPromise(fn, retryCount = 0) {
403
- try {
404
- return fn();
405
- }
406
- catch (e) {
531
+ const retryOrThrow = (e) => {
407
532
  if (isPromise(e) && retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
408
533
  return e.then(retryOnPromise.bind(null, fn, retryCount++));
409
534
  }
410
535
  throw e;
536
+ };
537
+ try {
538
+ const result = fn();
539
+ if (isPromise(result)) {
540
+ // not awaited promise is not caught by try/catch block
541
+ return result.catch((e) => retryOrThrow(e));
542
+ }
543
+ return result;
544
+ }
545
+ catch (e) {
546
+ return retryOrThrow(e);
411
547
  }
412
548
  }
413
549
 
@@ -450,118 +586,6 @@ var VNodeDataFlag;
450
586
  VNodeDataFlag[VNodeDataFlag["SERIALIZE"] = 16] = "SERIALIZE";
451
587
  })(VNodeDataFlag || (VNodeDataFlag = {}));
452
588
 
453
- /**
454
- * A friendly name tag for a VirtualVNode.
455
- *
456
- * Theses are used to give a name to a VirtualVNode. This is useful for debugging and testing.
457
- *
458
- * The name is only added in development mode and is not included in production builds.
459
- */
460
- const DEBUG_TYPE = 'q:type';
461
- var VirtualType;
462
- (function (VirtualType) {
463
- VirtualType["Virtual"] = "V";
464
- VirtualType["Fragment"] = "F";
465
- VirtualType["WrappedSignal"] = "S";
466
- VirtualType["Awaited"] = "A";
467
- VirtualType["Component"] = "C";
468
- VirtualType["InlineComponent"] = "I";
469
- VirtualType["Projection"] = "P";
470
- })(VirtualType || (VirtualType = {}));
471
- const START = '\x1b[34m';
472
- const END = '\x1b[0m';
473
- const VirtualTypeName = {
474
- [VirtualType.Virtual]: /* ********* */ START + 'Virtual' + END, //
475
- [VirtualType.Fragment]: /* ******** */ START + 'Fragment' + END, //
476
- [VirtualType.WrappedSignal]: /* *** */ START + 'Signal' + END, //
477
- [VirtualType.Awaited]: /* ********* */ START + 'Awaited' + END, //
478
- [VirtualType.Component]: /* ******* */ START + 'Component' + END, //
479
- [VirtualType.InlineComponent]: /* * */ START + 'InlineComponent' + END, //
480
- [VirtualType.Projection]: /* ****** */ START + 'Projection' + END, //
481
- };
482
- var QContainerValue;
483
- (function (QContainerValue) {
484
- QContainerValue["PAUSED"] = "paused";
485
- QContainerValue["RESUMED"] = "resumed";
486
- // these values below are used in the qwik loader as a plain text for the q:container selector
487
- // standard dangerouslySetInnerHTML
488
- QContainerValue["HTML"] = "html";
489
- // textarea
490
- QContainerValue["TEXT"] = "text";
491
- })(QContainerValue || (QContainerValue = {}));
492
-
493
- /** State factory of the component. */
494
- const OnRenderProp = 'q:renderFn';
495
- /** Component style content prefix */
496
- const ComponentStylesPrefixContent = '⭐️';
497
- /** `<some-element q:slot="...">` */
498
- const QSlot = 'q:slot';
499
- const QSlotParent = ':';
500
- const QSlotRef = 'q:sref';
501
- const QSlotS = 'q:s';
502
- const QStyle = 'q:style';
503
- const QStyleSelector = 'style[q\\:style]';
504
- const QStyleSSelector = 'style[q\\:sstyle]';
505
- const QStylesAllSelector = QStyleSelector + ',' + QStyleSSelector;
506
- const QScopedStyle = 'q:sstyle';
507
- const QCtxAttr = 'q:ctx';
508
- const QSubscribers = 'q:subs';
509
- const QFuncsPrefix = 'qFuncs_';
510
- const getQFuncs = (document, hash) => {
511
- return document[QFuncsPrefix + hash] || [];
512
- };
513
- const QBaseAttr = 'q:base';
514
- const QLocaleAttr = 'q:locale';
515
- const QManifestHashAttr = 'q:manifest-hash';
516
- const QInstanceAttr = 'q:instance';
517
- const QContainerIsland = 'q:container-island';
518
- const QContainerIslandEnd = '/' + QContainerIsland;
519
- const QIgnore = 'q:ignore';
520
- const QIgnoreEnd = '/' + QIgnore;
521
- const QContainerAttr = 'q:container';
522
- const QContainerAttrEnd = '/' + QContainerAttr;
523
- const QTemplate = 'q:template';
524
- // the same selector should be inside the qwik loader
525
- // and the same selector should be inside the qwik router spa-shim and spa-init
526
- const QContainerSelector = '[q\\:container]:not([q\\:container=' +
527
- QContainerValue.HTML +
528
- ']):not([q\\:container=' +
529
- QContainerValue.TEXT +
530
- '])';
531
- const HTML_NS = 'http://www.w3.org/1999/xhtml';
532
- const SVG_NS = 'http://www.w3.org/2000/svg';
533
- const MATH_NS = 'http://www.w3.org/1998/Math/MathML';
534
- const ResourceEvent = 'qResource';
535
- const RenderEvent = 'qRender';
536
- const TaskEvent = 'qTask';
537
- const QDefaultSlot = '';
538
- /**
539
- * Attribute to mark that this VNode has a pointer to itself from the `qwik/json` state.
540
- *
541
- * As the VNode get materialized the vnode now becomes eligible for mutation. Once the vnode mutates
542
- * the `VNode` references from the `qwik/json` may become invalid. For this reason, these references
543
- * need to be eagerly resolved. `VNODE_REF` stores a pointer to "this" vnode. This allows the system
544
- * to eagerly resolve these pointes as the vnodes are materialized.
545
- */
546
- const ELEMENT_ID = 'q:id';
547
- const ELEMENT_KEY = 'q:key';
548
- const ELEMENT_PROPS = 'q:props';
549
- const ELEMENT_SEQ = 'q:seq';
550
- const ELEMENT_SEQ_IDX = 'q:seqIdx';
551
- const Q_PREFIX = 'q:';
552
- /** Non serializable markers - always begins with `:` character */
553
- const NON_SERIALIZABLE_MARKER_PREFIX = ':';
554
- const USE_ON_LOCAL = NON_SERIALIZABLE_MARKER_PREFIX + 'on';
555
- const USE_ON_LOCAL_SEQ_IDX = NON_SERIALIZABLE_MARKER_PREFIX + 'onIdx';
556
- const USE_ON_LOCAL_FLAGS = NON_SERIALIZABLE_MARKER_PREFIX + 'onFlags';
557
- // comment nodes
558
- const FLUSH_COMMENT = 'qkssr-f';
559
- const STREAM_BLOCK_START_COMMENT = 'qkssr-pu';
560
- const STREAM_BLOCK_END_COMMENT = 'qkssr-po';
561
- const Q_PROPS_SEPARATOR = ':';
562
- const dangerouslySetInnerHTML = 'dangerouslySetInnerHTML';
563
- const qwikInspectorAttr = 'data-qwik-inspector';
564
-
565
589
  let _locale = undefined;
566
590
  /**
567
591
  * Retrieve the current locale.
@@ -611,7 +635,7 @@ function setLocale(locale) {
611
635
  _locale = locale;
612
636
  }
613
637
 
614
- const isQrl$1 = (value) => {
638
+ const isQrl = (value) => {
615
639
  return typeof value === 'function' && typeof value.getSymbol === 'function';
616
640
  };
617
641
 
@@ -683,21 +707,21 @@ const qrl = (chunkOrFn, symbol, lexicalScopeCapture = EMPTY_ARRAY, stackOffset =
683
707
  // Emit event
684
708
  announcedQRL.add(symbol);
685
709
  emitEvent('qprefetch', {
686
- symbols: [getSymbolHash(symbol)],
710
+ symbols: [symbol],
687
711
  bundles: chunk && [chunk],
688
712
  });
689
713
  }
690
714
  // Unwrap subscribers
691
- return createQRL(chunk, symbol, null, symbolFn, null, lexicalScopeCapture, null);
715
+ return createQRL(chunk, symbol, null, symbolFn, null, lexicalScopeCapture);
692
716
  };
693
717
  /** @internal */
694
718
  const inlinedQrl = (symbol, symbolName, lexicalScopeCapture = EMPTY_ARRAY) => {
695
719
  // Unwrap subscribers
696
- return createQRL(null, symbolName, symbol, null, null, lexicalScopeCapture, null);
720
+ return createQRL(null, symbolName, symbol, null, null, lexicalScopeCapture);
697
721
  };
698
722
  /** @internal */
699
723
  const _noopQrl = (symbolName, lexicalScopeCapture = EMPTY_ARRAY) => {
700
- return createQRL(null, symbolName, null, null, null, lexicalScopeCapture, null);
724
+ return createQRL(null, symbolName, null, null, null, lexicalScopeCapture);
701
725
  };
702
726
  /** @internal */
703
727
  const _noopQrlDEV = (symbolName, opts, lexicalScopeCapture = EMPTY_ARRAY) => {
@@ -726,6 +750,80 @@ const _regSymbol = (symbol, hash) => {
726
750
  return symbol;
727
751
  };
728
752
 
753
+ var ChoreType;
754
+ (function (ChoreType) {
755
+ /// MASKS defining three levels of sorting
756
+ ChoreType[ChoreType["MACRO"] = 240] = "MACRO";
757
+ /* order of elements (not encoded here) */
758
+ ChoreType[ChoreType["MICRO"] = 15] = "MICRO";
759
+ /** Ensure that the QRL promise is resolved before processing next chores in the queue */
760
+ ChoreType[ChoreType["QRL_RESOLVE"] = 1] = "QRL_RESOLVE";
761
+ ChoreType[ChoreType["RUN_QRL"] = 2] = "RUN_QRL";
762
+ ChoreType[ChoreType["TASK"] = 3] = "TASK";
763
+ ChoreType[ChoreType["NODE_DIFF"] = 4] = "NODE_DIFF";
764
+ ChoreType[ChoreType["NODE_PROP"] = 5] = "NODE_PROP";
765
+ ChoreType[ChoreType["COMPONENT"] = 6] = "COMPONENT";
766
+ ChoreType[ChoreType["RECOMPUTE_AND_SCHEDULE_EFFECTS"] = 7] = "RECOMPUTE_AND_SCHEDULE_EFFECTS";
767
+ // Next macro level
768
+ ChoreType[ChoreType["JOURNAL_FLUSH"] = 16] = "JOURNAL_FLUSH";
769
+ // Next macro level
770
+ ChoreType[ChoreType["VISIBLE"] = 32] = "VISIBLE";
771
+ // Next macro level
772
+ ChoreType[ChoreType["CLEANUP_VISIBLE"] = 48] = "CLEANUP_VISIBLE";
773
+ // Next macro level
774
+ ChoreType[ChoreType["WAIT_FOR_ALL"] = 255] = "WAIT_FOR_ALL";
775
+ })(ChoreType || (ChoreType = {}));
776
+
777
+ // <docs markdown="../readme.md#useLexicalScope">
778
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
779
+ // (edit ../readme.md#useLexicalScope instead and run `pnpm docs.sync`)
780
+ /**
781
+ * Used by the Qwik Optimizer to restore the lexically scoped variables.
782
+ *
783
+ * This method should not be present in the application source code.
784
+ *
785
+ * NOTE: `useLexicalScope` method can only be used in the synchronous portion of the callback
786
+ * (before any `await` statements.)
787
+ *
788
+ * @internal
789
+ */
790
+ // </docs>
791
+ const useLexicalScope = () => {
792
+ const context = getInvokeContext();
793
+ let qrl = context.$qrl$;
794
+ if (!qrl) {
795
+ const el = context.$element$;
796
+ assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
797
+ const containerElement = _getQContainerElement(el);
798
+ assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
799
+ const container = getDomContainer(containerElement);
800
+ qrl = container.parseQRL(decodeURIComponent(String(context.$url$)));
801
+ }
802
+ else {
803
+ assertQrl(qrl);
804
+ assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
805
+ }
806
+ return qrl.$captureRef$;
807
+ };
808
+
809
+ /**
810
+ * This is called by qwik-loader to schedule a QRL. It has to be synchronous.
811
+ *
812
+ * @internal
813
+ */
814
+ const queueQRL = (...args) => {
815
+ // This will already check container
816
+ const [runQrl] = useLexicalScope();
817
+ const context = getInvokeContext();
818
+ const hostElement = context.$hostElement$;
819
+ const container = getDomContainer(hostElement);
820
+ const scheduler = container.$scheduler$;
821
+ if (!scheduler) {
822
+ throw qError(QError.schedulerNotFound);
823
+ }
824
+ return scheduler(ChoreType.RUN_QRL, hostElement, runQrl, args);
825
+ };
826
+
729
827
  /**
730
828
  * Allows to project the children of the current component. <Slot/> can only be used within the
731
829
  * context of a component defined with `component$`.
@@ -850,6 +948,47 @@ function isPreventDefault(key) {
850
948
  return key.startsWith('preventdefault:');
851
949
  }
852
950
 
951
+ function getFileLocationFromJsx(jsxDev) {
952
+ if (!jsxDev) {
953
+ return null;
954
+ }
955
+ const sanitizedFileName = jsxDev.fileName?.replace(/\\/g, '/');
956
+ if (sanitizedFileName) {
957
+ return `${sanitizedFileName}:${jsxDev.lineNumber}:${jsxDev.columnNumber}`;
958
+ }
959
+ return null;
960
+ }
961
+
962
+ const styleContent = (styleId) => {
963
+ return ComponentStylesPrefixContent + styleId;
964
+ };
965
+ function hasClassAttr(props) {
966
+ for (const key in props) {
967
+ if (Object.prototype.hasOwnProperty.call(props, key) && isClassAttr(key)) {
968
+ return true;
969
+ }
970
+ }
971
+ return false;
972
+ }
973
+ function isClassAttr(key) {
974
+ return key === 'class' || key === 'className';
975
+ }
976
+ function convertScopedStyleIdsToArray(scopedStyleIds) {
977
+ return scopedStyleIds?.split(' ') ?? null;
978
+ }
979
+ function convertStyleIdsToString(scopedStyleIds) {
980
+ return Array.from(scopedStyleIds).join(' ');
981
+ }
982
+ const addComponentStylePrefix = (styleId) => {
983
+ if (styleId) {
984
+ let idx = 0;
985
+ do {
986
+ styleId = styleId.substring(0, idx) + styleContent(styleId.substring(idx));
987
+ } while ((idx = styleId.indexOf(' ', idx) + 1) !== 0);
988
+ }
989
+ return styleId || null;
990
+ };
991
+
853
992
  /** CSS properties which accept numbers but are not in units of "px". */
854
993
  const unitlessNumbers = new Set([
855
994
  'animationIterationCount',
@@ -1008,36 +1147,6 @@ const styleKey = (qStyles, index) => {
1008
1147
  assertQrl(qStyles);
1009
1148
  return `${hashCode(qStyles.$hash$)}-${index}`;
1010
1149
  };
1011
- const styleContent = (styleId) => {
1012
- return ComponentStylesPrefixContent + styleId;
1013
- };
1014
-
1015
- function hasClassAttr(props) {
1016
- for (const key in props) {
1017
- if (Object.prototype.hasOwnProperty.call(props, key) && isClassAttr(key)) {
1018
- return true;
1019
- }
1020
- }
1021
- return false;
1022
- }
1023
- function isClassAttr(key) {
1024
- return key === 'class' || key === 'className';
1025
- }
1026
- function convertScopedStyleIdsToArray(scopedStyleIds) {
1027
- return scopedStyleIds?.split(' ') ?? null;
1028
- }
1029
- function convertStyleIdsToString(scopedStyleIds) {
1030
- return Array.from(scopedStyleIds).join(' ');
1031
- }
1032
- const addComponentStylePrefix = (styleId) => {
1033
- if (styleId) {
1034
- let idx = 0;
1035
- do {
1036
- styleId = styleId.substring(0, idx) + styleContent(styleId.substring(idx));
1037
- } while ((idx = styleId.indexOf(' ', idx) + 1) !== 0);
1038
- }
1039
- return styleId || null;
1040
- };
1041
1150
 
1042
1151
  const STORE_TARGET = Symbol('store.target');
1043
1152
  const STORE_HANDLER = Symbol('store.handler');
@@ -1182,8 +1291,12 @@ class StoreHandler {
1182
1291
  return Reflect.ownKeys(target);
1183
1292
  }
1184
1293
  getOwnPropertyDescriptor(target, prop) {
1294
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
1185
1295
  if (Array.isArray(target) || typeof prop === 'symbol') {
1186
- return Object.getOwnPropertyDescriptor(target, prop);
1296
+ return descriptor;
1297
+ }
1298
+ if (descriptor && !descriptor.configurable) {
1299
+ return descriptor;
1187
1300
  }
1188
1301
  return {
1189
1302
  enumerable: true,
@@ -1191,39 +1304,185 @@ class StoreHandler {
1191
1304
  };
1192
1305
  }
1193
1306
  }
1194
- function addEffect(target, prop, store, effectSubscriber) {
1195
- const effectsMap = (store.$effects$ ||= {});
1196
- const effects = (Object.prototype.hasOwnProperty.call(effectsMap, prop) && effectsMap[prop]) ||
1197
- (effectsMap[prop] = []);
1307
+ function addEffect(target, prop, store, effectSubscription) {
1308
+ const effectsMap = (store.$effects$ ||= new Map());
1309
+ let effects = effectsMap.get(prop);
1310
+ if (!effects) {
1311
+ effects = new Set();
1312
+ effectsMap.set(prop, effects);
1313
+ }
1198
1314
  // Let's make sure that we have a reference to this effect.
1199
1315
  // Adding reference is essentially adding a subscription, so if the signal
1200
1316
  // changes we know who to notify.
1201
- ensureContainsEffect(effects, effectSubscriber);
1317
+ ensureContainsSubscription(effects, effectSubscription);
1202
1318
  // But when effect is scheduled in needs to be able to know which signals
1203
1319
  // to unsubscribe from. So we need to store the reference from the effect back
1204
1320
  // to this signal.
1205
- ensureContains(effectSubscriber, target);
1206
- // We need to add the subscriber to the effect so that we can clean it up later
1207
- ensureEffectContainsSubscriber(effectSubscriber[EffectSubscriptionsProp.EFFECT], target, store.$container$);
1321
+ ensureContainsBackRef(effectSubscription, target);
1322
+ addQrlToSerializationCtx(effectSubscription, store.$container$);
1208
1323
  }
1209
1324
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
1210
1325
  target[prop] = value;
1211
1326
  triggerEffects(currentStore.$container$, currentStore, getEffects(target, prop, currentStore.$effects$));
1212
1327
  }
1213
1328
  function getEffects(target, prop, storeEffects) {
1214
- let effectsToTrigger = storeEffects
1215
- ? Array.isArray(target)
1216
- ? Object.values(storeEffects).flatMap((effects) => effects)
1217
- : storeEffects[prop]
1218
- : null;
1219
- const storeArrayValue = storeEffects?.[STORE_ARRAY_PROP];
1329
+ let effectsToTrigger;
1330
+ if (storeEffects) {
1331
+ if (Array.isArray(target)) {
1332
+ for (const effects of storeEffects.values()) {
1333
+ effectsToTrigger ||= new Set();
1334
+ for (const effect of effects) {
1335
+ effectsToTrigger.add(effect);
1336
+ }
1337
+ }
1338
+ }
1339
+ else {
1340
+ effectsToTrigger = storeEffects.get(prop);
1341
+ }
1342
+ }
1343
+ const storeArrayValue = storeEffects?.get(STORE_ARRAY_PROP);
1220
1344
  if (storeArrayValue) {
1221
- effectsToTrigger ||= [];
1222
- effectsToTrigger.push(...storeArrayValue);
1345
+ effectsToTrigger ||= new Set();
1346
+ for (const effect of storeArrayValue) {
1347
+ effectsToTrigger.add(effect);
1348
+ }
1349
+ }
1350
+ return effectsToTrigger || null;
1351
+ }
1352
+
1353
+ /**
1354
+ * Special value used to mark that a given signal needs to be computed. This is essentially a
1355
+ * "marked as dirty" flag.
1356
+ */
1357
+ const NEEDS_COMPUTATION = Symbol('invalid');
1358
+ /** @internal */
1359
+ const _EFFECT_BACK_REF = Symbol('backRef');
1360
+
1361
+ /** Class for back reference to the EffectSubscription */
1362
+ class BackRef {
1363
+ [_EFFECT_BACK_REF] = null;
1364
+ }
1365
+ function clearAllEffects(container, consumer) {
1366
+ if (vnode_isVNode(consumer) && vnode_isElementVNode(consumer)) {
1367
+ ensureMaterialized(consumer);
1368
+ }
1369
+ const effects = consumer[_EFFECT_BACK_REF];
1370
+ if (!effects) {
1371
+ return;
1372
+ }
1373
+ for (const [, effect] of effects) {
1374
+ const backRefs = effect[EffectSubscriptionProp.BACK_REF];
1375
+ if (!backRefs) {
1376
+ return;
1377
+ }
1378
+ for (const producer of backRefs) {
1379
+ if (producer instanceof Signal) {
1380
+ clearSignal(container, producer, effect);
1381
+ }
1382
+ else if (container.$storeProxyMap$.has(producer)) {
1383
+ const target = container.$storeProxyMap$.get(producer);
1384
+ const storeHandler = getStoreHandler(target);
1385
+ clearStore(storeHandler, effect);
1386
+ }
1387
+ }
1388
+ }
1389
+ }
1390
+ function clearSignal(container, producer, effect) {
1391
+ const effects = producer.$effects$;
1392
+ if (effects) {
1393
+ effects.delete(effect);
1394
+ }
1395
+ if (producer instanceof WrappedSignal) {
1396
+ producer.$hostElement$ = null;
1397
+ clearAllEffects(container, producer);
1398
+ }
1399
+ }
1400
+ function clearStore(producer, effect) {
1401
+ const effects = producer?.$effects$;
1402
+ if (effects) {
1403
+ for (const propEffects of effects.values()) {
1404
+ propEffects.delete(effect);
1405
+ }
1223
1406
  }
1224
- return effectsToTrigger;
1225
1407
  }
1226
1408
 
1409
+ // <docs markdown="../../readme.md#implicit$FirstArg">
1410
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
1411
+ // (edit ../../readme.md#implicit$FirstArg instead and run `pnpm docs.sync`)
1412
+ /**
1413
+ * Create a `____$(...)` convenience method from `___(...)`.
1414
+ *
1415
+ * It is very common for functions to take a lazy-loadable resource as a first argument. For this
1416
+ * reason, the Qwik Optimizer automatically extracts the first argument from any function which ends
1417
+ * in `$`.
1418
+ *
1419
+ * This means that `foo$(arg0)` and `foo($(arg0))` are equivalent with respect to Qwik Optimizer.
1420
+ * The former is just a shorthand for the latter.
1421
+ *
1422
+ * For example, these function calls are equivalent:
1423
+ *
1424
+ * - `component$(() => {...})` is same as `component($(() => {...}))`
1425
+ *
1426
+ * ```tsx
1427
+ * export function myApi(callback: QRL<() => void>): void {
1428
+ * // ...
1429
+ * }
1430
+ *
1431
+ * export const myApi$ = implicit$FirstArg(myApi);
1432
+ * // type of myApi$: (callback: () => void): void
1433
+ *
1434
+ * // can be used as:
1435
+ * myApi$(() => console.log('callback'));
1436
+ *
1437
+ * // will be transpiled to:
1438
+ * // FILE: <current file>
1439
+ * myApi(qrl('./chunk-abc.js', 'callback'));
1440
+ *
1441
+ * // FILE: chunk-abc.js
1442
+ * export const callback = () => console.log('callback');
1443
+ * ```
1444
+ *
1445
+ * @param fn - A function that should have its first argument automatically `$`.
1446
+ * @public
1447
+ */
1448
+ // </docs>
1449
+ const implicit$FirstArg = (fn) => {
1450
+ return function (first, ...rest) {
1451
+ return fn.call(null, dollar(first), ...rest);
1452
+ };
1453
+ };
1454
+
1455
+ const createSignal$1 = (value) => {
1456
+ return new Signal(null, value);
1457
+ };
1458
+ const createComputedSignal = (qrl) => {
1459
+ throwIfQRLNotResolved(qrl);
1460
+ return new ComputedSignal(null, qrl);
1461
+ };
1462
+
1463
+ /**
1464
+ * Creates a Signal with the given value. If no value is given, the signal is created with
1465
+ * `undefined`.
1466
+ *
1467
+ * @public
1468
+ */
1469
+ const createSignal = createSignal$1;
1470
+ /** @internal */
1471
+ const createComputedQrl = createComputedSignal;
1472
+ /**
1473
+ * Create a computed signal which is calculated from the given QRL. A computed signal is a signal
1474
+ * which is calculated from other signals. When the signals change, the computed signal is
1475
+ * recalculated.
1476
+ *
1477
+ * The QRL must be a function which returns the value of the signal. The function must not have side
1478
+ * effects, and it mus be synchronous.
1479
+ *
1480
+ * If you need the function to be async, use `useSignal` and `useTask$` instead.
1481
+ *
1482
+ * @public
1483
+ */
1484
+ const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedQrl);
1485
+
1227
1486
  /**
1228
1487
  * @internal
1229
1488
  * The storage provider for hooks. Each invocation increases index i. Data is stored in an array.
@@ -1259,272 +1518,66 @@ const useSequentialScope = () => {
1259
1518
  };
1260
1519
  };
1261
1520
 
1262
- class Subscriber {
1263
- $effectDependencies$ = null;
1264
- }
1265
- function isSubscriber(value) {
1266
- return value instanceof Subscriber || value instanceof WrappedSignal;
1267
- }
1268
- function clearVNodeEffectDependencies(container, value) {
1269
- if (vnode_isElementVNode(value)) {
1270
- ensureMaterialized(value);
1271
- }
1272
- const effects = vnode_getProp(value, QSubscribers, container.$getObjectById$);
1273
- if (!effects) {
1274
- return;
1275
- }
1276
- for (let i = effects.length - 1; i >= 0; i--) {
1277
- const subscriber = effects[i];
1278
- clearEffects(subscriber, value, effects, i, container);
1279
- }
1280
- if (effects.length === 0) {
1281
- vnode_setProp(value, QSubscribers, null);
1282
- }
1283
- }
1284
- function clearSubscriberEffectDependencies(container, value) {
1285
- if (value.$effectDependencies$) {
1286
- for (let i = value.$effectDependencies$.length - 1; i >= 0; i--) {
1287
- const subscriber = value.$effectDependencies$[i];
1288
- clearEffects(subscriber, value, value.$effectDependencies$, i, container);
1521
+ function getSubscriber(effect, prop, data) {
1522
+ if (!effect[_EFFECT_BACK_REF]) {
1523
+ if (isServer && isSsrNode(effect)) {
1524
+ effect.setProp(QBackRefs, new Map());
1289
1525
  }
1290
- if (value.$effectDependencies$.length === 0) {
1291
- value.$effectDependencies$ = null;
1526
+ else {
1527
+ effect[_EFFECT_BACK_REF] = new Map();
1292
1528
  }
1293
1529
  }
1294
- }
1295
- function clearEffects(subscriber, value, effectArray, indexToRemove, container) {
1296
- let subscriptionRemoved = false;
1297
- const seenSet = new Set();
1298
- if (subscriber instanceof WrappedSignal) {
1299
- subscriptionRemoved = clearSignalEffects(subscriber, value, seenSet);
1300
- }
1301
- else if (container.$storeProxyMap$.has(subscriber)) {
1302
- const store = container.$storeProxyMap$.get(subscriber);
1303
- const handler = getStoreHandler(store);
1304
- subscriptionRemoved = clearStoreEffects(handler, value);
1530
+ const subMap = effect[_EFFECT_BACK_REF];
1531
+ let sub = subMap.get(prop);
1532
+ if (!sub) {
1533
+ sub = [effect, prop];
1534
+ subMap.set(prop, sub);
1305
1535
  }
1306
- if (subscriptionRemoved) {
1307
- effectArray.splice(indexToRemove, 1);
1536
+ if (data) {
1537
+ sub[EffectSubscriptionProp.DATA] = data;
1308
1538
  }
1539
+ return sub;
1309
1540
  }
1310
- function clearSignalEffects(subscriber, value, seenSet) {
1311
- const effectSubscriptions = subscriber.$effects$;
1312
- let subscriptionRemoved = false;
1313
- if (effectSubscriptions) {
1314
- for (let i = effectSubscriptions.length - 1; i >= 0; i--) {
1315
- const effect = effectSubscriptions[i];
1316
- if (effect[EffectSubscriptionsProp.EFFECT] === value) {
1317
- effectSubscriptions.splice(i, 1);
1318
- subscriptionRemoved = true;
1319
- }
1320
- }
1321
- }
1322
- if (subscriber instanceof WrappedSignal) {
1323
- const hostElement = subscriber.$hostElement$;
1324
- if (hostElement && hostElement === value) {
1325
- subscriber.$hostElement$ = null;
1326
- }
1327
- // clear the effects of the arguments
1328
- const args = subscriber.$args$;
1329
- if (args) {
1330
- clearArgsEffects(args, subscriber, seenSet);
1331
- }
1332
- }
1333
- return subscriptionRemoved;
1334
- }
1335
- function clearStoreEffects(storeHandler, value) {
1336
- const effectSubscriptions = storeHandler.$effects$;
1337
- if (!effectSubscriptions) {
1338
- return false;
1339
- }
1340
- let subscriptionRemoved = false;
1341
- for (const key in effectSubscriptions) {
1342
- const effects = effectSubscriptions[key];
1343
- for (let i = effects.length - 1; i >= 0; i--) {
1344
- const effect = effects[i];
1345
- if (effect[EffectSubscriptionsProp.EFFECT] === value) {
1346
- effects.splice(i, 1);
1347
- subscriptionRemoved = true;
1348
- }
1349
- }
1350
- if (effects.length === 0) {
1351
- delete effectSubscriptions[key];
1352
- }
1353
- }
1354
- return subscriptionRemoved;
1355
- }
1356
- function clearArgsEffects(args, subscriber, seenSet) {
1357
- for (let i = args.length - 1; i >= 0; i--) {
1358
- const arg = args[i];
1359
- clearArgEffect(arg, subscriber, seenSet);
1360
- }
1361
- }
1362
- function clearArgEffect(arg, subscriber, seenSet) {
1363
- if (seenSet.has(arg)) {
1364
- return;
1365
- }
1366
- seenSet.add(arg);
1367
- if (isSignal(arg)) {
1368
- clearSignalEffects(arg, subscriber, seenSet);
1369
- }
1370
- else if (typeof arg === 'object' && arg !== null) {
1371
- if (isStore(arg)) {
1372
- clearStoreEffects(getStoreHandler(arg), subscriber);
1373
- }
1374
- else {
1375
- for (const key in arg) {
1376
- clearArgEffect(arg[key], subscriber, seenSet);
1377
- }
1378
- }
1379
- }
1380
- else if (Array.isArray(arg)) {
1381
- clearArgsEffects(arg, subscriber, seenSet);
1382
- }
1383
- else ;
1541
+ function isSsrNode(value) {
1542
+ return '__brand__' in value && 'currentComponentNode' in value;
1384
1543
  }
1385
1544
 
1545
+ var TaskFlags;
1546
+ (function (TaskFlags) {
1547
+ TaskFlags[TaskFlags["VISIBLE_TASK"] = 1] = "VISIBLE_TASK";
1548
+ TaskFlags[TaskFlags["TASK"] = 2] = "TASK";
1549
+ TaskFlags[TaskFlags["RESOURCE"] = 4] = "RESOURCE";
1550
+ TaskFlags[TaskFlags["DIRTY"] = 8] = "DIRTY";
1551
+ })(TaskFlags || (TaskFlags = {}));
1386
1552
  /** @internal */
1387
- const useResourceQrl = (qrl, opts) => {
1388
- const { val, set, i, iCtx } = useSequentialScope();
1389
- if (val != null) {
1390
- return val;
1553
+ const useTaskQrl = (qrl) => {
1554
+ const { val, set, iCtx, i } = useSequentialScope();
1555
+ if (val) {
1556
+ return;
1391
1557
  }
1392
1558
  assertQrl(qrl);
1559
+ set(1);
1560
+ const task = new Task(TaskFlags.DIRTY | TaskFlags.TASK, i, iCtx.$hostElement$, qrl, undefined, null);
1561
+ // In V2 we add the task to the sequential scope. We need to do this
1562
+ // in order to be able to retrieve it later when the parent element is
1563
+ // deleted and we need to be able to release the task subscriptions.
1564
+ set(task);
1393
1565
  const container = iCtx.$container$;
1394
- const resource = createResourceReturn(container, opts);
1395
- const el = iCtx.$hostElement$;
1396
- const task = new Task(TaskFlags.DIRTY | TaskFlags.RESOURCE, i, el, qrl, resource, null);
1397
- runResource(task, container, iCtx.$hostElement$);
1398
- set(resource);
1399
- return resource;
1400
- };
1401
- // <docs markdown="../readme.md#useResource">
1402
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
1403
- // (edit ../readme.md#useResource instead and run `pnpm docs.sync`)
1404
- /**
1405
- * This method works like an async memoized function that runs whenever some tracked value changes
1406
- * and returns some data.
1407
- *
1408
- * `useResource` however returns immediate a `ResourceReturn` object that contains the data and a
1409
- * state that indicates if the data is available or not.
1410
- *
1411
- * The status can be one of the following:
1412
- *
1413
- * - `pending` - the data is not yet available.
1414
- * - `resolved` - the data is available.
1415
- * - `rejected` - the data is not available due to an error or timeout.
1416
- *
1417
- * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
1418
- * re-throw it (or a new Error), the resource status will never be `rejected`.
1419
- *
1420
- * ### Example
1421
- *
1422
- * Example showing how `useResource` to perform a fetch to request the weather, whenever the input
1423
- * city name changes.
1424
- *
1425
- * ```tsx
1426
- * const Cmp = component$(() => {
1427
- * const cityS = useSignal('');
1428
- *
1429
- * const weatherResource = useResource$(async ({ track, cleanup }) => {
1430
- * const cityName = track(cityS);
1431
- * const abortController = new AbortController();
1432
- * cleanup(() => abortController.abort('cleanup'));
1433
- * const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
1434
- * signal: abortController.signal,
1435
- * });
1436
- * const data = await res.json();
1437
- * return data as { temp: number };
1438
- * });
1439
- *
1440
- * return (
1441
- * <div>
1442
- * <input name="city" bind:value={cityS} />
1443
- * <Resource
1444
- * value={weatherResource}
1445
- * onResolved={(weather) => {
1446
- * return <div>Temperature: {weather.temp}</div>;
1447
- * }}
1448
- * />
1449
- * </div>
1450
- * );
1451
- * });
1452
- * ```
1453
- *
1454
- * @public
1455
- * @see Resource
1456
- * @see ResourceReturn
1457
- */
1458
- // </docs>
1459
- const Resource = (props) => {
1460
- // Resource path
1461
- return _jsxSorted(Fragment, null, null, getResourceValueAsPromise(props), 0, null);
1462
- };
1463
- function getResourceValueAsPromise(props) {
1464
- const resource = props.value;
1465
- if (isResourceReturn(resource)) {
1466
- const isBrowser = !isServerPlatform();
1467
- if (isBrowser) {
1468
- // create a subscription for the resource._state changes
1469
- const state = resource._state;
1470
- if (state === 'pending' && props.onPending) {
1471
- return Promise.resolve().then(useBindInvokeContext(props.onPending));
1472
- }
1473
- else if (state === 'rejected' && props.onRejected) {
1474
- return Promise.resolve(resource._error).then(useBindInvokeContext(props.onRejected));
1475
- }
1476
- else {
1477
- const resolvedValue = untrack(() => resource._resolved);
1478
- if (resolvedValue !== undefined) {
1479
- // resolved, pending without onPending prop or rejected without onRejected prop
1480
- return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved));
1481
- }
1482
- }
1483
- }
1484
- return resource.value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1485
- }
1486
- else if (isPromise(resource)) {
1487
- return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1488
- }
1489
- else if (isSignal(resource)) {
1490
- return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1566
+ const promise = container.$scheduler$(ChoreType.TASK, task);
1567
+ if (isPromise(promise)) {
1568
+ // TODO: should we handle this differently?
1569
+ promise.catch(() => { });
1491
1570
  }
1492
- else {
1493
- return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
1494
- }
1495
- }
1496
- const _createResourceReturn = (opts) => {
1497
- const resource = {
1498
- __brand: 'resource',
1499
- value: undefined,
1500
- loading: isServerPlatform() ? false : true,
1501
- _resolved: undefined,
1502
- _error: undefined,
1503
- _state: 'pending',
1504
- _timeout: opts?.timeout ?? -1,
1505
- _cache: 0,
1506
- };
1507
- return resource;
1508
1571
  };
1509
- const createResourceReturn = (container, opts, initialPromise) => {
1510
- const result = _createResourceReturn(opts);
1511
- result.value = initialPromise;
1512
- return createStore(container, result, StoreFlags.RECURSIVE);
1513
- };
1514
- const isResourceReturn = (obj) => {
1515
- return isObject(obj) && (getStoreTarget(obj) || obj).__brand === 'resource';
1516
- };
1517
- const runResource = (task, container, host) => {
1572
+ const runTask = (task, container, host) => {
1518
1573
  task.$flags$ &= ~TaskFlags.DIRTY;
1519
1574
  cleanupTask(task);
1520
- const iCtx = newInvokeContext(container.$locale$, host, undefined, ResourceEvent);
1575
+ const iCtx = newInvokeContext(container.$locale$, host, undefined, TaskEvent);
1521
1576
  iCtx.$container$ = container;
1522
- const taskFn = task.$qrl$.getFn(iCtx, () => clearSubscriberEffectDependencies(container, task));
1523
- const resource = task.$state$;
1524
- assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
1577
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
1525
1578
  const track = (obj, prop) => {
1526
1579
  const ctx = newInvokeContext();
1527
- ctx.$effectSubscriber$ = [task, EffectProperty.COMPONENT];
1580
+ ctx.$effectSubscriber$ = getSubscriber(task, EffectProperty.COMPONENT);
1528
1581
  ctx.$container$ = container;
1529
1582
  return invoke(ctx, () => {
1530
1583
  if (isFunction(obj)) {
@@ -1537,115 +1590,85 @@ const runResource = (task, container, host) => {
1537
1590
  return obj.value;
1538
1591
  }
1539
1592
  else {
1540
- return obj;
1593
+ throw qError(QError.trackObjectWithoutProp);
1541
1594
  }
1542
1595
  });
1543
1596
  };
1544
1597
  const handleError = (reason) => container.handleError(reason, host);
1545
- const cleanups = [];
1546
- task.$destroy$ = noSerialize(() => {
1547
- cleanups.forEach((fn) => {
1548
- try {
1549
- fn();
1550
- }
1551
- catch (err) {
1552
- handleError(err);
1553
- }
1554
- });
1555
- done = true;
1556
- });
1557
- const resourceTarget = unwrapStore(resource);
1558
- const opts = {
1559
- track,
1560
- cleanup(fn) {
1561
- if (typeof fn === 'function') {
1562
- cleanups.push(fn);
1563
- }
1564
- },
1565
- cache(policy) {
1566
- let milliseconds = 0;
1567
- if (policy === 'immutable') {
1568
- milliseconds = Infinity;
1569
- }
1570
- else {
1571
- milliseconds = policy;
1572
- }
1573
- resource._cache = milliseconds;
1574
- },
1575
- previous: resourceTarget._resolved,
1576
- };
1577
- let resolve;
1578
- let reject;
1579
- let done = false;
1580
- const setState = (resolved, value) => {
1581
- if (!done) {
1582
- done = true;
1583
- if (resolved) {
1584
- done = true;
1585
- resource.loading = false;
1586
- resource._state = 'resolved';
1587
- resource._resolved = value;
1588
- resource._error = undefined;
1589
- resolve(value);
1590
- }
1591
- else {
1592
- done = true;
1593
- resource.loading = false;
1594
- resource._state = 'rejected';
1595
- resource._error = value;
1596
- reject(value);
1598
+ let cleanupFns = null;
1599
+ const cleanup = (fn) => {
1600
+ if (typeof fn == 'function') {
1601
+ if (!cleanupFns) {
1602
+ cleanupFns = [];
1603
+ task.$destroy$ = noSerialize(() => {
1604
+ task.$destroy$ = null;
1605
+ cleanupFns.forEach((fn) => {
1606
+ try {
1607
+ fn();
1608
+ }
1609
+ catch (err) {
1610
+ handleError(err);
1611
+ }
1612
+ });
1613
+ });
1597
1614
  }
1598
- return true;
1615
+ cleanupFns.push(fn);
1599
1616
  }
1600
- return false;
1601
1617
  };
1602
- /**
1603
- * Add cleanup to resolve the resource if we are trying to run the same resource again while the
1604
- * previous one is not resolved yet. The next `runResource` run will call this cleanup
1605
- */
1606
- cleanups.push(() => {
1607
- if (untrack(() => resource.loading) === true) {
1608
- const value = untrack(() => resource._resolved);
1609
- setState(true, value);
1610
- }
1611
- });
1612
- // Execute mutation inside empty invocation
1613
- invoke(iCtx, () => {
1614
- // console.log('RESOURCE.pending: ');
1615
- resource._state = 'pending';
1616
- resource.loading = !isServerPlatform();
1617
- const promise = (resource.value = new Promise((r, re) => {
1618
- resolve = r;
1619
- reject = re;
1620
- }));
1621
- promise.catch(ignoreErrorToPreventNodeFromCrashing);
1622
- });
1623
- const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
1624
- setState(true, value);
1625
- }, (err) => {
1618
+ const taskApi = { track, cleanup };
1619
+ const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
1620
+ // If a Promise is thrown, that means we need to re-run the task.
1626
1621
  if (isPromise(err)) {
1627
- return err.then(() => runResource(task, container, host));
1622
+ return err.then(() => runTask(task, container, host));
1628
1623
  }
1629
1624
  else {
1630
- setState(false, err);
1625
+ throw err;
1631
1626
  }
1632
1627
  });
1633
- const timeout = resourceTarget._timeout;
1634
- if (timeout > 0) {
1635
- return Promise.race([
1636
- promise,
1637
- delay(timeout).then(() => {
1638
- if (setState(false, new Error('timeout'))) {
1639
- cleanupTask(task);
1640
- }
1641
- }),
1642
- ]);
1628
+ return result;
1629
+ };
1630
+ const cleanupTask = (task) => {
1631
+ const destroy = task.$destroy$;
1632
+ if (destroy) {
1633
+ task.$destroy$ = null;
1634
+ try {
1635
+ destroy();
1636
+ }
1637
+ catch (err) {
1638
+ logError(err);
1639
+ }
1643
1640
  }
1644
- return promise;
1645
1641
  };
1646
- const ignoreErrorToPreventNodeFromCrashing = (err) => {
1647
- // ignore error to prevent node from crashing
1648
- // node will crash in promise is rejected and no one is listening to the rejection.
1642
+ class Task extends BackRef {
1643
+ $flags$;
1644
+ $index$;
1645
+ $el$;
1646
+ $qrl$;
1647
+ $state$;
1648
+ $destroy$;
1649
+ constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
1650
+ super();
1651
+ this.$flags$ = $flags$;
1652
+ this.$index$ = $index$;
1653
+ this.$el$ = $el$;
1654
+ this.$qrl$ = $qrl$;
1655
+ this.$state$ = $state$;
1656
+ this.$destroy$ = $destroy$;
1657
+ }
1658
+ }
1659
+ const isTask = (value) => {
1660
+ return value instanceof Task;
1661
+ };
1662
+ /**
1663
+ * Used internally as a qrl event handler to schedule a task.
1664
+ *
1665
+ * @internal
1666
+ */
1667
+ const scheduleTask = (_event, element) => {
1668
+ const [task] = useLexicalScope();
1669
+ const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK;
1670
+ const container = getDomContainer(element);
1671
+ container.$scheduler$(type, task);
1649
1672
  };
1650
1673
 
1651
1674
  /** @file Public types for the client deserialization */
@@ -1723,22 +1746,67 @@ var VirtualVNodeProps;
1723
1746
  VirtualVNodeProps[VirtualVNodeProps["PROPS_OFFSET"] = 6] = "PROPS_OFFSET";
1724
1747
  })(VirtualVNodeProps || (VirtualVNodeProps = {}));
1725
1748
 
1726
- const isForeignObjectElement = (elementName) => elementName.toLowerCase() === 'foreignobject';
1749
+ const mapApp_findIndx = (elementVNode, key, start) => {
1750
+ assertTrue(start % 2 === 0, 'Expecting even number.');
1751
+ let bottom = start >> 1;
1752
+ let top = (elementVNode.length - 2) >> 1;
1753
+ while (bottom <= top) {
1754
+ const mid = bottom + ((top - bottom) >> 1);
1755
+ const midKey = elementVNode[mid << 1];
1756
+ if (midKey === key) {
1757
+ return mid << 1;
1758
+ }
1759
+ if (midKey < key) {
1760
+ bottom = mid + 1;
1761
+ }
1762
+ else {
1763
+ top = mid - 1;
1764
+ }
1765
+ }
1766
+ return (bottom << 1) ^ -1;
1767
+ };
1768
+ const mapArray_set = (elementVNode, key, value, start) => {
1769
+ const indx = mapApp_findIndx(elementVNode, key, start);
1770
+ if (indx >= 0) {
1771
+ if (value == null) {
1772
+ elementVNode.splice(indx, 2);
1773
+ }
1774
+ else {
1775
+ elementVNode[indx + 1] = value;
1776
+ }
1777
+ }
1778
+ else if (value != null) {
1779
+ elementVNode.splice(indx ^ -1, 0, key, value);
1780
+ }
1781
+ };
1782
+ const mapArray_get = (elementVNode, key, start) => {
1783
+ const indx = mapApp_findIndx(elementVNode, key, start);
1784
+ if (indx >= 0) {
1785
+ return elementVNode[indx + 1];
1786
+ }
1787
+ else {
1788
+ return null;
1789
+ }
1790
+ };
1791
+
1792
+ const isForeignObjectElement = (elementName) => {
1793
+ return isDev ? elementName.toLowerCase() === 'foreignobject' : elementName === 'foreignObject';
1794
+ };
1727
1795
  const isSvgElement = (elementName) => elementName === 'svg' || isForeignObjectElement(elementName);
1728
1796
  const isMathElement = (elementName) => elementName === 'math';
1729
1797
  const vnode_isDefaultNamespace = (vnode) => {
1730
1798
  const flags = vnode[VNodeProps.flags];
1731
1799
  return (flags & VNodeFlags.NAMESPACE_MASK) === 0;
1732
1800
  };
1733
- const vnode_getElementNamespaceFlags = (elementName) => {
1734
- if (isSvgElement(elementName)) {
1735
- return VNodeFlags.NS_svg;
1736
- }
1737
- else if (isMathElement(elementName)) {
1738
- return VNodeFlags.NS_math;
1739
- }
1740
- else {
1741
- return VNodeFlags.NS_html;
1801
+ const vnode_getElementNamespaceFlags = (element) => {
1802
+ const namespace = fastNamespaceURI(element);
1803
+ switch (namespace) {
1804
+ case SVG_NS:
1805
+ return VNodeFlags.NS_svg;
1806
+ case MATH_NS:
1807
+ return VNodeFlags.NS_math;
1808
+ default:
1809
+ return VNodeFlags.NS_html;
1742
1810
  }
1743
1811
  };
1744
1812
  function vnode_getDomChildrenWithCorrectNamespacesToInsert(journal, domParentVNode, newChild) {
@@ -2092,19 +2160,21 @@ const useOnEventsSequentialScope = () => {
2092
2160
  * @returns
2093
2161
  */
2094
2162
  const executeComponent = (container, renderHost, subscriptionHost, componentQRL, props) => {
2095
- const iCtx = newInvokeContext(container.$locale$, subscriptionHost, undefined, RenderEvent);
2096
- iCtx.$effectSubscriber$ = [subscriptionHost, EffectProperty.COMPONENT];
2097
- iCtx.$container$ = container;
2163
+ const iCtx = newInvokeContext(container.$locale$, subscriptionHost || undefined, undefined, RenderEvent);
2164
+ if (subscriptionHost) {
2165
+ iCtx.$effectSubscriber$ = getSubscriber(subscriptionHost, EffectProperty.COMPONENT);
2166
+ iCtx.$container$ = container;
2167
+ }
2098
2168
  let componentFn;
2099
2169
  container.ensureProjectionResolved(renderHost);
2100
2170
  let isInlineComponent = false;
2101
2171
  if (componentQRL === null) {
2102
- componentQRL = componentQRL || container.getHostProp(renderHost, OnRenderProp);
2172
+ componentQRL = container.getHostProp(renderHost, OnRenderProp);
2103
2173
  assertDefined(componentQRL, 'No Component found at this location');
2104
2174
  }
2105
- if (isQrl(componentQRL)) {
2175
+ if (isQrl$1(componentQRL)) {
2106
2176
  props = props || container.getHostProp(renderHost, ELEMENT_PROPS) || EMPTY_OBJ;
2107
- if (props && props.children) {
2177
+ if (props.children) {
2108
2178
  delete props.children;
2109
2179
  }
2110
2180
  componentFn = componentQRL.getFn(iCtx);
@@ -2122,18 +2192,16 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2122
2192
  if (!isInlineComponent) {
2123
2193
  container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
2124
2194
  container.setHostProp(renderHost, USE_ON_LOCAL_SEQ_IDX, null);
2125
- if (container.getHostProp(renderHost, ELEMENT_PROPS) !== props) {
2126
- container.setHostProp(renderHost, ELEMENT_PROPS, props);
2127
- }
2195
+ container.setHostProp(renderHost, ELEMENT_PROPS, props);
2128
2196
  }
2129
2197
  if (vnode_isVNode(renderHost)) {
2130
- clearVNodeEffectDependencies(container, renderHost);
2198
+ clearAllEffects(container, renderHost);
2131
2199
  }
2132
2200
  return componentFn(props);
2133
2201
  }, (jsx) => {
2134
2202
  const useOnEvents = container.getHostProp(renderHost, USE_ON_LOCAL);
2135
2203
  if (useOnEvents) {
2136
- return maybeThen(addUseOnEvents(jsx, useOnEvents), () => jsx);
2204
+ return addUseOnEvents(jsx, useOnEvents);
2137
2205
  }
2138
2206
  return jsx;
2139
2207
  }, (err) => {
@@ -2159,6 +2227,7 @@ const executeComponent = (container, renderHost, subscriptionHost, componentQRL,
2159
2227
  */
2160
2228
  function addUseOnEvents(jsx, useOnEvents) {
2161
2229
  const jsxElement = findFirstStringJSX(jsx);
2230
+ let jsxResult = jsx;
2162
2231
  return maybeThen(jsxElement, (jsxElement) => {
2163
2232
  let isInvisibleComponent = false;
2164
2233
  if (!jsxElement) {
@@ -2177,13 +2246,15 @@ function addUseOnEvents(jsx, useOnEvents) {
2177
2246
  if (Object.prototype.hasOwnProperty.call(useOnEvents, key)) {
2178
2247
  if (isInvisibleComponent) {
2179
2248
  if (key === 'onQvisible$') {
2180
- jsxElement = addScriptNodeForInvisibleComponents(jsx);
2249
+ const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2250
+ jsxResult = jsx;
2181
2251
  if (jsxElement) {
2182
2252
  addUseOnEvent(jsxElement, 'document:onQinit$', useOnEvents[key]);
2183
2253
  }
2184
2254
  }
2185
2255
  else if (key.startsWith('document:') || key.startsWith('window:')) {
2186
- jsxElement = addScriptNodeForInvisibleComponents(jsx);
2256
+ const [jsxElement, jsx] = addScriptNodeForInvisibleComponents(jsxResult);
2257
+ jsxResult = jsx;
2187
2258
  if (jsxElement) {
2188
2259
  addUseOnEvent(jsxElement, key, useOnEvents[key]);
2189
2260
  }
@@ -2201,7 +2272,7 @@ function addUseOnEvents(jsx, useOnEvents) {
2201
2272
  }
2202
2273
  }
2203
2274
  }
2204
- return jsxElement;
2275
+ return jsxResult;
2205
2276
  });
2206
2277
  }
2207
2278
  function addUseOnEvent(jsxElement, key, value) {
@@ -2247,6 +2318,9 @@ function addScriptNodeForInvisibleComponents(jsx) {
2247
2318
  type: 'placeholder',
2248
2319
  hidden: '',
2249
2320
  }, null, 3);
2321
+ if (jsx.type === Slot) {
2322
+ return [jsxElement, _jsxSorted(Fragment, null, null, [jsx, jsxElement], 0, null)];
2323
+ }
2250
2324
  if (jsx.children == null) {
2251
2325
  jsx.children = jsxElement;
2252
2326
  }
@@ -2256,15 +2330,23 @@ function addScriptNodeForInvisibleComponents(jsx) {
2256
2330
  else {
2257
2331
  jsx.children = [jsx.children, jsxElement];
2258
2332
  }
2259
- return jsxElement;
2333
+ return [jsxElement, jsx];
2260
2334
  }
2261
2335
  else if (Array.isArray(jsx) && jsx.length) {
2262
2336
  // get first element
2263
- return addScriptNodeForInvisibleComponents(jsx[0]);
2337
+ const [jsxElement, _] = addScriptNodeForInvisibleComponents(jsx[0]);
2338
+ return [jsxElement, jsx];
2264
2339
  }
2265
- return null;
2340
+ return [null, null];
2266
2341
  }
2267
2342
 
2343
+ /** @internal */
2344
+ const _CONST_PROPS = Symbol('CONST');
2345
+ /** @internal */
2346
+ const _VAR_PROPS = Symbol('VAR');
2347
+ /** @internal @deprecated v1 compat */
2348
+ const _IMMUTABLE = Symbol('IMMUTABLE');
2349
+
2268
2350
  function isSlotProp(prop) {
2269
2351
  return !prop.startsWith('q:') && !prop.startsWith(NON_SERIALIZABLE_MARKER_PREFIX);
2270
2352
  }
@@ -2273,12 +2355,24 @@ function isParentSlotProp(prop) {
2273
2355
  }
2274
2356
  /** @internal */
2275
2357
  const _restProps = (props, omit, target = {}) => {
2276
- for (const key in props) {
2358
+ let constPropsTarget = null;
2359
+ const constProps = props[_CONST_PROPS];
2360
+ if (constProps) {
2361
+ for (const key in constProps) {
2362
+ if (!omit.includes(key)) {
2363
+ constPropsTarget ||= {};
2364
+ constPropsTarget[key] = constProps[key];
2365
+ }
2366
+ }
2367
+ }
2368
+ const varPropsTarget = target;
2369
+ const varProps = props[_VAR_PROPS];
2370
+ for (const key in varProps) {
2277
2371
  if (!omit.includes(key)) {
2278
- target[key] = props[key];
2372
+ varPropsTarget[key] = varProps[key];
2279
2373
  }
2280
2374
  }
2281
- return target;
2375
+ return createPropsProxy(varPropsTarget, constPropsTarget);
2282
2376
  };
2283
2377
 
2284
2378
  function escapeHTML(html) {
@@ -2320,17 +2414,6 @@ function escapeHTML(html) {
2320
2414
  }
2321
2415
  }
2322
2416
 
2323
- function getFileLocationFromJsx(jsxDev) {
2324
- if (!jsxDev) {
2325
- return null;
2326
- }
2327
- const sanitizedFileName = jsxDev.fileName?.replace(/\\/g, '/');
2328
- if (sanitizedFileName) {
2329
- return `${sanitizedFileName}:${jsxDev.lineNumber}:${jsxDev.columnNumber}`;
2330
- }
2331
- return null;
2332
- }
2333
-
2334
2417
  const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2335
2418
  let journal = container.$journal$;
2336
2419
  /**
@@ -2349,9 +2432,9 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2349
2432
  /// (Node can be null, if we are at the end of the list.)
2350
2433
  let vCurrent = null;
2351
2434
  /// When we insert new node we start it here so that we can descend into it.
2352
- /// NOTE: it can't be stored in `vCurrent` because `vNewCurrent` is in journal
2435
+ /// NOTE: it can't be stored in `vCurrent` because `vNewNode` is in journal
2353
2436
  /// and is not connected to the tree.
2354
- let vNewNode = null; // TODO: delete, because journal is on vNode, the above comment no longer applies
2437
+ let vNewNode = null;
2355
2438
  /// When elements have keys they can be consumed out of order and therefore we can't use nextSibling.
2356
2439
  /// In such a case this array will contain the elements after the current location.
2357
2440
  /// The array even indices will contains keys and odd indices the vNode.
@@ -2404,7 +2487,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2404
2487
  }
2405
2488
  else if (isSignal(jsxValue)) {
2406
2489
  if (vCurrent) {
2407
- clearVNodeEffectDependencies(container, vCurrent);
2490
+ clearAllEffects(container, vCurrent);
2408
2491
  }
2409
2492
  expectVirtual(VirtualType.WrappedSignal, null);
2410
2493
  descend(trackSignalAndAssignHost(jsxValue, (vNewNode || vCurrent), EffectProperty.VNODE, container), true);
@@ -2619,9 +2702,10 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2619
2702
  };
2620
2703
  const projections = [];
2621
2704
  if (host) {
2705
+ const props = vnode_getProps(host);
2622
2706
  // we need to create empty projections for all the slots to remove unused slots content
2623
- for (let i = vnode_getPropStartIndex(host); i < host.length; i = i + 2) {
2624
- const prop = host[i];
2707
+ for (let i = 0; i < props.length; i = i + 2) {
2708
+ const prop = props[i];
2625
2709
  if (isSlotProp(prop)) {
2626
2710
  const slotName = prop;
2627
2711
  projections.push(slotName);
@@ -2789,10 +2873,19 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2789
2873
  // But we need to mark them so that they don't get pulled into the diff.
2790
2874
  const eventName = getEventNameFromJsxProp(key);
2791
2875
  const scope = getEventNameScopeFromJsxProp(key);
2792
- vnode_setProp(vNewNode, HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
2793
2876
  if (eventName) {
2877
+ vnode_setProp(vNewNode, HANDLER_PREFIX + ':' + scope + ':' + eventName, value);
2794
2878
  registerQwikLoaderEvent(eventName);
2795
2879
  }
2880
+ if (scope) {
2881
+ // add an event attr with empty value for qwikloader element selector.
2882
+ // We don't need value here. For ssr this value is a QRL,
2883
+ // but for CSR value should be just empty
2884
+ const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
2885
+ if (htmlEvent) {
2886
+ vnode_setAttr(journal, vNewNode, htmlEvent, '');
2887
+ }
2888
+ }
2796
2889
  needsQDispatchEventPatch = true;
2797
2890
  continue;
2798
2891
  }
@@ -2805,12 +2898,15 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2805
2898
  value(element);
2806
2899
  continue;
2807
2900
  }
2901
+ else if (value == null) {
2902
+ continue;
2903
+ }
2808
2904
  else {
2809
2905
  throw qError(QError.invalidRefValue, [currentFile]);
2810
2906
  }
2811
2907
  }
2812
2908
  if (isSignal(value)) {
2813
- const signalData = new EffectPropData({
2909
+ const signalData = new SubscriptionData({
2814
2910
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
2815
2911
  $isConst$: true,
2816
2912
  });
@@ -2914,7 +3010,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2914
3010
  let returnValue = false;
2915
3011
  qrls.flat(2).forEach((qrl) => {
2916
3012
  if (qrl) {
2917
- const value = qrl(event, element);
3013
+ const value = container.$scheduler$(ChoreType.RUN_QRL, vNode, qrl, [event, element]);
2918
3014
  returnValue = returnValue || value === true;
2919
3015
  }
2920
3016
  });
@@ -2926,10 +3022,10 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2926
3022
  /** @param tag Returns true if `qDispatchEvent` needs patching */
2927
3023
  function setBulkProps(vnode, srcAttrs, currentFile) {
2928
3024
  vnode_ensureElementInflated(vnode);
2929
- const dstAttrs = vnode;
3025
+ const dstAttrs = vnode_getProps(vnode);
2930
3026
  let srcIdx = 0;
2931
3027
  const srcLength = srcAttrs.length;
2932
- let dstIdx = ElementVNodeProps.PROPS_OFFSET;
3028
+ let dstIdx = 0;
2933
3029
  let dstLength = dstAttrs.length;
2934
3030
  let srcKey = srcIdx < srcLength ? srcAttrs[srcIdx++] : null;
2935
3031
  let dstKey = dstIdx < dstLength ? dstAttrs[dstIdx++] : null;
@@ -2949,12 +3045,15 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2949
3045
  value(element);
2950
3046
  return;
2951
3047
  }
3048
+ else if (value == null) {
3049
+ return;
3050
+ }
2952
3051
  else {
2953
3052
  throw qError(QError.invalidRefValue, [currentFile]);
2954
3053
  }
2955
3054
  }
2956
3055
  if (isSignal(value)) {
2957
- const signalData = new EffectPropData({
3056
+ const signalData = new SubscriptionData({
2958
3057
  $scopedStyleIdPrefix$: scopedStyleIdPrefix,
2959
3058
  $isConst$: false,
2960
3059
  });
@@ -2968,21 +3067,21 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
2968
3067
  };
2969
3068
  const recordJsxEvent = (key, value) => {
2970
3069
  const eventName = getEventNameFromJsxProp(key);
3070
+ const scope = getEventNameScopeFromJsxProp(key);
2971
3071
  if (eventName) {
2972
- const scope = getEventNameScopeFromJsxProp(key);
2973
3072
  record(':' + scope + ':' + eventName, value);
2974
- }
2975
- // add an event attr with empty value for qwikloader element selector.
2976
- // We don't need value here. For ssr this value is a QRL,
2977
- // but for CSR value should be just empty
2978
- const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
2979
- if (htmlEvent) {
2980
- record(htmlEvent, '');
2981
- }
2982
- // register an event for qwik loader
2983
- if (eventName) {
3073
+ // register an event for qwik loader
2984
3074
  registerQwikLoaderEvent(eventName);
2985
3075
  }
3076
+ if (scope) {
3077
+ // add an event attr with empty value for qwikloader element selector.
3078
+ // We don't need value here. For ssr this value is a QRL,
3079
+ // but for CSR value should be just empty
3080
+ const htmlEvent = convertEventNameFromJsxPropToHtmlAttr(key);
3081
+ if (htmlEvent) {
3082
+ record(htmlEvent, '');
3083
+ }
3084
+ }
2986
3085
  };
2987
3086
  while (srcKey !== null || dstKey !== null) {
2988
3087
  if (dstKey?.startsWith(HANDLER_PREFIX) || dstKey?.startsWith(Q_PREFIX)) {
@@ -3178,10 +3277,6 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
3178
3277
  else if (!hashesAreEqual) {
3179
3278
  insertNewComponent(host, componentQRL, jsxProps);
3180
3279
  if (vNewNode) {
3181
- if (host) {
3182
- // TODO(varixo): not sure why we need to copy flags here.
3183
- vNewNode[VNodeProps.flags] = host[VNodeProps.flags];
3184
- }
3185
3280
  host = vNewNode;
3186
3281
  shouldRender = true;
3187
3282
  }
@@ -3234,7 +3329,7 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
3234
3329
  }
3235
3330
  function insertNewComponent(host, componentQRL, jsxProps) {
3236
3331
  if (host) {
3237
- clearVNodeEffectDependencies(container, host);
3332
+ clearAllEffects(container, host);
3238
3333
  }
3239
3334
  vnode_insertBefore(journal, vParent, (vNewNode = vnode_newVirtual()), vCurrent && getInsertBefore());
3240
3335
  const jsxNode = jsxValue;
@@ -3322,8 +3417,8 @@ function propsDiffer(src, dst) {
3322
3417
  if (!src || !dst) {
3323
3418
  return true;
3324
3419
  }
3325
- let srcKeys = removePropsKeys(Object.keys(src), ['children', QSubscribers]);
3326
- let dstKeys = removePropsKeys(Object.keys(dst), ['children', QSubscribers]);
3420
+ let srcKeys = removePropsKeys(Object.keys(src), ['children', QBackRefs]);
3421
+ let dstKeys = removePropsKeys(Object.keys(dst), ['children', QBackRefs]);
3327
3422
  if (srcKeys.length !== dstKeys.length) {
3328
3423
  return true;
3329
3424
  }
@@ -3369,7 +3464,7 @@ function cleanup(container, vNode) {
3369
3464
  do {
3370
3465
  const type = vCursor[VNodeProps.flags];
3371
3466
  if (type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) {
3372
- clearVNodeEffectDependencies(container, vCursor);
3467
+ clearAllEffects(container, vCursor);
3373
3468
  markVNodeAsDeleted(vCursor);
3374
3469
  // Only elements and virtual nodes need to be traversed for children
3375
3470
  if (type & VNodeFlags.Virtual) {
@@ -3379,7 +3474,7 @@ function cleanup(container, vNode) {
3379
3474
  const obj = seq[i];
3380
3475
  if (isTask(obj)) {
3381
3476
  const task = obj;
3382
- clearSubscriberEffectDependencies(container, task);
3477
+ clearAllEffects(container, task);
3383
3478
  if (task.$flags$ & TaskFlags.VISIBLE_TASK) {
3384
3479
  container.$scheduler$(ChoreType.CLEANUP_VISIBLE, task);
3385
3480
  }
@@ -3394,8 +3489,8 @@ function cleanup(container, vNode) {
3394
3489
  vnode_getProp(vCursor, OnRenderProp, null) !== null;
3395
3490
  if (isComponent) {
3396
3491
  // SPECIAL CASE: If we are a component, we need to descend into the projected content and release the content.
3397
- const attrs = vCursor;
3398
- for (let i = VirtualVNodeProps.PROPS_OFFSET; i < attrs.length; i = i + 2) {
3492
+ const attrs = vnode_getProps(vCursor);
3493
+ for (let i = 0; i < attrs.length; i = i + 2) {
3399
3494
  const key = attrs[i];
3400
3495
  if (!isParentSlotProp(key) && isSlotProp(key)) {
3401
3496
  const value = attrs[i + 1];
@@ -3455,130 +3550,420 @@ function cleanup(container, vNode) {
3455
3550
  // We are back where we started, we are done.
3456
3551
  return;
3457
3552
  }
3458
- const vNextParentSibling = vnode_getNextSibling(vParent);
3459
- if (vNextParentSibling) {
3460
- vCursor = vNextParentSibling;
3461
- break;
3553
+ const vNextParentSibling = vnode_getNextSibling(vParent);
3554
+ if (vNextParentSibling) {
3555
+ vCursor = vNextParentSibling;
3556
+ break;
3557
+ }
3558
+ vParent = vnode_getParent(vParent);
3559
+ }
3560
+ if (vParent == null) {
3561
+ // We are done.
3562
+ return;
3563
+ }
3564
+ } while (true);
3565
+ }
3566
+ function cleanupStaleUnclaimedProjection(journal, projection) {
3567
+ // we are removing a node where the projection would go after slot render.
3568
+ // This is not needed, so we need to cleanup still unclaimed projection
3569
+ const projectionParent = vnode_getParent(projection);
3570
+ if (projectionParent) {
3571
+ const projectionParentType = projectionParent[VNodeProps.flags];
3572
+ if (projectionParentType & VNodeFlags.Element &&
3573
+ vnode_getElementName(projectionParent) === QTemplate) {
3574
+ // if parent is the q:template element then projection is still unclaimed - remove it
3575
+ vnode_remove(journal, projectionParent, projection, true);
3576
+ }
3577
+ }
3578
+ }
3579
+ function markVNodeAsDeleted(vCursor) {
3580
+ /**
3581
+ * Marks vCursor as deleted. We need to do this to prevent chores from running after the vnode is
3582
+ * removed. (for example signal subscriptions)
3583
+ */
3584
+ vCursor[VNodeProps.flags] |= VNodeFlags.Deleted;
3585
+ }
3586
+ /**
3587
+ * This marks the property as immutable. It is needed for the QRLs so that QwikLoader can get a hold
3588
+ * of them. This character must be `:` so that the `vnode_getAttr` can ignore them.
3589
+ */
3590
+ const HANDLER_PREFIX = ':';
3591
+ let count = 0;
3592
+ var SiblingsArray;
3593
+ (function (SiblingsArray) {
3594
+ SiblingsArray[SiblingsArray["Name"] = 0] = "Name";
3595
+ SiblingsArray[SiblingsArray["Key"] = 1] = "Key";
3596
+ SiblingsArray[SiblingsArray["VNode"] = 2] = "VNode";
3597
+ SiblingsArray[SiblingsArray["Size"] = 3] = "Size";
3598
+ SiblingsArray[SiblingsArray["NextVNode"] = 5] = "NextVNode";
3599
+ })(SiblingsArray || (SiblingsArray = {}));
3600
+
3601
+ /** @internal */
3602
+ const useResourceQrl = (qrl, opts) => {
3603
+ const { val, set, i, iCtx } = useSequentialScope();
3604
+ if (val != null) {
3605
+ return val;
3606
+ }
3607
+ assertQrl(qrl);
3608
+ const container = iCtx.$container$;
3609
+ const resource = createResourceReturn(container, opts);
3610
+ const el = iCtx.$hostElement$;
3611
+ const task = new Task(TaskFlags.DIRTY | TaskFlags.RESOURCE, i, el, qrl, resource, null);
3612
+ container.$scheduler$(ChoreType.TASK, task);
3613
+ set(resource);
3614
+ return resource;
3615
+ };
3616
+ // <docs markdown="../readme.md#useResource">
3617
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
3618
+ // (edit ../readme.md#useResource instead and run `pnpm docs.sync`)
3619
+ /**
3620
+ * This method works like an async memoized function that runs whenever some tracked value changes
3621
+ * and returns some data.
3622
+ *
3623
+ * `useResource` however returns immediate a `ResourceReturn` object that contains the data and a
3624
+ * state that indicates if the data is available or not.
3625
+ *
3626
+ * The status can be one of the following:
3627
+ *
3628
+ * - `pending` - the data is not yet available.
3629
+ * - `resolved` - the data is available.
3630
+ * - `rejected` - the data is not available due to an error or timeout.
3631
+ *
3632
+ * Be careful when using a `try/catch` statement in `useResource$`. If you catch the error and don't
3633
+ * re-throw it (or a new Error), the resource status will never be `rejected`.
3634
+ *
3635
+ * ### Example
3636
+ *
3637
+ * Example showing how `useResource` to perform a fetch to request the weather, whenever the input
3638
+ * city name changes.
3639
+ *
3640
+ * ```tsx
3641
+ * const Cmp = component$(() => {
3642
+ * const cityS = useSignal('');
3643
+ *
3644
+ * const weatherResource = useResource$(async ({ track, cleanup }) => {
3645
+ * const cityName = track(cityS);
3646
+ * const abortController = new AbortController();
3647
+ * cleanup(() => abortController.abort('cleanup'));
3648
+ * const res = await fetch(`http://weatherdata.com?city=${cityName}`, {
3649
+ * signal: abortController.signal,
3650
+ * });
3651
+ * const data = await res.json();
3652
+ * return data as { temp: number };
3653
+ * });
3654
+ *
3655
+ * return (
3656
+ * <div>
3657
+ * <input name="city" bind:value={cityS} />
3658
+ * <Resource
3659
+ * value={weatherResource}
3660
+ * onResolved={(weather) => {
3661
+ * return <div>Temperature: {weather.temp}</div>;
3662
+ * }}
3663
+ * />
3664
+ * </div>
3665
+ * );
3666
+ * });
3667
+ * ```
3668
+ *
3669
+ * @public
3670
+ * @see Resource
3671
+ * @see ResourceReturn
3672
+ */
3673
+ // </docs>
3674
+ const Resource = (props) => {
3675
+ // Resource path
3676
+ return _jsxSorted(Fragment, null, null, getResourceValueAsPromise(props), 0, null);
3677
+ };
3678
+ function getResourceValueAsPromise(props) {
3679
+ const resource = props.value;
3680
+ if (isResourceReturn(resource) && resource.value) {
3681
+ const isBrowser = !isServerPlatform();
3682
+ if (isBrowser) {
3683
+ // create a subscription for the resource._state changes
3684
+ const state = resource._state;
3685
+ if (state === 'pending' && props.onPending) {
3686
+ return Promise.resolve().then(useBindInvokeContext(props.onPending));
3687
+ }
3688
+ else if (state === 'rejected' && props.onRejected) {
3689
+ return Promise.resolve(resource._error).then(useBindInvokeContext(props.onRejected));
3690
+ }
3691
+ else {
3692
+ const resolvedValue = untrack(() => resource._resolved);
3693
+ if (resolvedValue !== undefined) {
3694
+ // resolved, pending without onPending prop or rejected without onRejected prop
3695
+ return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved));
3696
+ }
3697
+ }
3698
+ }
3699
+ return resource.value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3700
+ }
3701
+ else if (isPromise(resource)) {
3702
+ return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3703
+ }
3704
+ else if (isSignal(resource)) {
3705
+ return Promise.resolve(resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3706
+ }
3707
+ else {
3708
+ return Promise.resolve(resource).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
3709
+ }
3710
+ }
3711
+ const _createResourceReturn = (opts) => {
3712
+ const resource = {
3713
+ __brand: 'resource',
3714
+ value: undefined,
3715
+ loading: isServerPlatform() ? false : true,
3716
+ _resolved: undefined,
3717
+ _error: undefined,
3718
+ _state: 'pending',
3719
+ _timeout: opts?.timeout ?? -1,
3720
+ _cache: 0,
3721
+ };
3722
+ return resource;
3723
+ };
3724
+ const createResourceReturn = (container, opts, initialPromise) => {
3725
+ const result = _createResourceReturn(opts);
3726
+ result.value = initialPromise;
3727
+ return createStore(container, result, StoreFlags.RECURSIVE);
3728
+ };
3729
+ const isResourceReturn = (obj) => {
3730
+ return isObject(obj) && (getStoreTarget(obj) || obj).__brand === 'resource';
3731
+ };
3732
+ const runResource = (task, container, host) => {
3733
+ task.$flags$ &= ~TaskFlags.DIRTY;
3734
+ cleanupTask(task);
3735
+ const iCtx = newInvokeContext(container.$locale$, host, undefined, ResourceEvent);
3736
+ iCtx.$container$ = container;
3737
+ const taskFn = task.$qrl$.getFn(iCtx, () => clearAllEffects(container, task));
3738
+ const resource = task.$state$;
3739
+ assertDefined(resource, 'useResource: when running a resource, "task.resource" must be a defined.', task);
3740
+ const track = (obj, prop) => {
3741
+ const ctx = newInvokeContext();
3742
+ ctx.$effectSubscriber$ = getSubscriber(task, EffectProperty.COMPONENT);
3743
+ ctx.$container$ = container;
3744
+ return invoke(ctx, () => {
3745
+ if (isFunction(obj)) {
3746
+ return obj();
3747
+ }
3748
+ if (prop) {
3749
+ return obj[prop];
3750
+ }
3751
+ else if (isSignal(obj)) {
3752
+ return obj.value;
3753
+ }
3754
+ else {
3755
+ return obj;
3756
+ }
3757
+ });
3758
+ };
3759
+ const handleError = (reason) => container.handleError(reason, host);
3760
+ const cleanups = [];
3761
+ task.$destroy$ = noSerialize(() => {
3762
+ cleanups.forEach((fn) => {
3763
+ try {
3764
+ fn();
3765
+ }
3766
+ catch (err) {
3767
+ handleError(err);
3768
+ }
3769
+ });
3770
+ done = true;
3771
+ });
3772
+ const resourceTarget = unwrapStore(resource);
3773
+ const opts = {
3774
+ track,
3775
+ cleanup(fn) {
3776
+ if (typeof fn === 'function') {
3777
+ cleanups.push(fn);
3778
+ }
3779
+ },
3780
+ cache(policy) {
3781
+ let milliseconds = 0;
3782
+ if (policy === 'immutable') {
3783
+ milliseconds = Infinity;
3784
+ }
3785
+ else {
3786
+ milliseconds = policy;
3787
+ }
3788
+ resource._cache = milliseconds;
3789
+ },
3790
+ previous: resourceTarget._resolved,
3791
+ };
3792
+ let resolve;
3793
+ let reject;
3794
+ let done = false;
3795
+ const setState = (resolved, value) => {
3796
+ if (!done) {
3797
+ done = true;
3798
+ if (resolved) {
3799
+ done = true;
3800
+ resource.loading = false;
3801
+ resource._state = 'resolved';
3802
+ resource._resolved = value;
3803
+ resource._error = undefined;
3804
+ resolve(value);
3805
+ }
3806
+ else {
3807
+ done = true;
3808
+ resource.loading = false;
3809
+ resource._state = 'rejected';
3810
+ resource._error = value;
3811
+ reject(value);
3462
3812
  }
3463
- vParent = vnode_getParent(vParent);
3813
+ return true;
3464
3814
  }
3465
- if (vParent == null) {
3466
- // We are done.
3467
- return;
3815
+ return false;
3816
+ };
3817
+ /**
3818
+ * Add cleanup to resolve the resource if we are trying to run the same resource again while the
3819
+ * previous one is not resolved yet. The next `runResource` run will call this cleanup
3820
+ */
3821
+ cleanups.push(() => {
3822
+ if (untrack(() => resource.loading) === true) {
3823
+ const value = untrack(() => resource._resolved);
3824
+ setState(true, value);
3468
3825
  }
3469
- } while (true);
3470
- }
3471
- function cleanupStaleUnclaimedProjection(journal, projection) {
3472
- // we are removing a node where the projection would go after slot render.
3473
- // This is not needed, so we need to cleanup still unclaimed projection
3474
- const projectionParent = vnode_getParent(projection);
3475
- if (projectionParent) {
3476
- const projectionParentType = projectionParent[VNodeProps.flags];
3477
- if (projectionParentType & VNodeFlags.Element &&
3478
- vnode_getElementName(projectionParent) === QTemplate) {
3479
- // if parent is the q:template element then projection is still unclaimed - remove it
3480
- vnode_remove(journal, projectionParent, projection, true);
3826
+ });
3827
+ // Execute mutation inside empty invocation
3828
+ invoke(iCtx, () => {
3829
+ // console.log('RESOURCE.pending: ');
3830
+ resource._state = 'pending';
3831
+ resource.loading = !isServerPlatform();
3832
+ const promise = (resource.value = new Promise((r, re) => {
3833
+ resolve = r;
3834
+ reject = re;
3835
+ }));
3836
+ promise.catch(ignoreErrorToPreventNodeFromCrashing);
3837
+ });
3838
+ const promise = safeCall(() => Promise.resolve(taskFn(opts)), (value) => {
3839
+ setState(true, value);
3840
+ }, (err) => {
3841
+ if (isPromise(err)) {
3842
+ return err.then(() => runResource(task, container, host));
3481
3843
  }
3844
+ else {
3845
+ setState(false, err);
3846
+ }
3847
+ });
3848
+ const timeout = resourceTarget._timeout;
3849
+ if (timeout > 0) {
3850
+ return Promise.race([
3851
+ promise,
3852
+ delay(timeout).then(() => {
3853
+ if (setState(false, new Error('timeout'))) {
3854
+ cleanupTask(task);
3855
+ }
3856
+ }),
3857
+ ]);
3482
3858
  }
3483
- }
3484
- function markVNodeAsDeleted(vCursor) {
3485
- /**
3486
- * Marks vCursor as deleted. We need to do this to prevent chores from running after the vnode is
3487
- * removed. (for example signal subscriptions)
3488
- */
3489
- vCursor[VNodeProps.flags] |= VNodeFlags.Deleted;
3490
- }
3491
- /**
3492
- * This marks the property as immutable. It is needed for the QRLs so that QwikLoader can get a hold
3493
- * of them. This character must be `:` so that the `vnode_getAttr` can ignore them.
3494
- */
3495
- const HANDLER_PREFIX = ':';
3496
- let count = 0;
3497
- var SiblingsArray;
3498
- (function (SiblingsArray) {
3499
- SiblingsArray[SiblingsArray["Name"] = 0] = "Name";
3500
- SiblingsArray[SiblingsArray["Key"] = 1] = "Key";
3501
- SiblingsArray[SiblingsArray["VNode"] = 2] = "VNode";
3502
- SiblingsArray[SiblingsArray["Size"] = 3] = "Size";
3503
- SiblingsArray[SiblingsArray["NextVNode"] = 5] = "NextVNode";
3504
- })(SiblingsArray || (SiblingsArray = {}));
3505
-
3506
- // <docs markdown="../../readme.md#implicit$FirstArg">
3507
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
3508
- // (edit ../../readme.md#implicit$FirstArg instead and run `pnpm docs.sync`)
3509
- /**
3510
- * Create a `____$(...)` convenience method from `___(...)`.
3511
- *
3512
- * It is very common for functions to take a lazy-loadable resource as a first argument. For this
3513
- * reason, the Qwik Optimizer automatically extracts the first argument from any function which ends
3514
- * in `$`.
3515
- *
3516
- * This means that `foo$(arg0)` and `foo($(arg0))` are equivalent with respect to Qwik Optimizer.
3517
- * The former is just a shorthand for the latter.
3518
- *
3519
- * For example, these function calls are equivalent:
3520
- *
3521
- * - `component$(() => {...})` is same as `component($(() => {...}))`
3522
- *
3523
- * ```tsx
3524
- * export function myApi(callback: QRL<() => void>): void {
3525
- * // ...
3526
- * }
3527
- *
3528
- * export const myApi$ = implicit$FirstArg(myApi);
3529
- * // type of myApi$: (callback: () => void): void
3530
- *
3531
- * // can be used as:
3532
- * myApi$(() => console.log('callback'));
3533
- *
3534
- * // will be transpiled to:
3535
- * // FILE: <current file>
3536
- * myApi(qrl('./chunk-abc.js', 'callback'));
3537
- *
3538
- * // FILE: chunk-abc.js
3539
- * export const callback = () => console.log('callback');
3540
- * ```
3541
- *
3542
- * @param fn - A function that should have its first argument automatically `$`.
3543
- * @public
3544
- */
3545
- // </docs>
3546
- const implicit$FirstArg = (fn) => {
3547
- return function (first, ...rest) {
3548
- return fn.call(null, dollar(first), ...rest);
3549
- };
3550
- };
3551
-
3552
- const createSignal$1 = (value) => {
3553
- return new Signal(null, value);
3859
+ return promise;
3554
3860
  };
3555
- const createComputedSignal = (qrl) => {
3556
- throwIfQRLNotResolved(qrl);
3557
- return new ComputedSignal(null, qrl);
3861
+ const ignoreErrorToPreventNodeFromCrashing = (err) => {
3862
+ // ignore error to prevent node from crashing
3863
+ // node will crash in promise is rejected and no one is listening to the rejection.
3558
3864
  };
3559
3865
 
3866
+ /// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
3867
+ const aVNodePath = [];
3868
+ const bVNodePath = [];
3560
3869
  /**
3561
- * Creates a Signal with the given value. If no value is given, the signal is created with
3562
- * `undefined`.
3870
+ * Compare two VNodes and determine their document position relative to each other.
3563
3871
  *
3564
- * @public
3872
+ * @param a VNode to compare
3873
+ * @param b VNode to compare
3874
+ * @param rootVNode - Root VNode of a container
3875
+ * @returns -1 if `a` is before `b`, 0 if `a` is the same as `b`, 1 if `a` is after `b`.
3565
3876
  */
3566
- const createSignal = createSignal$1;
3567
- /** @internal */
3568
- const createComputedQrl = createComputedSignal;
3877
+ const vnode_documentPosition = (a, b, rootVNode) => {
3878
+ if (a === b) {
3879
+ return 0;
3880
+ }
3881
+ let aDepth = -1;
3882
+ let bDepth = -1;
3883
+ while (a) {
3884
+ const vNode = (aVNodePath[++aDepth] = a);
3885
+ a = (vNode[VNodeProps.parent] ||
3886
+ (rootVNode && vnode_getProp(a, QSlotParent, (id) => vnode_locate(rootVNode, id))));
3887
+ }
3888
+ while (b) {
3889
+ const vNode = (bVNodePath[++bDepth] = b);
3890
+ b = (vNode[VNodeProps.parent] ||
3891
+ (rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))));
3892
+ }
3893
+ while (aDepth >= 0 && bDepth >= 0) {
3894
+ a = aVNodePath[aDepth];
3895
+ b = bVNodePath[bDepth];
3896
+ if (a === b) {
3897
+ // if the nodes are the same, we need to check the next level.
3898
+ aDepth--;
3899
+ bDepth--;
3900
+ }
3901
+ else {
3902
+ // We found a difference so we need to scan nodes at this level.
3903
+ let cursor = b;
3904
+ do {
3905
+ cursor = vnode_getNextSibling(cursor);
3906
+ if (cursor === a) {
3907
+ return 1;
3908
+ }
3909
+ } while (cursor);
3910
+ cursor = b;
3911
+ do {
3912
+ cursor = vnode_getPreviousSibling(cursor);
3913
+ if (cursor === a) {
3914
+ return -1;
3915
+ }
3916
+ } while (cursor);
3917
+ if (rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))) {
3918
+ // The "b" node is a projection, so we need to set it after "a" node,
3919
+ // because the "a" node could be a context provider.
3920
+ return -1;
3921
+ }
3922
+ // The node is not in the list of siblings, that means it must be disconnected.
3923
+ return 1;
3924
+ }
3925
+ }
3926
+ return aDepth < bDepth ? -1 : 1;
3927
+ };
3928
+ /// These global variables are used to avoid creating new arrays for each call to `ssrNodeDocumentPosition`.
3929
+ const aSsrNodePath = [];
3930
+ const bSsrNodePath = [];
3569
3931
  /**
3570
- * Create a computed signal which is calculated from the given QRL. A computed signal is a signal
3571
- * which is calculated from other signals. When the signals change, the computed signal is
3572
- * recalculated.
3573
- *
3574
- * The QRL must be a function which returns the value of the signal. The function must not have side
3575
- * effects, and it mus be synchronous.
3932
+ * Compare two SSR nodes and determine their document position relative to each other. Compares only
3933
+ * position between parent and child.
3576
3934
  *
3577
- * If you need the function to be async, use `useSignal` and `useTask$` instead.
3578
- *
3579
- * @public
3935
+ * @param a SSR node to compare
3936
+ * @param b SSR node to compare
3937
+ * @returns -1 if `a` is before `b`, 0 if `a` is the same as `b`, 1 if `a` is after `b`.
3580
3938
  */
3581
- const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedQrl);
3939
+ const ssrNodeDocumentPosition = (a, b) => {
3940
+ if (a === b) {
3941
+ return 0;
3942
+ }
3943
+ let aDepth = -1;
3944
+ let bDepth = -1;
3945
+ while (a) {
3946
+ const ssrNode = (aSsrNodePath[++aDepth] = a);
3947
+ a = ssrNode.currentComponentNode;
3948
+ }
3949
+ while (b) {
3950
+ const ssrNode = (bSsrNodePath[++bDepth] = b);
3951
+ b = ssrNode.currentComponentNode;
3952
+ }
3953
+ while (aDepth >= 0 && bDepth >= 0) {
3954
+ a = aSsrNodePath[aDepth];
3955
+ b = bSsrNodePath[bDepth];
3956
+ if (a === b) {
3957
+ // if the nodes are the same, we need to check the next level.
3958
+ aDepth--;
3959
+ bDepth--;
3960
+ }
3961
+ else {
3962
+ return 1;
3963
+ }
3964
+ }
3965
+ return aDepth < bDepth ? -1 : 1;
3966
+ };
3582
3967
 
3583
3968
  /**
3584
3969
  * Scheduler is responsible for running application code in predictable order.
@@ -3661,38 +4046,29 @@ const createComputed$ = /*#__PURE__*/ implicit$FirstArg(createComputedQrl);
3661
4046
  * - Visible Tasks are sorted afterJournalFlush, than depth first on component and finally in
3662
4047
  * declaration order within component.
3663
4048
  */
3664
- var ChoreType;
3665
- (function (ChoreType) {
3666
- /// MASKS defining three levels of sorting
3667
- ChoreType[ChoreType["MACRO"] = 240] = "MACRO";
3668
- /* order of elements (not encoded here) */
3669
- ChoreType[ChoreType["MICRO"] = 15] = "MICRO";
3670
- /** Ensure tha the QRL promise is resolved before processing next chores in the queue */
3671
- ChoreType[ChoreType["QRL_RESOLVE"] = 1] = "QRL_RESOLVE";
3672
- ChoreType[ChoreType["RESOURCE"] = 2] = "RESOURCE";
3673
- ChoreType[ChoreType["TASK"] = 3] = "TASK";
3674
- ChoreType[ChoreType["NODE_DIFF"] = 4] = "NODE_DIFF";
3675
- ChoreType[ChoreType["NODE_PROP"] = 5] = "NODE_PROP";
3676
- ChoreType[ChoreType["COMPONENT_SSR"] = 6] = "COMPONENT_SSR";
3677
- ChoreType[ChoreType["COMPONENT"] = 7] = "COMPONENT";
3678
- ChoreType[ChoreType["RECOMPUTE_AND_SCHEDULE_EFFECTS"] = 8] = "RECOMPUTE_AND_SCHEDULE_EFFECTS";
3679
- ChoreType[ChoreType["JOURNAL_FLUSH"] = 16] = "JOURNAL_FLUSH";
3680
- ChoreType[ChoreType["VISIBLE"] = 32] = "VISIBLE";
3681
- ChoreType[ChoreType["CLEANUP_VISIBLE"] = 48] = "CLEANUP_VISIBLE";
3682
- ChoreType[ChoreType["WAIT_FOR_ALL"] = 255] = "WAIT_FOR_ALL";
3683
- })(ChoreType || (ChoreType = {}));
4049
+ // Turn this on to get debug output of what the scheduler is doing.
4050
+ const DEBUG$1 = false;
4051
+ const getPromise = (chore) => (chore.$promise$ ||= new Promise((resolve) => {
4052
+ chore.$resolve$ = resolve;
4053
+ }));
3684
4054
  const createScheduler = (container, scheduleDrain, journalFlush) => {
3685
4055
  const choreQueue = [];
4056
+ const qrlRuns = [];
3686
4057
  let currentChore = null;
3687
- let journalFlushScheduled = false;
4058
+ let drainScheduled = false;
3688
4059
  return schedule;
3689
4060
  ///// IMPLEMENTATION /////
3690
4061
  function schedule(type, hostOrTask = null, targetOrQrl = null, payload = null) {
3691
- const runLater = type !== ChoreType.WAIT_FOR_ALL && type !== ChoreType.COMPONENT_SSR;
3692
- const isTask = type === ChoreType.TASK ||
3693
- type === ChoreType.VISIBLE ||
3694
- type === ChoreType.RESOURCE ||
3695
- type === ChoreType.CLEANUP_VISIBLE;
4062
+ const isServer = !isDomContainer(container);
4063
+ const isComponentSsr = isServer && type === ChoreType.COMPONENT;
4064
+ const runLater = type !== ChoreType.WAIT_FOR_ALL && !isComponentSsr && type !== ChoreType.RUN_QRL;
4065
+ const isTask = type === ChoreType.TASK || type === ChoreType.VISIBLE || type === ChoreType.CLEANUP_VISIBLE;
4066
+ const isClientOnly = type === ChoreType.JOURNAL_FLUSH ||
4067
+ type === ChoreType.NODE_DIFF ||
4068
+ type === ChoreType.NODE_PROP;
4069
+ if (isServer && isClientOnly) {
4070
+ return;
4071
+ }
3696
4072
  if (isTask) {
3697
4073
  hostOrTask.$flags$ |= TaskFlags.DIRTY;
3698
4074
  }
@@ -3711,184 +4087,234 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
3711
4087
  $returnValue$: null,
3712
4088
  $executed$: false,
3713
4089
  };
3714
- chore.$promise$ = new Promise((resolve) => (chore.$resolve$ = resolve));
3715
4090
  chore = sortedInsert(choreQueue, chore, container.rootVNode || null);
3716
- if (!journalFlushScheduled && runLater) {
4091
+ if (!drainScheduled && runLater) {
3717
4092
  // If we are not currently draining, we need to schedule a drain.
3718
- journalFlushScheduled = true;
4093
+ drainScheduled = true;
3719
4094
  schedule(ChoreType.JOURNAL_FLUSH);
3720
- scheduleDrain();
4095
+ // Catch here to avoid unhandled promise rejection
4096
+ scheduleDrain()?.catch?.(() => { });
3721
4097
  }
4098
+ // TODO figure out what to do with chore errors
3722
4099
  if (runLater) {
3723
- return chore.$promise$;
4100
+ return getPromise(chore);
3724
4101
  }
3725
4102
  else {
3726
- return drainUpTo(chore, container.rootVNode || null);
3727
- }
3728
- }
3729
- /**
3730
- * Execute all of the chores up to and including the given chore.
3731
- *
3732
- * @param runUptoChore
3733
- */
3734
- function drainUpTo(runUptoChore, rootVNode) {
3735
- // If it already ran, it's not in the queue
3736
- if (runUptoChore.$executed$) {
3737
- return runUptoChore.$returnValue$;
3738
- }
3739
- if (currentChore) {
3740
- // Already running chore, which means we're waiting for async completion
3741
- return runUptoChore.$promise$;
4103
+ return drainUpTo(chore, isServer);
3742
4104
  }
4105
+ }
4106
+ /** Execute all of the chores up to and including the given chore. */
4107
+ function drainUpTo(runUptoChore, isServer) {
4108
+ let maxRetries = 5000;
3743
4109
  while (choreQueue.length) {
3744
- const nextChore = choreQueue.shift();
3745
- const order = choreComparator(nextChore, runUptoChore, rootVNode, false);
3746
- if (order === null) {
3747
- continue;
4110
+ if (maxRetries-- < 0) {
4111
+ throw new Error('drainUpTo: max retries reached');
3748
4112
  }
3749
- if (order > 0) {
3750
- // we have processed all of the chores up to and including the given chore.
3751
- break;
4113
+ if (currentChore) {
4114
+ // Already running chore, which means we're waiting for async completion
4115
+ return getPromise(currentChore)
4116
+ .then(() => drainUpTo(runUptoChore, isServer))
4117
+ .catch((e) => {
4118
+ container.handleError(e, currentChore?.$host$);
4119
+ });
4120
+ }
4121
+ const nextChore = choreQueue[0];
4122
+ if (nextChore.$executed$) {
4123
+ choreQueue.shift();
4124
+ if (nextChore === runUptoChore) {
4125
+ break;
4126
+ }
4127
+ continue;
3752
4128
  }
3753
- const isDeletedVNode = vNodeAlreadyDeleted(nextChore);
3754
- if (isDeletedVNode &&
4129
+ if (vNodeAlreadyDeleted(nextChore) &&
3755
4130
  // we need to process cleanup tasks for deleted nodes
3756
4131
  nextChore.$type$ !== ChoreType.CLEANUP_VISIBLE) {
4132
+ choreQueue.shift();
3757
4133
  continue;
3758
4134
  }
3759
- const returnValue = executeChore(nextChore);
3760
- if (isPromise(returnValue)) {
3761
- const promise = returnValue.then(() => drainUpTo(runUptoChore, rootVNode));
3762
- return promise;
3763
- }
4135
+ executeChore(nextChore, isServer);
3764
4136
  }
3765
4137
  return runUptoChore.$returnValue$;
3766
4138
  }
3767
- function executeChore(chore) {
4139
+ function executeChore(chore, isServer) {
3768
4140
  const host = chore.$host$;
3769
4141
  assertEqual(currentChore, null, 'Chore already running.');
3770
4142
  currentChore = chore;
3771
4143
  let returnValue = null;
3772
- switch (chore.$type$) {
3773
- case ChoreType.JOURNAL_FLUSH:
3774
- returnValue = journalFlush();
3775
- journalFlushScheduled = false;
3776
- break;
3777
- case ChoreType.COMPONENT:
3778
- case ChoreType.COMPONENT_SSR:
3779
- returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
3780
- if (chore.$type$ === ChoreType.COMPONENT) {
3781
- const styleScopedId = container.getHostProp(host, QScopedStyle);
3782
- return vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId));
4144
+ try {
4145
+ switch (chore.$type$) {
4146
+ case ChoreType.WAIT_FOR_ALL:
4147
+ {
4148
+ if (isServer) {
4149
+ drainScheduled = false;
4150
+ }
3783
4151
  }
3784
- else {
3785
- return jsx;
4152
+ break;
4153
+ case ChoreType.JOURNAL_FLUSH:
4154
+ {
4155
+ returnValue = journalFlush();
4156
+ drainScheduled = false;
4157
+ }
4158
+ break;
4159
+ case ChoreType.COMPONENT:
4160
+ {
4161
+ returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
4162
+ if (isServer) {
4163
+ return jsx;
4164
+ }
4165
+ else {
4166
+ const styleScopedId = container.getHostProp(host, QScopedStyle);
4167
+ return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
4168
+ }
4169
+ }, (err) => container.handleError(err, host));
4170
+ }
4171
+ break;
4172
+ case ChoreType.RUN_QRL:
4173
+ {
4174
+ const fn = chore.$target$.getFn();
4175
+ const result = retryOnPromise(() => fn(...chore.$payload$));
4176
+ if (isPromise(result)) {
4177
+ const handled = result
4178
+ .finally(() => {
4179
+ qrlRuns.splice(qrlRuns.indexOf(handled), 1);
4180
+ })
4181
+ .catch((error) => {
4182
+ container.handleError(error, chore.$host$);
4183
+ });
4184
+ // Don't wait for the promise to resolve
4185
+ // TODO come up with a better solution, we also want concurrent signal handling with tasks but serial tasks
4186
+ qrlRuns.push(handled);
4187
+ DEBUG$1 &&
4188
+ debugTrace('execute.DONE (but still running)', chore, currentChore, choreQueue);
4189
+ chore.$returnValue$ = handled;
4190
+ chore.$resolve$?.(handled);
4191
+ currentChore = null;
4192
+ chore.$executed$ = true;
4193
+ // early out so we don't call after()
4194
+ return;
4195
+ }
4196
+ returnValue = null;
4197
+ }
4198
+ break;
4199
+ case ChoreType.TASK:
4200
+ case ChoreType.VISIBLE:
4201
+ {
4202
+ const payload = chore.$payload$;
4203
+ if (payload.$flags$ & TaskFlags.RESOURCE) {
4204
+ const result = runResource(payload, container, host);
4205
+ // Don't await the return value of the resource, because async resources should not be awaited.
4206
+ // The reason for this is that we should be able to update for example a node with loading
4207
+ // text. If we await the resource, the loading text will not be displayed until the resource
4208
+ // is loaded.
4209
+ // Awaiting on the client also causes a deadlock.
4210
+ // In any case, the resource will never throw.
4211
+ returnValue = isServer ? result : null;
4212
+ }
4213
+ else {
4214
+ returnValue = runTask(payload, container, host);
4215
+ }
4216
+ }
4217
+ break;
4218
+ case ChoreType.CLEANUP_VISIBLE:
4219
+ {
4220
+ const task = chore.$payload$;
4221
+ cleanupTask(task);
4222
+ }
4223
+ break;
4224
+ case ChoreType.NODE_DIFF:
4225
+ {
4226
+ const parentVirtualNode = chore.$target$;
4227
+ let jsx = chore.$payload$;
4228
+ if (isSignal(jsx)) {
4229
+ jsx = jsx.value;
4230
+ }
4231
+ returnValue = retryOnPromise(() => vnode_diff(container, jsx, parentVirtualNode, null));
4232
+ }
4233
+ break;
4234
+ case ChoreType.NODE_PROP:
4235
+ {
4236
+ const virtualNode = chore.$host$;
4237
+ const payload = chore.$payload$;
4238
+ let value = payload.$value$;
4239
+ if (isSignal(value)) {
4240
+ value = value.value;
4241
+ }
4242
+ const isConst = payload.$isConst$;
4243
+ const journal = container.$journal$;
4244
+ const property = chore.$idx$;
4245
+ const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
4246
+ if (isConst) {
4247
+ const element = virtualNode[ElementVNodeProps.element];
4248
+ journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue);
4249
+ }
4250
+ else {
4251
+ vnode_setAttr(journal, virtualNode, property, serializedValue);
4252
+ }
4253
+ }
4254
+ break;
4255
+ case ChoreType.QRL_RESOLVE: {
4256
+ {
4257
+ const target = chore.$target$;
4258
+ returnValue = !target.resolved ? target.resolve() : null;
3786
4259
  }
3787
- }, (err) => container.handleError(err, host));
3788
- break;
3789
- case ChoreType.RESOURCE:
3790
- // Don't await the return value of the resource, because async resources should not be awaited.
3791
- // The reason for this is that we should be able to update for example a node with loading
3792
- // text. If we await the resource, the loading text will not be displayed until the resource
3793
- // is loaded.
3794
- const result = runResource(chore.$payload$, container, host);
3795
- returnValue = isDomContainer(container) ? null : result;
3796
- break;
3797
- case ChoreType.TASK:
3798
- returnValue = runTask(chore.$payload$, container, host);
3799
- break;
3800
- case ChoreType.VISIBLE:
3801
- returnValue = runTask(chore.$payload$, container, host);
3802
- break;
3803
- case ChoreType.CLEANUP_VISIBLE:
3804
- const task = chore.$payload$;
3805
- cleanupTask(task);
3806
- break;
3807
- case ChoreType.NODE_DIFF:
3808
- const parentVirtualNode = chore.$target$;
3809
- let jsx = chore.$payload$;
3810
- if (isSignal(jsx)) {
3811
- jsx = jsx.value;
3812
- }
3813
- returnValue = vnode_diff(container, jsx, parentVirtualNode, null);
3814
- break;
3815
- case ChoreType.NODE_PROP:
3816
- const virtualNode = chore.$host$;
3817
- const payload = chore.$payload$;
3818
- let value = payload.$value$;
3819
- if (isSignal(value)) {
3820
- value = value.value;
3821
- }
3822
- const isConst = payload.$isConst$;
3823
- const journal = container.$journal$;
3824
- const property = chore.$idx$;
3825
- const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
3826
- if (isConst) {
3827
- const element = virtualNode[ElementVNodeProps.element];
3828
- journal.push(VNodeJournalOpCode.SetAttribute, element, property, serializedValue);
3829
- }
3830
- else {
3831
- vnode_setAttr(journal, virtualNode, property, serializedValue);
3832
- }
3833
- break;
3834
- case ChoreType.QRL_RESOLVE: {
3835
- const target = chore.$target$;
3836
- returnValue = !target.resolved ? target.resolve() : null;
3837
- break;
3838
- }
3839
- case ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS: {
3840
- const target = chore.$target$;
3841
- const forceRunEffects = target.$forceRunEffects$;
3842
- target.$forceRunEffects$ = false;
3843
- if (!target.$effects$?.length) {
3844
4260
  break;
3845
4261
  }
3846
- returnValue = retryOnPromise(() => {
3847
- if (target.$computeIfNeeded$() || forceRunEffects) {
3848
- triggerEffects(container, target, target.$effects$);
4262
+ case ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS: {
4263
+ {
4264
+ const target = chore.$target$;
4265
+ const forceRunEffects = target.$forceRunEffects$;
4266
+ target.$forceRunEffects$ = false;
4267
+ if (!target.$effects$?.size) {
4268
+ break;
4269
+ }
4270
+ returnValue = retryOnPromise(() => {
4271
+ if (target.$computeIfNeeded$() || forceRunEffects) {
4272
+ triggerEffects(container, target, target.$effects$);
4273
+ }
4274
+ });
3849
4275
  }
3850
- });
3851
- break;
4276
+ break;
4277
+ }
3852
4278
  }
3853
4279
  }
3854
- return maybeThenPassError(returnValue, (value) => {
3855
- if (currentChore) {
3856
- currentChore.$executed$ = true;
3857
- currentChore.$resolve$?.(value);
3858
- }
4280
+ catch (e) {
4281
+ returnValue = Promise.reject(e);
4282
+ }
4283
+ const after = (value, error) => {
3859
4284
  currentChore = null;
3860
- return (chore.$returnValue$ = value);
3861
- });
3862
- }
3863
- };
3864
- const toNumber = (value) => {
3865
- return typeof value === 'number' ? value : -1;
3866
- };
3867
- /**
3868
- * When a derived signal is update we need to run vnode_diff. However the signal can update multiple
3869
- * times during component execution. For this reason it is necessary for us to update the schedule
3870
- * work with the latest result of the signal.
3871
- */
3872
- const choreUpdate = (existing, newChore) => {
3873
- if (existing.$type$ === ChoreType.NODE_DIFF) {
3874
- existing.$payload$ = newChore.$payload$;
3875
- }
3876
- };
3877
- function vNodeAlreadyDeleted(chore) {
3878
- return !!(chore.$host$ &&
3879
- vnode_isVNode(chore.$host$) &&
3880
- chore.$host$[VNodeProps.flags] & VNodeFlags.Deleted);
3881
- }
3882
- function choreComparator(a, b, rootVNode, shouldThrowOnHostMismatch) {
3883
- const macroTypeDiff = (a.$type$ & ChoreType.MACRO) - (b.$type$ & ChoreType.MACRO);
3884
- if (macroTypeDiff !== 0) {
3885
- return macroTypeDiff;
4285
+ chore.$executed$ = true;
4286
+ if (error) {
4287
+ container.handleError(error, host);
4288
+ }
4289
+ else {
4290
+ chore.$returnValue$ = value;
4291
+ chore.$resolve$?.(value);
4292
+ }
4293
+ };
4294
+ if (isPromise(returnValue)) {
4295
+ chore.$promise$ = returnValue.then(after, (error) => after(undefined, error));
4296
+ chore.$resolve$?.(chore.$promise$);
4297
+ chore.$resolve$ = undefined;
4298
+ }
4299
+ else {
4300
+ after(returnValue);
4301
+ }
3886
4302
  }
3887
- // JOURNAL_FLUSH does not have a host or $idx$, so we can't compare it.
3888
- if (a.$type$ !== ChoreType.JOURNAL_FLUSH) {
4303
+ /**
4304
+ * Compares two chores to determine their execution order in the scheduler's queue.
4305
+ *
4306
+ * @param a - The first chore to compare
4307
+ * @param b - The second chore to compare
4308
+ * @returns A number indicating the relative order of the chores. A negative number means `a` runs
4309
+ * before `b`.
4310
+ */
4311
+ function choreComparator(a, b, rootVNode) {
4312
+ const macroTypeDiff = (a.$type$ & ChoreType.MACRO) - (b.$type$ & ChoreType.MACRO);
4313
+ if (macroTypeDiff !== 0) {
4314
+ return macroTypeDiff;
4315
+ }
3889
4316
  const aHost = a.$host$;
3890
4317
  const bHost = b.$host$;
3891
- // QRL_RESOLVE does not have a host.
3892
4318
  if (aHost !== bHost && aHost !== null && bHost !== null) {
3893
4319
  if (vnode_isVNode(aHost) && vnode_isVNode(bHost)) {
3894
4320
  // we are running on the client.
@@ -3898,6 +4324,8 @@ function choreComparator(a, b, rootVNode, shouldThrowOnHostMismatch) {
3898
4324
  }
3899
4325
  }
3900
4326
  else {
4327
+ assertFalse(vnode_isVNode(aHost), 'expected aHost to be SSRNode but it is a VNode');
4328
+ assertFalse(vnode_isVNode(bHost), 'expected bHost to be SSRNode but it is a VNode');
3901
4329
  // we are running on the server.
3902
4330
  // On server we can't schedule task for a different host!
3903
4331
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -3906,258 +4334,125 @@ function choreComparator(a, b, rootVNode, shouldThrowOnHostMismatch) {
3906
4334
  You are attempting to change a state that has already been streamed to the client.
3907
4335
  This can lead to inconsistencies between Server-Side Rendering (SSR) and Client-Side Rendering (CSR).
3908
4336
  Problematic Node: ${aHost.toString()}`;
3909
- if (shouldThrowOnHostMismatch) {
3910
- throw qError(QError.serverHostMismatch, [errorMessage]);
3911
- }
3912
4337
  logWarn(errorMessage);
3913
- return null;
4338
+ const hostDiff = ssrNodeDocumentPosition(aHost, bHost);
4339
+ if (hostDiff !== 0) {
4340
+ return hostDiff;
4341
+ }
3914
4342
  }
3915
4343
  }
3916
4344
  const microTypeDiff = (a.$type$ & ChoreType.MICRO) - (b.$type$ & ChoreType.MICRO);
3917
4345
  if (microTypeDiff !== 0) {
3918
4346
  return microTypeDiff;
3919
4347
  }
4348
+ // types are the same
3920
4349
  const idxDiff = toNumber(a.$idx$) - toNumber(b.$idx$);
3921
4350
  if (idxDiff !== 0) {
3922
4351
  return idxDiff;
3923
4352
  }
3924
- // If the host is the same, we need to compare the target.
3925
- if (a.$target$ !== b.$target$ &&
3926
- ((a.$type$ === ChoreType.QRL_RESOLVE && b.$type$ === ChoreType.QRL_RESOLVE) ||
3927
- (a.$type$ === ChoreType.NODE_PROP && b.$type$ === ChoreType.NODE_PROP) ||
3928
- (a.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS &&
3929
- b.$type$ === ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS))) {
4353
+ // If the host is the same (or missing), and the type is the same, we need to compare the target.
4354
+ if (a.$target$ !== b.$target$ || a.$payload$ !== b.$payload$) {
3930
4355
  // 1 means that we are going to process chores as FIFO
3931
4356
  return 1;
3932
4357
  }
3933
- }
3934
- return 0;
3935
- }
3936
- function sortedFindIndex(sortedArray, value, rootVNode) {
3937
- /// We need to ensure that the `queue` is sorted by priority.
3938
- /// 1. Find a place where to insert into.
3939
- let bottom = 0;
3940
- let top = sortedArray.length;
3941
- while (bottom < top) {
3942
- const middle = bottom + ((top - bottom) >> 1);
3943
- const midChore = sortedArray[middle];
3944
- const comp = choreComparator(value, midChore, rootVNode, true);
3945
- if (comp < 0) {
3946
- top = middle;
3947
- }
3948
- else if (comp > 0) {
3949
- bottom = middle + 1;
3950
- }
3951
- else {
3952
- // We already have the host in the queue.
3953
- return middle;
4358
+ // If the chore is the same as the current chore, we will run it again
4359
+ if (b === currentChore) {
4360
+ return 1;
3954
4361
  }
4362
+ // The chores are the same and will run only once
4363
+ return 0;
3955
4364
  }
3956
- return ~bottom;
3957
- }
3958
- function sortedInsert(sortedArray, value, rootVNode) {
3959
- /// We need to ensure that the `queue` is sorted by priority.
3960
- /// 1. Find a place where to insert into.
3961
- const idx = sortedFindIndex(sortedArray, value, rootVNode);
3962
- if (idx < 0) {
3963
- /// 2. Insert the chore into the queue.
3964
- sortedArray.splice(~idx, 0, value);
3965
- return value;
3966
- }
3967
- const existing = sortedArray[idx];
3968
- choreUpdate(existing, value);
3969
- return existing;
3970
- }
3971
-
3972
- // <docs markdown="../readme.md#useLexicalScope">
3973
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
3974
- // (edit ../readme.md#useLexicalScope instead and run `pnpm docs.sync`)
3975
- /**
3976
- * Used by the Qwik Optimizer to restore the lexically scoped variables.
3977
- *
3978
- * This method should not be present in the application source code.
3979
- *
3980
- * NOTE: `useLexicalScope` method can only be used in the synchronous portion of the callback
3981
- * (before any `await` statements.)
3982
- *
3983
- * @internal
3984
- */
3985
- // </docs>
3986
- const useLexicalScope = () => {
3987
- const context = getInvokeContext();
3988
- let qrl = context.$qrl$;
3989
- if (!qrl) {
3990
- const el = context.$element$;
3991
- assertDefined(el, 'invoke: element must be defined inside useLexicalScope()', context);
3992
- const containerElement = _getQContainerElement(el);
3993
- assertDefined(containerElement, `invoke: cant find parent q:container of`, el);
3994
- const container = getDomContainer(containerElement);
3995
- qrl = container.parseQRL(decodeURIComponent(String(context.$url$)));
3996
- }
3997
- else {
3998
- assertQrl(qrl);
3999
- assertDefined(qrl.$captureRef$, 'invoke: qrl $captureRef$ must be defined inside useLexicalScope()', qrl);
4000
- }
4001
- return qrl.$captureRef$;
4002
- };
4003
-
4004
- var TaskFlags;
4005
- (function (TaskFlags) {
4006
- TaskFlags[TaskFlags["VISIBLE_TASK"] = 1] = "VISIBLE_TASK";
4007
- TaskFlags[TaskFlags["TASK"] = 2] = "TASK";
4008
- TaskFlags[TaskFlags["RESOURCE"] = 4] = "RESOURCE";
4009
- TaskFlags[TaskFlags["DIRTY"] = 8] = "DIRTY";
4010
- })(TaskFlags || (TaskFlags = {}));
4011
- /** @internal */
4012
- const useTaskQrl = (qrl, opts) => {
4013
- const { val, set, iCtx, i } = useSequentialScope();
4014
- if (val) {
4015
- return;
4016
- }
4017
- assertQrl(qrl);
4018
- set(1);
4019
- const host = iCtx.$hostElement$;
4020
- const task = new Task(TaskFlags.DIRTY | TaskFlags.TASK, i, iCtx.$hostElement$, qrl, undefined, null);
4021
- // In V2 we add the task to the sequential scope. We need to do this
4022
- // in order to be able to retrieve it later when the parent element is
4023
- // deleted and we need to be able to release the task subscriptions.
4024
- set(task);
4025
- const result = runTask(task, iCtx.$container$, host);
4026
- if (isPromise(result)) {
4027
- throw result;
4028
- }
4029
- qrl.$resolveLazy$(iCtx.$element$);
4030
- if (isServerPlatform()) {
4031
- useRunTask(task, opts?.eagerness);
4032
- }
4033
- };
4034
- const runTask = (task, container, host) => {
4035
- task.$flags$ &= ~TaskFlags.DIRTY;
4036
- cleanupTask(task);
4037
- const iCtx = newInvokeContext(container.$locale$, host, undefined, TaskEvent);
4038
- iCtx.$container$ = container;
4039
- const taskFn = task.$qrl$.getFn(iCtx, () => clearSubscriberEffectDependencies(container, task));
4040
- const track = (obj, prop) => {
4041
- const ctx = newInvokeContext();
4042
- ctx.$effectSubscriber$ = [task, EffectProperty.COMPONENT];
4043
- ctx.$container$ = container;
4044
- return invoke(ctx, () => {
4045
- if (isFunction(obj)) {
4046
- return obj();
4047
- }
4048
- if (prop) {
4049
- return obj[prop];
4050
- }
4051
- else if (isSignal(obj)) {
4052
- return obj.value;
4365
+ function sortedFindIndex(sortedArray, value, rootVNode) {
4366
+ /// We need to ensure that the `queue` is sorted by priority.
4367
+ /// 1. Find a place where to insert into.
4368
+ let bottom = 0;
4369
+ let top = sortedArray.length;
4370
+ while (bottom < top) {
4371
+ const middle = bottom + ((top - bottom) >> 1);
4372
+ const midChore = sortedArray[middle];
4373
+ const comp = choreComparator(value, midChore, rootVNode);
4374
+ if (comp < 0) {
4375
+ top = middle;
4376
+ }
4377
+ else if (comp > 0) {
4378
+ bottom = middle + 1;
4053
4379
  }
4054
4380
  else {
4055
- return obj;
4056
- }
4057
- });
4058
- };
4059
- const handleError = (reason) => container.handleError(reason, host);
4060
- let cleanupFns = null;
4061
- const cleanup = (fn) => {
4062
- if (typeof fn == 'function') {
4063
- if (!cleanupFns) {
4064
- cleanupFns = [];
4065
- task.$destroy$ = noSerialize(() => {
4066
- task.$destroy$ = null;
4067
- cleanupFns.forEach((fn) => {
4068
- try {
4069
- fn();
4070
- }
4071
- catch (err) {
4072
- handleError(err);
4073
- }
4074
- });
4075
- });
4381
+ // We already have the host in the queue.
4382
+ return middle;
4076
4383
  }
4077
- cleanupFns.push(fn);
4078
- }
4079
- };
4080
- const taskApi = { track, cleanup };
4081
- const result = safeCall(() => taskFn(taskApi), cleanup, (err) => {
4082
- if (isPromise(err)) {
4083
- return err.then(() => runTask(task, container, host));
4084
- }
4085
- else {
4086
- return handleError(err);
4087
4384
  }
4088
- });
4089
- return result;
4090
- };
4091
- const cleanupTask = (task) => {
4092
- const destroy = task.$destroy$;
4093
- if (destroy) {
4094
- task.$destroy$ = null;
4095
- try {
4096
- destroy();
4385
+ return ~bottom;
4386
+ }
4387
+ function sortedInsert(sortedArray, value, rootVNode) {
4388
+ /// We need to ensure that the `queue` is sorted by priority.
4389
+ /// 1. Find a place where to insert into.
4390
+ const idx = sortedFindIndex(sortedArray, value, rootVNode);
4391
+ if (idx < 0) {
4392
+ /// 2. Insert the chore into the queue.
4393
+ sortedArray.splice(~idx, 0, value);
4394
+ return value;
4097
4395
  }
4098
- catch (err) {
4099
- logError(err);
4396
+ const existing = sortedArray[idx];
4397
+ /**
4398
+ * When a derived signal is updated we need to run vnode_diff. However the signal can update
4399
+ * multiple times during component execution. For this reason it is necessary for us to update
4400
+ * the chore with the latest result of the signal.
4401
+ */
4402
+ if (existing.$type$ === ChoreType.NODE_DIFF) {
4403
+ existing.$payload$ = value.$payload$;
4100
4404
  }
4405
+ if (existing.$executed$) {
4406
+ existing.$executed$ = false;
4407
+ }
4408
+ return existing;
4101
4409
  }
4102
4410
  };
4103
- const useRunTask = (task, eagerness) => {
4104
- if (eagerness === 'visible' || eagerness === 'intersection-observer') {
4105
- useOn('qvisible', getTaskHandlerQrl(task));
4106
- }
4107
- else if (eagerness === 'load' || eagerness === 'document-ready') {
4108
- useOnDocument('qinit', getTaskHandlerQrl(task));
4109
- }
4110
- else if (eagerness === 'idle' || eagerness === 'document-idle') {
4111
- useOnDocument('qidle', getTaskHandlerQrl(task));
4112
- }
4113
- };
4114
- const getTaskHandlerQrl = (task) => {
4115
- const taskQrl = task.$qrl$;
4116
- const taskHandler = createQRL(taskQrl.$chunk$, '_hW', _hW, null, null, [task], taskQrl.$symbol$);
4117
- // Needed for chunk lookup in dev mode
4118
- if (taskQrl.dev) {
4119
- taskHandler.dev = taskQrl.dev;
4120
- }
4121
- return taskHandler;
4122
- };
4123
- class Task extends Subscriber {
4124
- $flags$;
4125
- $index$;
4126
- $el$;
4127
- $qrl$;
4128
- $state$;
4129
- $destroy$;
4130
- constructor($flags$, $index$, $el$, $qrl$, $state$, $destroy$) {
4131
- super();
4132
- this.$flags$ = $flags$;
4133
- this.$index$ = $index$;
4134
- this.$el$ = $el$;
4135
- this.$qrl$ = $qrl$;
4136
- this.$state$ = $state$;
4137
- this.$destroy$ = $destroy$;
4138
- }
4139
- }
4140
- const isTask = (value) => {
4141
- return value instanceof Task;
4142
- };
4143
- /**
4144
- * Low-level API used by the Optimizer to process `useTask$()` API. This method is not intended to
4145
- * be used by developers.
4146
- *
4147
- * @internal
4148
- */
4149
- const _hW = () => {
4150
- const [task] = useLexicalScope();
4151
- const container = getDomContainer(task.$el$);
4152
- const type = task.$flags$ & TaskFlags.VISIBLE_TASK ? ChoreType.VISIBLE : ChoreType.TASK;
4153
- container.$scheduler$(type, task);
4411
+ const toNumber = (value) => {
4412
+ return typeof value === 'number' ? value : -1;
4154
4413
  };
4155
-
4156
- /**
4157
- * Special value used to mark that a given signal needs to be computed. This is essentially a
4158
- * "marked as dirty" flag.
4159
- */
4160
- const NEEDS_COMPUTATION = Symbol('invalid');
4414
+ function vNodeAlreadyDeleted(chore) {
4415
+ return !!(chore.$host$ &&
4416
+ vnode_isVNode(chore.$host$) &&
4417
+ chore.$host$[VNodeProps.flags] & VNodeFlags.Deleted);
4418
+ }
4419
+ function debugChoreTypeToString(type) {
4420
+ return ({
4421
+ [ChoreType.QRL_RESOLVE]: 'QRL_RESOLVE',
4422
+ [ChoreType.RUN_QRL]: 'RUN_QRL',
4423
+ [ChoreType.TASK]: 'TASK',
4424
+ [ChoreType.NODE_DIFF]: 'NODE_DIFF',
4425
+ [ChoreType.NODE_PROP]: 'NODE_PROP',
4426
+ [ChoreType.COMPONENT]: 'COMPONENT',
4427
+ [ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS]: 'RECOMPUTE_SIGNAL',
4428
+ [ChoreType.JOURNAL_FLUSH]: 'JOURNAL_FLUSH',
4429
+ [ChoreType.VISIBLE]: 'VISIBLE',
4430
+ [ChoreType.CLEANUP_VISIBLE]: 'CLEANUP_VISIBLE',
4431
+ [ChoreType.WAIT_FOR_ALL]: 'WAIT_FOR_ALL',
4432
+ }[type] || 'UNKNOWN: ' + type);
4433
+ }
4434
+ function debugChoreToString(chore) {
4435
+ const type = debugChoreTypeToString(chore.$type$);
4436
+ const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
4437
+ const qrlTarget = chore.$target$?.$symbol$;
4438
+ return `Chore(${type} ${chore.$type$ === ChoreType.QRL_RESOLVE || chore.$type$ === ChoreType.RUN_QRL ? qrlTarget : host} ${chore.$idx$})`;
4439
+ }
4440
+ function debugTrace(action, arg, currentChore, queue) {
4441
+ const lines = ['===========================\nScheduler: ' + action];
4442
+ if (arg && !('$type$' in arg)) {
4443
+ lines.push(' arg: ' + String(arg).replaceAll(/\n.*/gim, ''));
4444
+ }
4445
+ if (queue) {
4446
+ queue.forEach((chore) => {
4447
+ const active = chore === arg ? '>>>' : ' ';
4448
+ lines.push(` ${active} > ` +
4449
+ (chore === currentChore ? '[running] ' : '') +
4450
+ debugChoreToString(chore));
4451
+ });
4452
+ }
4453
+ // eslint-disable-next-line no-console
4454
+ console.log(lines.join('\n') + '\n');
4455
+ }
4161
4456
 
4162
4457
  /**
4163
4458
  * @file
@@ -4191,18 +4486,19 @@ const isSignal = (value) => {
4191
4486
  return value instanceof Signal;
4192
4487
  };
4193
4488
  /** @internal */
4194
- class EffectPropData {
4489
+ class SubscriptionData {
4195
4490
  data;
4196
4491
  constructor(data) {
4197
4492
  this.data = data;
4198
4493
  }
4199
4494
  }
4200
- var EffectSubscriptionsProp;
4201
- (function (EffectSubscriptionsProp) {
4202
- EffectSubscriptionsProp[EffectSubscriptionsProp["EFFECT"] = 0] = "EFFECT";
4203
- EffectSubscriptionsProp[EffectSubscriptionsProp["PROPERTY"] = 1] = "PROPERTY";
4204
- EffectSubscriptionsProp[EffectSubscriptionsProp["FIRST_BACK_REF_OR_DATA"] = 2] = "FIRST_BACK_REF_OR_DATA";
4205
- })(EffectSubscriptionsProp || (EffectSubscriptionsProp = {}));
4495
+ var EffectSubscriptionProp;
4496
+ (function (EffectSubscriptionProp) {
4497
+ EffectSubscriptionProp[EffectSubscriptionProp["CONSUMER"] = 0] = "CONSUMER";
4498
+ EffectSubscriptionProp[EffectSubscriptionProp["PROPERTY"] = 1] = "PROPERTY";
4499
+ EffectSubscriptionProp[EffectSubscriptionProp["BACK_REF"] = 2] = "BACK_REF";
4500
+ EffectSubscriptionProp[EffectSubscriptionProp["DATA"] = 3] = "DATA";
4501
+ })(EffectSubscriptionProp || (EffectSubscriptionProp = {}));
4206
4502
  var EffectProperty;
4207
4503
  (function (EffectProperty) {
4208
4504
  EffectProperty["COMPONENT"] = ":";
@@ -4239,19 +4535,16 @@ class Signal {
4239
4535
  }
4240
4536
  const effectSubscriber = ctx.$effectSubscriber$;
4241
4537
  if (effectSubscriber) {
4242
- const effects = (this.$effects$ ||= []);
4538
+ const effects = (this.$effects$ ||= new Set());
4243
4539
  // Let's make sure that we have a reference to this effect.
4244
4540
  // Adding reference is essentially adding a subscription, so if the signal
4245
4541
  // changes we know who to notify.
4246
- ensureContainsEffect(effects, effectSubscriber);
4542
+ ensureContainsSubscription(effects, effectSubscriber);
4247
4543
  // But when effect is scheduled in needs to be able to know which signals
4248
4544
  // to unsubscribe from. So we need to store the reference from the effect back
4249
4545
  // to this signal.
4250
- ensureContains(effectSubscriber, this);
4251
- if (isSubscriber(this)) {
4252
- // We need to add the subscriber to the effect so that we can clean it up later
4253
- ensureEffectContainsSubscriber(effectSubscriber[EffectSubscriptionsProp.EFFECT], this, this.$container$);
4254
- }
4546
+ ensureContainsBackRef(effectSubscriber, this);
4547
+ addQrlToSerializationCtx(effectSubscriber, this.$container$);
4255
4548
  }
4256
4549
  }
4257
4550
  return this.untrackedValue;
@@ -4270,122 +4563,97 @@ class Signal {
4270
4563
  }
4271
4564
  toString() {
4272
4565
  return (`[${this.constructor.name}${this.$invalid$ ? ' INVALID' : ''} ${String(this.$untrackedValue$)}]` +
4273
- (this.$effects$?.map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' ')).join('\n') || ''));
4566
+ (Array.from(this.$effects$ || [])
4567
+ .map((e) => '\n -> ' + pad(qwikDebugToString(e[0]), ' '))
4568
+ .join('\n') || ''));
4274
4569
  }
4275
4570
  toJSON() {
4276
4571
  return { value: this.$untrackedValue$ };
4277
4572
  }
4278
4573
  }
4279
- /** Ensure the item is in array (do nothing if already there) */
4280
- const ensureContains = (array, value) => {
4281
- const isMissing = array.indexOf(value) === -1;
4282
- if (isMissing) {
4283
- array.push(value);
4284
- }
4285
- };
4286
- const ensureContainsEffect = (array, effectSubscriptions) => {
4287
- for (let i = 0; i < array.length; i++) {
4288
- const existingEffect = array[i];
4289
- if (existingEffect[0] === effectSubscriptions[0] &&
4290
- existingEffect[1] === effectSubscriptions[1]) {
4291
- return;
4292
- }
4293
- }
4294
- array.push(effectSubscriptions);
4574
+ const ensureContainsSubscription = (array, effectSubscription) => {
4575
+ array.add(effectSubscription);
4295
4576
  };
4296
- const ensureEffectContainsSubscriber = (effect, subscriber, container) => {
4297
- if (isSubscriber(effect)) {
4298
- effect.$effectDependencies$ ||= [];
4299
- if (subscriberExistInSubscribers(effect.$effectDependencies$, subscriber)) {
4300
- return;
4577
+ /** Ensure the item is in back refs set */
4578
+ const ensureContainsBackRef = (array, value) => {
4579
+ array[EffectSubscriptionProp.BACK_REF] ||= new Set();
4580
+ array[EffectSubscriptionProp.BACK_REF].add(value);
4581
+ };
4582
+ const addQrlToSerializationCtx = (effectSubscriber, container) => {
4583
+ if (!!container && !isDomContainer(container)) {
4584
+ const effect = effectSubscriber[EffectSubscriptionProp.CONSUMER];
4585
+ const property = effectSubscriber[EffectSubscriptionProp.PROPERTY];
4586
+ let qrl = null;
4587
+ if (isTask(effect)) {
4588
+ qrl = effect.$qrl$;
4301
4589
  }
4302
- effect.$effectDependencies$.push(subscriber);
4303
- }
4304
- else if (vnode_isVNode(effect) && !vnode_isTextVNode(effect)) {
4305
- let subscribers = vnode_getProp(effect, QSubscribers, container ? container.$getObjectById$ : null);
4306
- subscribers ||= [];
4307
- if (subscriberExistInSubscribers(subscribers, subscriber)) {
4308
- return;
4590
+ else if (effect instanceof ComputedSignal) {
4591
+ qrl = effect.$computeQrl$;
4309
4592
  }
4310
- subscribers.push(subscriber);
4311
- vnode_setProp(effect, QSubscribers, subscribers);
4312
- }
4313
- else if (isSSRNode(effect)) {
4314
- let subscribers = effect.getProp(QSubscribers);
4315
- subscribers ||= [];
4316
- if (subscriberExistInSubscribers(subscribers, subscriber)) {
4317
- return;
4593
+ else if (property === EffectProperty.COMPONENT) {
4594
+ qrl = container.getHostProp(effect, OnRenderProp);
4318
4595
  }
4319
- subscribers.push(subscriber);
4320
- effect.setProp(QSubscribers, subscribers);
4321
- }
4322
- };
4323
- const isSSRNode = (effect) => {
4324
- return 'setProp' in effect && 'getProp' in effect && 'removeProp' in effect && 'id' in effect;
4325
- };
4326
- const subscriberExistInSubscribers = (subscribers, subscriber) => {
4327
- for (let i = 0; i < subscribers.length; i++) {
4328
- if (subscribers[i] === subscriber) {
4329
- return true;
4596
+ if (qrl) {
4597
+ container.serializationCtx.$eventQrls$.add(qrl);
4330
4598
  }
4331
4599
  }
4332
- return false;
4333
4600
  };
4334
4601
  const triggerEffects = (container, signal, effects) => {
4602
+ const isBrowser = isDomContainer(container);
4335
4603
  if (effects) {
4336
- const scheduleEffect = (effectSubscriptions) => {
4337
- const effect = effectSubscriptions[EffectSubscriptionsProp.EFFECT];
4338
- const property = effectSubscriptions[EffectSubscriptionsProp.PROPERTY];
4604
+ const scheduleEffect = (effectSubscription) => {
4605
+ const consumer = effectSubscription[EffectSubscriptionProp.CONSUMER];
4606
+ const property = effectSubscription[EffectSubscriptionProp.PROPERTY];
4339
4607
  assertDefined(container, 'Container must be defined.');
4340
- if (isTask(effect)) {
4341
- effect.$flags$ |= TaskFlags.DIRTY;
4608
+ if (isTask(consumer)) {
4609
+ consumer.$flags$ |= TaskFlags.DIRTY;
4342
4610
  let choreType = ChoreType.TASK;
4343
- if (effect.$flags$ & TaskFlags.VISIBLE_TASK) {
4611
+ if (consumer.$flags$ & TaskFlags.VISIBLE_TASK) {
4344
4612
  choreType = ChoreType.VISIBLE;
4345
4613
  }
4346
- else if (effect.$flags$ & TaskFlags.RESOURCE) {
4347
- choreType = ChoreType.RESOURCE;
4348
- }
4349
- container.$scheduler$(choreType, effect);
4614
+ container.$scheduler$(choreType, consumer);
4350
4615
  }
4351
- else if (effect instanceof Signal) {
4616
+ else if (consumer instanceof Signal) {
4352
4617
  // we don't schedule ComputedSignal/DerivedSignal directly, instead we invalidate it and
4353
4618
  // and schedule the signals effects (recursively)
4354
- if (effect instanceof ComputedSignal) {
4619
+ if (consumer instanceof ComputedSignal) {
4355
4620
  // Ensure that the computed signal's QRL is resolved.
4356
4621
  // If not resolved schedule it to be resolved.
4357
- if (!effect.$computeQrl$.resolved) {
4358
- container.$scheduler$(ChoreType.QRL_RESOLVE, null, effect.$computeQrl$);
4622
+ if (!consumer.$computeQrl$.resolved) {
4623
+ container.$scheduler$(ChoreType.QRL_RESOLVE, null, consumer.$computeQrl$);
4359
4624
  }
4360
4625
  }
4361
- effect.$invalidate$();
4626
+ consumer.$invalidate$();
4362
4627
  }
4363
4628
  else if (property === EffectProperty.COMPONENT) {
4364
- const host = effect;
4629
+ const host = consumer;
4365
4630
  const qrl = container.getHostProp(host, OnRenderProp);
4366
4631
  assertDefined(qrl, 'Component must have QRL');
4367
4632
  const props = container.getHostProp(host, ELEMENT_PROPS);
4368
4633
  container.$scheduler$(ChoreType.COMPONENT, host, qrl, props);
4369
4634
  }
4370
- else if (property === EffectProperty.VNODE) {
4371
- const host = effect;
4372
- const target = host;
4373
- container.$scheduler$(ChoreType.NODE_DIFF, host, target, signal);
4374
- }
4375
- else {
4376
- const host = effect;
4377
- const effectData = effectSubscriptions[EffectSubscriptionsProp.FIRST_BACK_REF_OR_DATA];
4378
- if (effectData instanceof EffectPropData) {
4379
- const data = effectData.data;
4380
- const payload = {
4381
- ...data,
4382
- $value$: signal,
4383
- };
4384
- container.$scheduler$(ChoreType.NODE_PROP, host, property, payload);
4635
+ else if (isBrowser) {
4636
+ if (property === EffectProperty.VNODE) {
4637
+ const host = consumer;
4638
+ container.$scheduler$(ChoreType.NODE_DIFF, host, host, signal);
4639
+ }
4640
+ else {
4641
+ const host = consumer;
4642
+ const effectData = effectSubscription[EffectSubscriptionProp.DATA];
4643
+ if (effectData instanceof SubscriptionData) {
4644
+ const data = effectData.data;
4645
+ const payload = {
4646
+ ...data,
4647
+ $value$: signal,
4648
+ };
4649
+ container.$scheduler$(ChoreType.NODE_PROP, host, property, payload);
4650
+ }
4385
4651
  }
4386
4652
  }
4387
4653
  };
4388
- effects.forEach(scheduleEffect);
4654
+ for (const effect of effects) {
4655
+ scheduleEffect(effect);
4656
+ }
4389
4657
  }
4390
4658
  };
4391
4659
  /**
@@ -4443,7 +4711,7 @@ class ComputedSignal extends Signal {
4443
4711
  throwIfQRLNotResolved(computeQrl);
4444
4712
  const ctx = tryGetInvokeContext();
4445
4713
  const previousEffectSubscription = ctx?.$effectSubscriber$;
4446
- ctx && (ctx.$effectSubscriber$ = [this, EffectProperty.VNODE]);
4714
+ ctx && (ctx.$effectSubscriber$ = getSubscriber(this, EffectProperty.VNODE));
4447
4715
  try {
4448
4716
  const untrackedValue = computeQrl.getFn(ctx)();
4449
4717
  if (isPromise(untrackedValue)) {
@@ -4481,9 +4749,9 @@ class WrappedSignal extends Signal {
4481
4749
  // We need a separate flag to know when the computation needs running because
4482
4750
  // we need the old value to know if effects need running after computation
4483
4751
  $invalid$ = true;
4484
- $effectDependencies$ = null;
4485
4752
  $hostElement$ = null;
4486
4753
  $forceRunEffects$ = false;
4754
+ [_EFFECT_BACK_REF] = null;
4487
4755
  constructor(container, fn, args, fnStr) {
4488
4756
  super(container, NEEDS_COMPUTATION);
4489
4757
  this.$args$ = args;
@@ -4551,7 +4819,7 @@ const applyQwikComponentBody = (ssr, jsx, component) => {
4551
4819
  if (jsx.key !== null) {
4552
4820
  host.setProp(ELEMENT_KEY, jsx.key);
4553
4821
  }
4554
- return scheduler(ChoreType.COMPONENT_SSR, host, componentQrl, srcProps);
4822
+ return scheduler(ChoreType.COMPONENT, host, componentQrl, srcProps);
4555
4823
  };
4556
4824
 
4557
4825
  class ParentComponentData {
@@ -4563,21 +4831,10 @@ class ParentComponentData {
4563
4831
  }
4564
4832
  }
4565
4833
  /** @internal */
4566
- function _walkJSX(ssr, value, options) {
4834
+ async function _walkJSX(ssr, value, options) {
4567
4835
  const stack = [value];
4568
- let resolveDrain;
4569
- let rejectDrain;
4570
- const drained = options.allowPromises &&
4571
- new Promise((res, rej) => {
4572
- resolveDrain = res;
4573
- rejectDrain = rej;
4574
- });
4575
4836
  const enqueue = (value) => stack.push(value);
4576
- const resolveValue = (value) => {
4577
- stack.push(value);
4578
- drain();
4579
- };
4580
- const drain = () => {
4837
+ const drain = async () => {
4581
4838
  while (stack.length) {
4582
4839
  const value = stack.pop();
4583
4840
  if (value instanceof ParentComponentData) {
@@ -4587,20 +4844,10 @@ function _walkJSX(ssr, value, options) {
4587
4844
  }
4588
4845
  else if (typeof value === 'function') {
4589
4846
  if (value === Promise) {
4590
- if (!options.allowPromises) {
4591
- throw qError(QError.promisesNotExpected);
4592
- }
4593
- stack.pop().then(resolveValue, rejectDrain);
4594
- return;
4595
- }
4596
- const waitOn = value.apply(ssr);
4597
- if (waitOn) {
4598
- if (!options.allowPromises) {
4599
- throw qError(QError.promisesNotExpected);
4600
- }
4601
- waitOn.then(drain, rejectDrain);
4602
- return;
4847
+ stack.push(await stack.pop());
4848
+ continue;
4603
4849
  }
4850
+ await value.apply(ssr);
4604
4851
  continue;
4605
4852
  }
4606
4853
  processJSXNode(ssr, enqueue, value, {
@@ -4608,12 +4855,8 @@ function _walkJSX(ssr, value, options) {
4608
4855
  parentComponentFrame: options.parentComponentFrame,
4609
4856
  });
4610
4857
  }
4611
- if (stack.length === 0 && options.allowPromises) {
4612
- resolveDrain();
4613
- }
4614
4858
  };
4615
- drain();
4616
- return drained;
4859
+ await drain();
4617
4860
  }
4618
4861
  function processJSXNode(ssr, enqueue, value, options) {
4619
4862
  // console.log('processJSXNode', value);
@@ -4652,7 +4895,6 @@ function processJSXNode(ssr, enqueue, value, options) {
4652
4895
  enqueue(async () => {
4653
4896
  for await (const chunk of value) {
4654
4897
  await _walkJSX(ssr, chunk, {
4655
- allowPromises: true,
4656
4898
  currentStyleScoped: options.styleScoped,
4657
4899
  parentComponentFrame: options.parentComponentFrame,
4658
4900
  });
@@ -4739,7 +4981,6 @@ function processJSXNode(ssr, enqueue, value, options) {
4739
4981
  value = generator({
4740
4982
  async write(chunk) {
4741
4983
  await _walkJSX(ssr, chunk, {
4742
- allowPromises: true,
4743
4984
  currentStyleScoped: options.styleScoped,
4744
4985
  parentComponentFrame: options.parentComponentFrame,
4745
4986
  });
@@ -4896,11 +5137,23 @@ function setEvent(serializationCtx, key, rawValue) {
4896
5137
  const appendToValue = (valueToAppend) => {
4897
5138
  value = (value == null ? '' : value + '\n') + valueToAppend;
4898
5139
  };
5140
+ const getQrlString = (qrl) => {
5141
+ /**
5142
+ * If there are captures we need to schedule so everything is executed in the right order + qrls
5143
+ * are resolved.
5144
+ *
5145
+ * For internal qrls (starting with `_`) we assume that they do the right thing.
5146
+ */
5147
+ if (!qrl.$symbol$.startsWith('_') && (qrl.$captureRef$ || qrl.$capture$)) {
5148
+ qrl = createQRL(null, '_run', queueQRL, null, null, [qrl]);
5149
+ }
5150
+ return qrlToString(serializationCtx, qrl);
5151
+ };
4899
5152
  if (Array.isArray(qrls)) {
4900
5153
  for (let i = 0; i <= qrls.length; i++) {
4901
5154
  const qrl = qrls[i];
4902
- if (isQrl(qrl)) {
4903
- appendToValue(qrlToString(serializationCtx, qrl));
5155
+ if (isQrl$1(qrl)) {
5156
+ appendToValue(getQrlString(qrl));
4904
5157
  addQwikEventToSerializationContext(serializationCtx, key, qrl);
4905
5158
  }
4906
5159
  else if (qrl != null) {
@@ -4912,8 +5165,8 @@ function setEvent(serializationCtx, key, rawValue) {
4912
5165
  }
4913
5166
  }
4914
5167
  }
4915
- else if (isQrl(qrls)) {
4916
- value = qrlToString(serializationCtx, qrls);
5168
+ else if (isQrl$1(qrls)) {
5169
+ value = getQrlString(qrls);
4917
5170
  addQwikEventToSerializationContext(serializationCtx, key, qrls);
4918
5171
  }
4919
5172
  return value;
@@ -4963,7 +5216,7 @@ function appendClassIfScopedStyleExists(jsx, styleScoped) {
4963
5216
  *
4964
5217
  * @public
4965
5218
  */
4966
- const version = "2.0.0-alpha.5-dev+cb53bbd";
5219
+ const version = "2.0.0-alpha.7-dev+a26598a";
4967
5220
 
4968
5221
  /** @internal */
4969
5222
  class _SharedContainer {
@@ -4995,13 +5248,6 @@ class _SharedContainer {
4995
5248
  }
4996
5249
  }
4997
5250
 
4998
- /** @internal */
4999
- const _CONST_PROPS = Symbol('CONST');
5000
- /** @internal */
5001
- const _VAR_PROPS = Symbol('VAR');
5002
- /** @internal @deprecated v1 compat */
5003
- const _IMMUTABLE = Symbol('IMMUTABLE');
5004
-
5005
5251
  // Keep these properties named like this so they're the same as from wrapSignal
5006
5252
  const getValueProp = (p0) => p0.value;
5007
5253
  const getProp = (p0, p1) => p0[p1];
@@ -5413,7 +5659,7 @@ function qwikDebugToString(value) {
5413
5659
  else if (isTask(value)) {
5414
5660
  return `Task(${qwikDebugToString(value.$qrl$)})`;
5415
5661
  }
5416
- else if (isQrl$1(value)) {
5662
+ else if (isQrl(value)) {
5417
5663
  return `Qrl(${value.$symbol$})`;
5418
5664
  }
5419
5665
  else if (typeof value === 'object' || typeof value === 'function') {
@@ -5567,14 +5813,21 @@ const VNodeDataChar = {
5567
5813
  CONTEXT_CHAR: /* **** */ ']',
5568
5814
  SEQ_IDX: /* ************ */ 94, // `^` - `q:seqIdx' - Sequential scope id
5569
5815
  SEQ_IDX_CHAR: /* **** */ '^',
5570
- SUBS: /* *************** */ 96, // '`' - `q:subs' - Effect dependencies/subscriptions
5571
- SUBS_CHAR: /* ******* */ '`',
5816
+ BACK_REFS: /* ********** */ 96, // '`' - `q:brefs' - Effect dependencies/subscriptions
5817
+ BACK_REFS_CHAR: /* ** */ '`',
5572
5818
  SEPARATOR: /* ********* */ 124, // `|` - Separator char to encode any key/value pairs.
5573
5819
  SEPARATOR_CHAR: /* ** */ '|',
5574
5820
  SLOT: /* ************** */ 126, // `~` - `q:slot' - Slot name
5575
5821
  SLOT_CHAR: /* ******* */ '~',
5576
5822
  };
5577
5823
 
5824
+ const mergeMaps = (map1, map2) => {
5825
+ for (const [k, v] of map2) {
5826
+ map1.set(k, v);
5827
+ }
5828
+ return map1;
5829
+ };
5830
+
5578
5831
  /**
5579
5832
  * @file
5580
5833
  *
@@ -5830,6 +6083,7 @@ const vnode_ensureElementInflated = (vnode) => {
5830
6083
  elementVNode[VNodeProps.flags] ^= VNodeFlags.Inflated;
5831
6084
  const element = elementVNode[ElementVNodeProps.element];
5832
6085
  const attributes = element.attributes;
6086
+ const props = vnode_getProps(elementVNode);
5833
6087
  for (let idx = 0; idx < attributes.length; idx++) {
5834
6088
  const attr = attributes[idx];
5835
6089
  const key = attr.name;
@@ -5840,15 +6094,15 @@ const vnode_ensureElementInflated = (vnode) => {
5840
6094
  }
5841
6095
  else if (key.startsWith(QContainerAttr)) {
5842
6096
  if (attr.value === QContainerValue.HTML) {
5843
- mapArray_set(elementVNode, dangerouslySetInnerHTML, element.innerHTML, ElementVNodeProps.PROPS_OFFSET);
6097
+ mapArray_set(props, dangerouslySetInnerHTML, element.innerHTML, 0);
5844
6098
  }
5845
6099
  else if (attr.value === QContainerValue.TEXT && 'value' in element) {
5846
- mapArray_set(elementVNode, 'value', element.value, ElementVNodeProps.PROPS_OFFSET);
6100
+ mapArray_set(props, 'value', element.value, 0);
5847
6101
  }
5848
6102
  }
5849
6103
  else if (!key.startsWith('on:')) {
5850
6104
  const value = attr.value;
5851
- mapArray_set(elementVNode, key, value, ElementVNodeProps.PROPS_OFFSET);
6105
+ mapArray_set(props, key, value, 0);
5852
6106
  }
5853
6107
  }
5854
6108
  }
@@ -6284,54 +6538,16 @@ const vnode_applyJournal = (journal) => {
6284
6538
  journal.length = 0;
6285
6539
  };
6286
6540
  //////////////////////////////////////////////////////////////////////////////////////////////////////
6287
- const mapApp_findIndx = (elementVNode, key, start) => {
6288
- assertTrue(start % 2 === 0, 'Expecting even number.');
6289
- let bottom = start >> 1;
6290
- let top = (elementVNode.length - 2) >> 1;
6291
- while (bottom <= top) {
6292
- const mid = bottom + ((top - bottom) >> 1);
6293
- const midKey = elementVNode[mid << 1];
6294
- if (midKey === key) {
6295
- return mid << 1;
6296
- }
6297
- if (midKey < key) {
6298
- bottom = mid + 1;
6299
- }
6300
- else {
6301
- top = mid - 1;
6302
- }
6303
- }
6304
- return (bottom << 1) ^ -1;
6305
- };
6306
- const mapArray_set = (elementVNode, key, value, start) => {
6307
- const indx = mapApp_findIndx(elementVNode, key, start);
6308
- if (indx >= 0) {
6309
- if (value == null) {
6310
- elementVNode.splice(indx, 2);
6311
- }
6312
- else {
6313
- elementVNode[indx + 1] = value;
6314
- }
6315
- }
6316
- else if (value != null) {
6317
- elementVNode.splice(indx ^ -1, 0, key, value);
6318
- }
6319
- };
6320
- const mapArray_get = (elementVNode, key, start) => {
6321
- const indx = mapApp_findIndx(elementVNode, key, start);
6322
- if (indx >= 0) {
6323
- return elementVNode[indx + 1];
6324
- }
6325
- else {
6326
- return null;
6327
- }
6328
- };
6329
- //////////////////////////////////////////////////////////////////////////////////////////////////////
6330
6541
  const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
6331
6542
  ensureElementOrVirtualVNode(parent);
6332
6543
  if (vnode_isElementVNode(parent)) {
6333
6544
  ensureMaterialized(parent);
6334
6545
  }
6546
+ if (newChild === insertBefore) {
6547
+ // invalid insertBefore. We can't insert before self reference
6548
+ // prevent infinity loop and putting self reference to next sibling
6549
+ insertBefore = null;
6550
+ }
6335
6551
  let adjustedInsertBefore = null;
6336
6552
  if (insertBefore == null) {
6337
6553
  if (vnode_isVirtualVNode(parent)) {
@@ -6455,9 +6671,10 @@ const vnode_getElementName = (vnode) => {
6455
6671
  const elementVNode = ensureElementVNode(vnode);
6456
6672
  let elementName = elementVNode[ElementVNodeProps.elementName];
6457
6673
  if (elementName === undefined) {
6458
- elementName = elementVNode[ElementVNodeProps.elementName] =
6459
- elementVNode[ElementVNodeProps.element].nodeName.toLowerCase();
6460
- elementVNode[VNodeProps.flags] |= vnode_getElementNamespaceFlags(elementName);
6674
+ const element = elementVNode[ElementVNodeProps.element];
6675
+ const nodeName = isDev ? fastNodeName(element).toLowerCase() : fastNodeName(element);
6676
+ elementName = elementVNode[ElementVNodeProps.elementName] = nodeName;
6677
+ elementVNode[VNodeProps.flags] |= vnode_getElementNamespaceFlags(element);
6461
6678
  }
6462
6679
  return elementName;
6463
6680
  };
@@ -6639,6 +6856,20 @@ const fastFirstChild = (node) => {
6639
6856
  }
6640
6857
  return node;
6641
6858
  };
6859
+ let _fastNamespaceURI = null;
6860
+ const fastNamespaceURI = (element) => {
6861
+ if (!_fastNamespaceURI) {
6862
+ _fastNamespaceURI = fastGetter(element, 'namespaceURI');
6863
+ }
6864
+ return _fastNamespaceURI.call(element);
6865
+ };
6866
+ let _fastNodeName = null;
6867
+ const fastNodeName = (element) => {
6868
+ if (!_fastNodeName) {
6869
+ _fastNodeName = fastGetter(element, 'nodeName');
6870
+ }
6871
+ return _fastNodeName.call(element);
6872
+ };
6642
6873
  const fastGetter = (prototype, name) => {
6643
6874
  let getter;
6644
6875
  while (prototype && !(getter = Object.getOwnPropertyDescriptor(prototype, name)?.get)) {
@@ -6705,8 +6936,11 @@ const materializeFromDOM = (vParent, firstChild, vData) => {
6705
6936
  container.$setRawState$(parseInt(id), vParent);
6706
6937
  isDev && vnode_setAttr(null, vParent, ELEMENT_ID, id);
6707
6938
  }
6708
- else if (peek() === VNodeDataChar.SUBS) {
6709
- vnode_setProp(vParent, QSubscribers, consumeValue());
6939
+ else if (peek() === VNodeDataChar.BACK_REFS) {
6940
+ if (!container) {
6941
+ container = getDomContainer(vParent[ElementVNodeProps.element]);
6942
+ }
6943
+ setEffectBackRefFromVNodeData(vParent, consumeValue(), container);
6710
6944
  }
6711
6945
  else {
6712
6946
  // prevent infinity loop if there are some characters outside the range
@@ -6716,6 +6950,18 @@ const materializeFromDOM = (vParent, firstChild, vData) => {
6716
6950
  }
6717
6951
  return vFirstChild;
6718
6952
  };
6953
+ function setEffectBackRefFromVNodeData(vParent, value, container) {
6954
+ const deserializedSubMap = container.$getObjectById$(value);
6955
+ if (!vParent[_EFFECT_BACK_REF]) {
6956
+ Object.defineProperty(vParent, _EFFECT_BACK_REF, {
6957
+ value: deserializedSubMap,
6958
+ });
6959
+ }
6960
+ else {
6961
+ const subMap = vParent[_EFFECT_BACK_REF];
6962
+ mergeMaps(subMap, deserializedSubMap);
6963
+ }
6964
+ }
6719
6965
  const processVNodeData$1 = (vData, callback) => {
6720
6966
  let nextToConsumeIdx = 0;
6721
6967
  let ch = 0;
@@ -6760,8 +7006,9 @@ const vnode_getAttrKeys = (vnode) => {
6760
7006
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6761
7007
  vnode_ensureElementInflated(vnode);
6762
7008
  const keys = [];
6763
- for (let i = vnode_getPropStartIndex(vnode); i < vnode.length; i = i + 2) {
6764
- const key = vnode[i];
7009
+ const props = vnode_getProps(vnode);
7010
+ for (let i = 0; i < props.length; i = i + 2) {
7011
+ const key = props[i];
6765
7012
  if (!key.startsWith(Q_PROPS_SEPARATOR)) {
6766
7013
  keys.push(key);
6767
7014
  }
@@ -6774,22 +7021,23 @@ const vnode_setAttr = (journal, vnode, key, value) => {
6774
7021
  const type = vnode[VNodeProps.flags];
6775
7022
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6776
7023
  vnode_ensureElementInflated(vnode);
6777
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7024
+ const props = vnode_getProps(vnode);
7025
+ const idx = mapApp_findIndx(props, key, 0);
6778
7026
  if (idx >= 0) {
6779
- if (vnode[idx + 1] != value && (type & VNodeFlags.Element) !== 0) {
7027
+ if (props[idx + 1] != value && (type & VNodeFlags.Element) !== 0) {
6780
7028
  // Values are different, update DOM
6781
7029
  const element = vnode[ElementVNodeProps.element];
6782
7030
  journal && journal.push(VNodeJournalOpCode.SetAttribute, element, key, value);
6783
7031
  }
6784
7032
  if (value == null) {
6785
- vnode.splice(idx, 2);
7033
+ props.splice(idx, 2);
6786
7034
  }
6787
7035
  else {
6788
- vnode[idx + 1] = value;
7036
+ props[idx + 1] = value;
6789
7037
  }
6790
7038
  }
6791
7039
  else if (value != null) {
6792
- vnode.splice(idx ^ -1, 0, key, value);
7040
+ props.splice(idx ^ -1, 0, key, value);
6793
7041
  if ((type & VNodeFlags.Element) !== 0) {
6794
7042
  // New value, update DOM
6795
7043
  const element = vnode[ElementVNodeProps.element];
@@ -6802,7 +7050,8 @@ const vnode_getAttr = (vnode, key) => {
6802
7050
  const type = vnode[VNodeProps.flags];
6803
7051
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6804
7052
  vnode_ensureElementInflated(vnode);
6805
- return mapArray_get(vnode, key, vnode_getPropStartIndex(vnode));
7053
+ const props = vnode_getProps(vnode);
7054
+ return mapArray_get(props, key, 0);
6806
7055
  }
6807
7056
  return null;
6808
7057
  };
@@ -6810,11 +7059,12 @@ const vnode_getProp = (vnode, key, getObject) => {
6810
7059
  const type = vnode[VNodeProps.flags];
6811
7060
  if ((type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) !== 0) {
6812
7061
  type & VNodeFlags.Element && vnode_ensureElementInflated(vnode);
6813
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7062
+ const props = vnode_getProps(vnode);
7063
+ const idx = mapApp_findIndx(props, key, 0);
6814
7064
  if (idx >= 0) {
6815
- let value = vnode[idx + 1];
7065
+ let value = props[idx + 1];
6816
7066
  if (typeof value === 'string' && getObject) {
6817
- vnode[idx + 1] = value = getObject(value);
7067
+ props[idx + 1] = value = getObject(value);
6818
7068
  }
6819
7069
  return value;
6820
7070
  }
@@ -6823,12 +7073,13 @@ const vnode_getProp = (vnode, key, getObject) => {
6823
7073
  };
6824
7074
  const vnode_setProp = (vnode, key, value) => {
6825
7075
  ensureElementOrVirtualVNode(vnode);
6826
- const idx = mapApp_findIndx(vnode, key, vnode_getPropStartIndex(vnode));
7076
+ const props = vnode_getProps(vnode);
7077
+ const idx = mapApp_findIndx(props, key, 0);
6827
7078
  if (idx >= 0) {
6828
- vnode[idx + 1] = value;
7079
+ props[idx + 1] = value;
6829
7080
  }
6830
7081
  else if (value != null) {
6831
- vnode.splice(idx ^ -1, 0, key, value);
7082
+ props.splice(idx ^ -1, 0, key, value);
6832
7083
  }
6833
7084
  };
6834
7085
  const vnode_getPropStartIndex = (vnode) => {
@@ -6841,6 +7092,9 @@ const vnode_getPropStartIndex = (vnode) => {
6841
7092
  }
6842
7093
  throw qError(QError.invalidVNodeType, [type]);
6843
7094
  };
7095
+ const vnode_getProps = (vnode) => {
7096
+ return vnode[vnode_getPropStartIndex(vnode)];
7097
+ };
6844
7098
  const vnode_getParent = (vnode) => {
6845
7099
  return vnode[VNodeProps.parent] || null;
6846
7100
  };
@@ -7004,8 +7258,11 @@ function materializeFromVNodeData(vParent, vData, element, child) {
7004
7258
  else if (peek() === VNodeDataChar.SEQ_IDX) {
7005
7259
  vnode_setAttr(null, vParent, ELEMENT_SEQ_IDX, consumeValue());
7006
7260
  }
7007
- else if (peek() === VNodeDataChar.SUBS) {
7008
- vnode_setProp(vParent, QSubscribers, consumeValue());
7261
+ else if (peek() === VNodeDataChar.BACK_REFS) {
7262
+ if (!container) {
7263
+ container = getDomContainer(element);
7264
+ }
7265
+ setEffectBackRefFromVNodeData(vParent, consumeValue(), container);
7009
7266
  }
7010
7267
  else if (peek() === VNodeDataChar.CONTEXT) {
7011
7268
  vnode_setAttr(null, vParent, QCtxAttr, consumeValue());
@@ -7071,60 +7328,6 @@ const vnode_getType = (vnode) => {
7071
7328
  throw qError(QError.invalidVNodeType, [type]);
7072
7329
  };
7073
7330
  const isElement = (node) => node && typeof node == 'object' && fastNodeType(node) === /** Node.ELEMENT_NODE* */ 1;
7074
- /// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
7075
- const aPath = [];
7076
- const bPath = [];
7077
- const vnode_documentPosition = (a, b, rootVNode) => {
7078
- if (a === b) {
7079
- return 0;
7080
- }
7081
- let aDepth = -1;
7082
- let bDepth = -1;
7083
- while (a) {
7084
- const vNode = (aPath[++aDepth] = a);
7085
- a = (vNode[VNodeProps.parent] ||
7086
- (rootVNode && vnode_getProp(a, QSlotParent, (id) => vnode_locate(rootVNode, id))));
7087
- }
7088
- while (b) {
7089
- const vNode = (bPath[++bDepth] = b);
7090
- b = (vNode[VNodeProps.parent] ||
7091
- (rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))));
7092
- }
7093
- while (aDepth >= 0 && bDepth >= 0) {
7094
- a = aPath[aDepth];
7095
- b = bPath[bDepth];
7096
- if (a === b) {
7097
- // if the nodes are the same, we need to check the next level.
7098
- aDepth--;
7099
- bDepth--;
7100
- }
7101
- else {
7102
- // We found a difference so we need to scan nodes at this level.
7103
- let cursor = b;
7104
- do {
7105
- cursor = vnode_getNextSibling(cursor);
7106
- if (cursor === a) {
7107
- return 1;
7108
- }
7109
- } while (cursor);
7110
- cursor = b;
7111
- do {
7112
- cursor = vnode_getPreviousSibling(cursor);
7113
- if (cursor === a) {
7114
- return -1;
7115
- }
7116
- } while (cursor);
7117
- if (rootVNode && vnode_getProp(b, QSlotParent, (id) => vnode_locate(rootVNode, id))) {
7118
- // The "b" node is a projection, so we need to set it after "a" node,
7119
- // because the "a" node could be a context provider.
7120
- return -1;
7121
- }
7122
- // The node is not in the list of siblings, that means it must be disconnected.
7123
- return 1;
7124
- }
7125
- }
7126
- return aDepth < bDepth ? -1 : 1;
7127
- };
7128
7331
  /**
7129
7332
  * Use this method to find the parent component for projection.
7130
7333
  *
@@ -7167,23 +7370,20 @@ const vnode_getProjectionParentComponent = (vHost, rootVNode) => {
7167
7370
  };
7168
7371
  const VNodeArray = class VNode extends Array {
7169
7372
  static createElement(flags, parent, previousSibling, nextSibling, firstChild, lastChild, element, elementName) {
7170
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7171
- vnode.push(firstChild, lastChild, element, elementName);
7373
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, firstChild, lastChild, element, elementName, []);
7172
7374
  return vnode;
7173
7375
  }
7174
7376
  static createText(flags, parent, previousSibling, nextSibling, textNode, text) {
7175
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7176
- vnode.push(textNode, text);
7377
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, textNode, text);
7177
7378
  return vnode;
7178
7379
  }
7179
7380
  static createVirtual(flags, parent, previousSibling, nextSibling, firstChild, lastChild) {
7180
- const vnode = new VNode(flags, parent, previousSibling, nextSibling);
7181
- vnode.push(firstChild, lastChild);
7381
+ const vnode = new VNode(flags, parent, previousSibling, nextSibling, firstChild, lastChild, []);
7182
7382
  return vnode;
7183
7383
  }
7184
- constructor(flags, parent, previousSibling, nextSibling) {
7185
- super();
7186
- this.push(flags, parent, previousSibling, nextSibling);
7384
+ constructor(flags, parent, previousSibling, nextSibling, ...rest) {
7385
+ // @ts-expect-error
7386
+ super(flags, parent, previousSibling, nextSibling, ...rest);
7187
7387
  if (isDev) {
7188
7388
  this.toString = vnode_toString;
7189
7389
  }
@@ -7248,10 +7448,12 @@ function invokeApply(context, fn, args) {
7248
7448
  return returnValue;
7249
7449
  }
7250
7450
  const newInvokeContextFromTuple = ([element, event, url]) => {
7251
- const container = element.closest(QContainerSelector);
7451
+ const domContainer = getDomContainer(element);
7452
+ const container = domContainer.element;
7453
+ const vNode = container ? vnode_locate(domContainer.rootVNode, element) : undefined;
7252
7454
  const locale = container?.getAttribute(QLocaleAttr) || undefined;
7253
7455
  locale && setLocale(locale);
7254
- return newInvokeContext(locale, undefined, element, event, url);
7456
+ return newInvokeContext(locale, vNode, element, event, url);
7255
7457
  };
7256
7458
  // TODO how about putting url and locale (and event/custom?) in to a "static" object
7257
7459
  const newInvokeContext = (locale, hostElement, element, event, url) => {
@@ -7286,16 +7488,14 @@ const trackInvocation = /*#__PURE__*/ newInvokeContext(undefined, undefined, und
7286
7488
  * @param property `true` - subscriber is component `false` - subscriber is VNode `string` -
7287
7489
  * subscriber is property
7288
7490
  * @param container
7491
+ * @param data - Additional subscription data
7289
7492
  * @returns
7290
7493
  */
7291
7494
  const trackSignal = (fn, subscriber, property, container, data) => {
7292
7495
  const previousSubscriber = trackInvocation.$effectSubscriber$;
7293
7496
  const previousContainer = trackInvocation.$container$;
7294
7497
  try {
7295
- trackInvocation.$effectSubscriber$ = [subscriber, property];
7296
- if (data) {
7297
- trackInvocation.$effectSubscriber$.push(data);
7298
- }
7498
+ trackInvocation.$effectSubscriber$ = getSubscriber(subscriber, property, data);
7299
7499
  trackInvocation.$container$ = container;
7300
7500
  return invoke(trackInvocation, fn);
7301
7501
  }
@@ -7316,8 +7516,16 @@ const _getContextElement = () => {
7316
7516
  if (iCtx) {
7317
7517
  const hostElement = iCtx.$hostElement$;
7318
7518
  let element = null;
7319
- if (vnode_isVNode(hostElement) && vnode_isElementVNode(hostElement)) {
7320
- element = vnode_getNode(hostElement);
7519
+ if (hostElement != null) {
7520
+ if (vnode_isVNode(hostElement)) {
7521
+ if (vnode_isElementVNode(hostElement)) {
7522
+ element = vnode_getNode(hostElement);
7523
+ }
7524
+ }
7525
+ else {
7526
+ // isSSRnode
7527
+ element = hostElement;
7528
+ }
7321
7529
  }
7322
7530
  return element ?? iCtx.$qrl$?.$setContainer$(undefined);
7323
7531
  }
@@ -7953,9 +8161,9 @@ class DomContainer extends _SharedContainer {
7953
8161
  $storeProxyMap$ = new WeakMap();
7954
8162
  $qFuncs$;
7955
8163
  $instanceHash$;
7956
- stateData;
8164
+ vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
8165
+ $stateData$;
7957
8166
  $styleIds$ = null;
7958
- $vnodeLocate$ = (id) => vnode_locate(this.rootVNode, id);
7959
8167
  $renderCount$ = 0;
7960
8168
  constructor(element) {
7961
8169
  super(() => this.scheduleRender(), () => vnode_applyJournal(this.$journal$), {}, element.getAttribute('q:locale'));
@@ -7981,29 +8189,29 @@ class DomContainer extends _SharedContainer {
7981
8189
  this.rootVNode = vnode_newUnMaterializedElement(this.element);
7982
8190
  // These are here to initialize all properties at once for single class transition
7983
8191
  this.$rawStateData$ = null;
7984
- this.stateData = null;
8192
+ this.$stateData$ = null;
7985
8193
  const document = this.element.ownerDocument;
7986
8194
  if (!document.qVNodeData) {
7987
8195
  processVNodeData(document);
7988
8196
  }
7989
8197
  this.$rawStateData$ = [];
7990
- this.stateData = [];
8198
+ this.$stateData$ = [];
7991
8199
  const qwikStates = element.querySelectorAll('script[type="qwik/state"]');
7992
8200
  if (qwikStates.length !== 0) {
7993
8201
  const lastState = qwikStates[qwikStates.length - 1];
7994
8202
  this.$rawStateData$ = JSON.parse(lastState.textContent);
7995
- this.stateData = wrapDeserializerProxy(this, this.$rawStateData$);
8203
+ this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
7996
8204
  }
7997
8205
  this.$qFuncs$ = getQFuncs(document, this.$instanceHash$) || EMPTY_ARRAY;
7998
8206
  }
7999
8207
  $setRawState$(id, vParent) {
8000
- this.stateData[id] = vParent;
8208
+ this.$stateData$[id] = vParent;
8001
8209
  }
8002
8210
  parseQRL(qrl) {
8003
8211
  return inflateQRL(this, parseQRL(qrl));
8004
8212
  }
8005
8213
  handleError(err, host) {
8006
- if (qDev) {
8214
+ if (qDev && host) {
8007
8215
  // Clean vdom
8008
8216
  if (typeof document !== 'undefined') {
8009
8217
  const vHost = host;
@@ -8027,7 +8235,7 @@ class DomContainer extends _SharedContainer {
8027
8235
  throw err;
8028
8236
  }
8029
8237
  }
8030
- const errorStore = this.resolveContext(host, ERROR_CONTEXT);
8238
+ const errorStore = host && this.resolveContext(host, ERROR_CONTEXT);
8031
8239
  if (!errorStore) {
8032
8240
  throw err;
8033
8241
  }
@@ -8063,7 +8271,7 @@ class DomContainer extends _SharedContainer {
8063
8271
  vNode =
8064
8272
  vnode_getParent(vNode) ||
8065
8273
  // If virtual node, than it could be a slot so we need to read its parent.
8066
- vnode_getProp(vNode, QSlotParent, this.$vnodeLocate$);
8274
+ vnode_getProp(vNode, QSlotParent, this.vNodeLocate);
8067
8275
  }
8068
8276
  else {
8069
8277
  vNode = vnode_getParent(vNode);
@@ -8083,7 +8291,7 @@ class DomContainer extends _SharedContainer {
8083
8291
  case ELEMENT_PROPS:
8084
8292
  case OnRenderProp:
8085
8293
  case QCtxAttr:
8086
- case QSubscribers:
8294
+ case QBackRefs:
8087
8295
  getObjectById = this.$getObjectById$;
8088
8296
  break;
8089
8297
  case ELEMENT_SEQ_IDX:
@@ -8096,7 +8304,7 @@ class DomContainer extends _SharedContainer {
8096
8304
  scheduleRender() {
8097
8305
  this.$renderCount$++;
8098
8306
  this.renderDone ||= getPlatform().nextTick(() => this.processChores());
8099
- return this.renderDone;
8307
+ return this.renderDone.finally(() => emitEvent('qrender', { instanceHash: this.$instanceHash$, renderCount: this.$renderCount$ }));
8100
8308
  }
8101
8309
  processChores() {
8102
8310
  let renderCount = this.$renderCount$;
@@ -8119,12 +8327,13 @@ class DomContainer extends _SharedContainer {
8119
8327
  ensureProjectionResolved(vNode) {
8120
8328
  if ((vNode[VNodeProps.flags] & VNodeFlags.Resolved) === 0) {
8121
8329
  vNode[VNodeProps.flags] |= VNodeFlags.Resolved;
8122
- for (let i = vnode_getPropStartIndex(vNode); i < vNode.length; i = i + 2) {
8123
- const prop = vNode[i];
8330
+ const props = vnode_getProps(vNode);
8331
+ for (let i = 0; i < props.length; i = i + 2) {
8332
+ const prop = props[i];
8124
8333
  if (isSlotProp(prop)) {
8125
- const value = vNode[i + 1];
8334
+ const value = props[i + 1];
8126
8335
  if (typeof value == 'string') {
8127
- vNode[i + 1] = this.$vnodeLocate$(value);
8336
+ props[i + 1] = this.vNodeLocate(value);
8128
8337
  }
8129
8338
  }
8130
8339
  }
@@ -8135,7 +8344,7 @@ class DomContainer extends _SharedContainer {
8135
8344
  id = parseFloat(id);
8136
8345
  }
8137
8346
  assertTrue(id < this.$rawStateData$.length / 2, `Invalid reference: ${id} >= ${this.$rawStateData$.length / 2}`);
8138
- return this.stateData[id];
8347
+ return this.$stateData$[id];
8139
8348
  };
8140
8349
  getSyncFn(id) {
8141
8350
  const fn = this.$qFuncs$[id];
@@ -8207,8 +8416,7 @@ class DeserializationHandler {
8207
8416
  ? parseInt(property, 10)
8208
8417
  : NaN;
8209
8418
  if (Number.isNaN(i) || i < 0 || i >= this.$length$) {
8210
- const out = Reflect.get(target, property, receiver);
8211
- return out;
8419
+ return Reflect.get(target, property, receiver);
8212
8420
  }
8213
8421
  // The serialized data is an array with 2 values for each item
8214
8422
  const idx = i * 2;
@@ -8274,6 +8482,7 @@ const inflate = (container, target, typeId, data) => {
8274
8482
  switch (typeId) {
8275
8483
  case TypeIds.Object:
8276
8484
  // We use getters for making complex values lazy
8485
+ // TODO scan the data for computeQRLs and schedule resolve chores
8277
8486
  for (let i = 0; i < data.length; i += 4) {
8278
8487
  const key = deserializeData(container, data[i], data[i + 1]);
8279
8488
  const valType = data[i + 2];
@@ -8313,7 +8522,7 @@ const inflate = (container, target, typeId, data) => {
8313
8522
  task.$flags$ = v[1];
8314
8523
  task.$index$ = v[2];
8315
8524
  task.$el$ = v[3];
8316
- task.$effectDependencies$ = v[4];
8525
+ task[_EFFECT_BACK_REF] = v[4];
8317
8526
  task.$state$ = v[5];
8318
8527
  break;
8319
8528
  case TypeIds.Resource:
@@ -8336,12 +8545,9 @@ const inflate = (container, target, typeId, data) => {
8336
8545
  break;
8337
8546
  case TypeIds.Store:
8338
8547
  case TypeIds.StoreArray: {
8339
- const [value, flags, effects, storeEffect] = data;
8548
+ const [value, flags, effects] = data;
8340
8549
  const store = getOrCreateStore(value, flags, container);
8341
8550
  const storeHandler = getStoreHandler(store);
8342
- if (storeEffect) {
8343
- effects[STORE_ARRAY_PROP] = storeEffect;
8344
- }
8345
8551
  storeHandler.$effects$ = effects;
8346
8552
  target = store;
8347
8553
  break;
@@ -8350,7 +8556,7 @@ const inflate = (container, target, typeId, data) => {
8350
8556
  const signal = target;
8351
8557
  const d = data;
8352
8558
  signal.$untrackedValue$ = d[0];
8353
- signal.$effects$ = d.slice(1);
8559
+ signal.$effects$ = new Set(d.slice(1));
8354
8560
  break;
8355
8561
  }
8356
8562
  case TypeIds.WrappedSignal: {
@@ -8358,10 +8564,10 @@ const inflate = (container, target, typeId, data) => {
8358
8564
  const d = data;
8359
8565
  signal.$func$ = container.getSyncFn(d[0]);
8360
8566
  signal.$args$ = d[1];
8361
- signal.$effectDependencies$ = d[2];
8567
+ signal[_EFFECT_BACK_REF] = d[2];
8362
8568
  signal.$untrackedValue$ = d[3];
8363
8569
  signal.$hostElement$ = d[4];
8364
- signal.$effects$ = d.slice(5);
8570
+ signal.$effects$ = new Set(d.slice(5));
8365
8571
  break;
8366
8572
  }
8367
8573
  case TypeIds.ComputedSignal: {
@@ -8481,6 +8687,7 @@ const _constants = [
8481
8687
  EMPTY_ARRAY,
8482
8688
  EMPTY_OBJ,
8483
8689
  NEEDS_COMPUTATION,
8690
+ STORE_ARRAY_PROP,
8484
8691
  Slot,
8485
8692
  Fragment,
8486
8693
  NaN,
@@ -8561,6 +8768,8 @@ const allocate = (container, typeId, value) => {
8561
8768
  reject = rej;
8562
8769
  });
8563
8770
  resolvers.set(promise, [resolve, reject]);
8771
+ // Don't leave unhandled promise rejections
8772
+ promise.catch(() => { });
8564
8773
  return promise;
8565
8774
  case TypeIds.Uint8Array:
8566
8775
  const encodedLength = value.length;
@@ -8570,12 +8779,9 @@ const allocate = (container, typeId, value) => {
8570
8779
  return new Uint8Array(decodedLength);
8571
8780
  case TypeIds.PropsProxy:
8572
8781
  return createPropsProxy(null, null);
8573
- case TypeIds.RefVNode:
8574
8782
  case TypeIds.VNode:
8575
- const vnodeOrDocument = retrieveVNodeOrDocument(container, value);
8576
- if (typeId === TypeIds.VNode) {
8577
- return vnodeOrDocument;
8578
- }
8783
+ return retrieveVNodeOrDocument(container, value);
8784
+ case TypeIds.RefVNode:
8579
8785
  const vNode = retrieveVNodeOrDocument(container, value);
8580
8786
  if (vnode_isVNode(vNode)) {
8581
8787
  return vnode_getNode(vNode);
@@ -8584,7 +8790,7 @@ const allocate = (container, typeId, value) => {
8584
8790
  throw qError(QError.serializeErrorExpectedVNode, [typeof vNode]);
8585
8791
  }
8586
8792
  case TypeIds.EffectData:
8587
- return new EffectPropData({});
8793
+ return new SubscriptionData({});
8588
8794
  default:
8589
8795
  throw qError(QError.serializeErrorCannotAllocate, [typeId]);
8590
8796
  }
@@ -8616,7 +8822,7 @@ function parseQRL(qrl) {
8616
8822
  assertDefined(backChannel, 'Missing QRL_RUNTIME_CHUNK');
8617
8823
  qrlRef = backChannel.get(symbol);
8618
8824
  }
8619
- return createQRL(chunk, symbol, qrlRef, null, captureIds, null, null);
8825
+ return createQRL(chunk, symbol, qrlRef, null, captureIds, null);
8620
8826
  }
8621
8827
  function inflateQRL(container, qrl) {
8622
8828
  const captureIds = qrl.$capture$;
@@ -8734,7 +8940,7 @@ prepVNodeData) => {
8734
8940
  /** Visit an object, adding anything that will be serialized as to scan */
8735
8941
  const visit = (obj) => {
8736
8942
  if (typeof obj === 'function') {
8737
- if (isQrl(obj)) {
8943
+ if (isQrl$1(obj)) {
8738
8944
  if (obj.$captureRef$) {
8739
8945
  discoveredValues.push(...obj.$captureRef$);
8740
8946
  }
@@ -8753,6 +8959,7 @@ prepVNodeData) => {
8753
8959
  obj instanceof RegExp ||
8754
8960
  obj instanceof Uint8Array ||
8755
8961
  obj instanceof URLSearchParams ||
8962
+ vnode_isVNode(obj) ||
8756
8963
  (typeof FormData !== 'undefined' && obj instanceof FormData) ||
8757
8964
  // Ignore the no serialize objects
8758
8965
  fastSkipSerialize(obj)) ;
@@ -8762,8 +8969,7 @@ prepVNodeData) => {
8762
8969
  else if (isStore(obj)) {
8763
8970
  const target = getStoreTarget(obj);
8764
8971
  const effects = getStoreHandler(obj).$effects$;
8765
- const storeEffect = effects?.[STORE_ARRAY_PROP] ?? null;
8766
- discoveredValues.push(target, effects, storeEffect);
8972
+ discoveredValues.push(target, effects);
8767
8973
  for (const prop in target) {
8768
8974
  const propValue = target[prop];
8769
8975
  if (storeProxyMap.has(propValue)) {
@@ -8797,9 +9003,7 @@ prepVNodeData) => {
8797
9003
  }
8798
9004
  // WrappedSignal uses syncQrl which has no captured refs
8799
9005
  if (obj instanceof WrappedSignal) {
8800
- if (obj.$effectDependencies$) {
8801
- discoveredValues.push(...obj.$effectDependencies$);
8802
- }
9006
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8803
9007
  if (obj.$args$) {
8804
9008
  discoveredValues.push(...obj.$args$);
8805
9009
  }
@@ -8812,7 +9016,8 @@ prepVNodeData) => {
8812
9016
  }
8813
9017
  }
8814
9018
  else if (obj instanceof Task) {
8815
- discoveredValues.push(obj.$el$, obj.$qrl$, obj.$state$, obj.$effectDependencies$);
9019
+ discoveredValues.push(obj.$el$, obj.$qrl$, obj.$state$);
9020
+ discoverEffectBackRefs(obj[_EFFECT_BACK_REF], discoveredValues);
8816
9021
  }
8817
9022
  else if (isSsrNode(obj)) {
8818
9023
  discoverValuesForVNodeData(obj.vnodeData, discoveredValues);
@@ -8831,7 +9036,7 @@ prepVNodeData) => {
8831
9036
  else if (Array.isArray(obj)) {
8832
9037
  discoveredValues.push(...obj);
8833
9038
  }
8834
- else if (isQrl(obj)) {
9039
+ else if (isQrl$1(obj)) {
8835
9040
  obj.$captureRef$ && obj.$captureRef$.length && discoveredValues.push(...obj.$captureRef$);
8836
9041
  }
8837
9042
  else if (isPropsProxy(obj)) {
@@ -8847,7 +9052,7 @@ prepVNodeData) => {
8847
9052
  });
8848
9053
  promises.push(obj);
8849
9054
  }
8850
- else if (obj instanceof EffectPropData) {
9055
+ else if (obj instanceof SubscriptionData) {
8851
9056
  discoveredValues.push(obj.data);
8852
9057
  }
8853
9058
  else if (isObjectLiteral(obj)) {
@@ -8892,15 +9097,31 @@ const discoverValuesForVNodeData = (vnodeData, discoveredValues) => {
8892
9097
  for (const value of vnodeData) {
8893
9098
  if (isSsrAttrs(value)) {
8894
9099
  for (let i = 1; i < value.length; i += 2) {
8895
- if (value[i - 1] === ELEMENT_KEY) {
9100
+ const attrValue = value[i];
9101
+ if (typeof attrValue === 'string') {
8896
9102
  continue;
8897
9103
  }
8898
- const attrValue = value[i];
8899
9104
  discoveredValues.push(attrValue);
8900
9105
  }
8901
9106
  }
8902
9107
  }
8903
9108
  };
9109
+ const discoverEffectBackRefs = (effectsBackRefs, discoveredValues) => {
9110
+ if (effectsBackRefs) {
9111
+ // We need serialize effect subscriptions with back refs
9112
+ let hasBackRefs = false;
9113
+ for (const [, effect] of effectsBackRefs) {
9114
+ const backRefs = effect[EffectSubscriptionProp.BACK_REF];
9115
+ if (backRefs) {
9116
+ hasBackRefs = true;
9117
+ break;
9118
+ }
9119
+ }
9120
+ if (hasBackRefs) {
9121
+ discoveredValues.push(effectsBackRefs);
9122
+ }
9123
+ }
9124
+ };
8904
9125
  const promiseResults = new WeakMap();
8905
9126
  /**
8906
9127
  * Format:
@@ -8972,7 +9193,7 @@ function serialize(serializationContext) {
8972
9193
  else if (value === Fragment) {
8973
9194
  output(TypeIds.Constant, Constants.Fragment);
8974
9195
  }
8975
- else if (isQrl(value)) {
9196
+ else if (isQrl$1(value)) {
8976
9197
  const qrl = qrlToString(serializationContext, value);
8977
9198
  const id = serializationContext.$addRoot$(qrl);
8978
9199
  output(TypeIds.QRL, id);
@@ -9047,6 +9268,9 @@ function serialize(serializationContext) {
9047
9268
  else if (value === NEEDS_COMPUTATION) {
9048
9269
  output(TypeIds.Constant, Constants.NEEDS_COMPUTATION);
9049
9270
  }
9271
+ else if (value === STORE_ARRAY_PROP) {
9272
+ output(TypeIds.Constant, Constants.STORE_ARRAY_PROP);
9273
+ }
9050
9274
  else {
9051
9275
  throw qError(QError.serializeErrorUnknownType, [typeof value]);
9052
9276
  }
@@ -9083,7 +9307,7 @@ function serialize(serializationContext) {
9083
9307
  : 0;
9084
9308
  output(TypeIds.PropsProxy, out);
9085
9309
  }
9086
- else if (value instanceof EffectPropData) {
9310
+ else if (value instanceof SubscriptionData) {
9087
9311
  output(TypeIds.EffectData, [value.data.$scopedStyleIdPrefix$, value.data.$isConst$]);
9088
9312
  }
9089
9313
  else if (isStore(value)) {
@@ -9094,6 +9318,7 @@ function serialize(serializationContext) {
9094
9318
  if (!res) {
9095
9319
  throw qError(QError.serializeErrorUnvisited, ['resource']);
9096
9320
  }
9321
+ // TODO the effects include the resourcereturn which has duplicate data
9097
9322
  output(TypeIds.Resource, [...res, getStoreHandler(value).$effects$]);
9098
9323
  }
9099
9324
  else {
@@ -9101,7 +9326,6 @@ function serialize(serializationContext) {
9101
9326
  const storeTarget = getStoreTarget(value);
9102
9327
  const flags = storeHandler.$flags$;
9103
9328
  const effects = storeHandler.$effects$;
9104
- const storeEffect = effects?.[STORE_ARRAY_PROP] ?? null;
9105
9329
  const innerStores = [];
9106
9330
  for (const prop in storeTarget) {
9107
9331
  const propValue = storeTarget[prop];
@@ -9111,7 +9335,7 @@ function serialize(serializationContext) {
9111
9335
  serializationContext.$addRoot$(innerStore);
9112
9336
  }
9113
9337
  }
9114
- const out = [storeTarget, flags, effects, storeEffect, ...innerStores];
9338
+ const out = [storeTarget, flags, effects, ...innerStores];
9115
9339
  while (out[out.length - 1] == null) {
9116
9340
  out.pop();
9117
9341
  }
@@ -9150,7 +9374,7 @@ function serialize(serializationContext) {
9150
9374
  if (value instanceof WrappedSignal) {
9151
9375
  output(TypeIds.WrappedSignal, [
9152
9376
  ...serializeWrappingFn(serializationContext, value),
9153
- value.$effectDependencies$,
9377
+ filterEffectBackRefs(value[_EFFECT_BACK_REF]),
9154
9378
  v,
9155
9379
  value.$hostElement$,
9156
9380
  ...(value.$effects$ || []),
@@ -9257,7 +9481,7 @@ function serialize(serializationContext) {
9257
9481
  value.$flags$,
9258
9482
  value.$index$,
9259
9483
  value.$el$,
9260
- value.$effectDependencies$,
9484
+ value[_EFFECT_BACK_REF],
9261
9485
  value.$state$,
9262
9486
  ];
9263
9487
  while (out[out.length - 1] == null) {
@@ -9280,12 +9504,27 @@ function serialize(serializationContext) {
9280
9504
  const out = btoa(buf).replace(/=+$/, '');
9281
9505
  output(TypeIds.Uint8Array, out);
9282
9506
  }
9507
+ else if (vnode_isVNode(value)) {
9508
+ output(TypeIds.Constant, Constants.Undefined);
9509
+ }
9283
9510
  else {
9284
9511
  throw qError(QError.serializeErrorUnknownType, [typeof value]);
9285
9512
  }
9286
9513
  };
9287
9514
  writeValue(serializationContext.$roots$, -1);
9288
9515
  }
9516
+ function filterEffectBackRefs(effectBackRef) {
9517
+ let effectBackRefToSerialize = null;
9518
+ if (effectBackRef) {
9519
+ for (const [effectProp, effect] of effectBackRef) {
9520
+ if (effect[EffectSubscriptionProp.BACK_REF]) {
9521
+ effectBackRefToSerialize ||= new Map();
9522
+ effectBackRefToSerialize.set(effectProp, effect);
9523
+ }
9524
+ }
9525
+ }
9526
+ return effectBackRefToSerialize;
9527
+ }
9289
9528
  function serializeWrappingFn(serializationContext, value) {
9290
9529
  // if value is an object then we need to wrap this in ()
9291
9530
  if (value.$funcStr$ && value.$funcStr$[0] === '{') {
@@ -9298,15 +9537,12 @@ function serializeWrappingFn(serializationContext, value) {
9298
9537
  function qrlToString(serializationContext, value) {
9299
9538
  let symbol = value.$symbol$;
9300
9539
  let chunk = value.$chunk$;
9301
- const refSymbol = value.$refSymbol$ ?? symbol;
9302
9540
  const platform = getPlatform();
9303
9541
  if (platform) {
9304
- const result = platform.chunkForSymbol(refSymbol, chunk, value.dev?.file);
9542
+ const result = platform.chunkForSymbol(symbol, chunk, value.dev?.file);
9305
9543
  if (result) {
9306
9544
  chunk = result[1];
9307
- if (!value.$refSymbol$) {
9308
- symbol = result[0];
9309
- }
9545
+ symbol = result[0];
9310
9546
  }
9311
9547
  }
9312
9548
  const isSync = isSyncQrl(value);
@@ -9483,9 +9719,9 @@ const frameworkType = (obj) => {
9483
9719
  return ((typeof obj === 'object' &&
9484
9720
  obj !== null &&
9485
9721
  (obj instanceof Signal || obj instanceof Task || isJSXNode(obj))) ||
9486
- isQrl(obj));
9722
+ isQrl$1(obj));
9487
9723
  };
9488
- const canSerialize = (value) => {
9724
+ const canSerialize = (value, seen = new WeakSet()) => {
9489
9725
  if (value == null ||
9490
9726
  typeof value === 'string' ||
9491
9727
  typeof value === 'number' ||
@@ -9494,6 +9730,10 @@ const canSerialize = (value) => {
9494
9730
  return true;
9495
9731
  }
9496
9732
  else if (typeof value === 'object') {
9733
+ if (seen.has(value)) {
9734
+ return true;
9735
+ }
9736
+ seen.add(value);
9497
9737
  const proto = Object.getPrototypeOf(value);
9498
9738
  if (isStore(value)) {
9499
9739
  value = getStoreTarget(value);
@@ -9502,7 +9742,7 @@ const canSerialize = (value) => {
9502
9742
  for (const key in value) {
9503
9743
  // if the value is a props proxy, then sometimes we could create a component-level subscription,
9504
9744
  // so we should call untrack here to avoid tracking the value
9505
- if (!canSerialize(untrack(() => value[key]))) {
9745
+ if (!canSerialize(untrack(() => value[key]), seen)) {
9506
9746
  return false;
9507
9747
  }
9508
9748
  }
@@ -9510,7 +9750,7 @@ const canSerialize = (value) => {
9510
9750
  }
9511
9751
  else if (proto == Array.prototype) {
9512
9752
  for (let i = 0; i < value.length; i++) {
9513
- if (!canSerialize(value[i])) {
9753
+ if (!canSerialize(value[i], seen)) {
9514
9754
  return false;
9515
9755
  }
9516
9756
  }
@@ -9560,7 +9800,7 @@ const canSerialize = (value) => {
9560
9800
  }
9561
9801
  }
9562
9802
  else if (typeof value === 'function') {
9563
- if (isQrl(value) || isQwikComponent(value)) {
9803
+ if (isQrl$1(value) || isQwikComponent(value)) {
9564
9804
  return true;
9565
9805
  }
9566
9806
  }
@@ -9613,20 +9853,21 @@ var Constants;
9613
9853
  Constants[Constants["EMPTY_ARRAY"] = 5] = "EMPTY_ARRAY";
9614
9854
  Constants[Constants["EMPTY_OBJ"] = 6] = "EMPTY_OBJ";
9615
9855
  Constants[Constants["NEEDS_COMPUTATION"] = 7] = "NEEDS_COMPUTATION";
9616
- Constants[Constants["Slot"] = 8] = "Slot";
9617
- Constants[Constants["Fragment"] = 9] = "Fragment";
9618
- Constants[Constants["NaN"] = 10] = "NaN";
9619
- Constants[Constants["PositiveInfinity"] = 11] = "PositiveInfinity";
9620
- Constants[Constants["NegativeInfinity"] = 12] = "NegativeInfinity";
9621
- Constants[Constants["MaxSafeInt"] = 13] = "MaxSafeInt";
9856
+ Constants[Constants["STORE_ARRAY_PROP"] = 8] = "STORE_ARRAY_PROP";
9857
+ Constants[Constants["Slot"] = 9] = "Slot";
9858
+ Constants[Constants["Fragment"] = 10] = "Fragment";
9859
+ Constants[Constants["NaN"] = 11] = "NaN";
9860
+ Constants[Constants["PositiveInfinity"] = 12] = "PositiveInfinity";
9861
+ Constants[Constants["NegativeInfinity"] = 13] = "NegativeInfinity";
9862
+ Constants[Constants["MaxSafeInt"] = 14] = "MaxSafeInt";
9622
9863
  // used for close fragment
9623
- Constants[Constants["AlmostMaxSafeInt"] = 14] = "AlmostMaxSafeInt";
9624
- Constants[Constants["MinSafeInt"] = 15] = "MinSafeInt";
9864
+ Constants[Constants["AlmostMaxSafeInt"] = 15] = "AlmostMaxSafeInt";
9865
+ Constants[Constants["MinSafeInt"] = 16] = "MinSafeInt";
9625
9866
  })(Constants || (Constants = {}));
9626
9867
 
9627
9868
  /** @internal */
9628
9869
  const verifySerializable = (value, preMessage) => {
9629
- const seen = new Set();
9870
+ const seen = new WeakSet();
9630
9871
  return _verifySerializable(value, seen, '_', preMessage);
9631
9872
  };
9632
9873
  const _verifySerializable = (value, seen, ctx, preMessage) => {
@@ -9635,10 +9876,12 @@ const _verifySerializable = (value, seen, ctx, preMessage) => {
9635
9876
  return value;
9636
9877
  }
9637
9878
  if (shouldSerialize(unwrapped)) {
9638
- if (seen.has(unwrapped)) {
9639
- return value;
9879
+ if (typeof unwrapped === 'object') {
9880
+ if (seen.has(unwrapped)) {
9881
+ return value;
9882
+ }
9883
+ seen.add(unwrapped);
9640
9884
  }
9641
- seen.add(unwrapped);
9642
9885
  if (isSignal(unwrapped)) {
9643
9886
  return value;
9644
9887
  }
@@ -9742,16 +9985,7 @@ const _weakSerialize = (input) => {
9742
9985
  return input;
9743
9986
  };
9744
9987
 
9745
- const isQrl = (value) => {
9746
- return typeof value === 'function' && typeof value.getSymbol === 'function';
9747
- };
9748
- // Make sure this value is same as value in `platform.ts`
9749
- const SYNC_QRL = '<sync>';
9750
- /** Sync QRL is a function which is serialized into `<script q:func="qwik/json">` tag. */
9751
- const isSyncQrl = (value) => {
9752
- return isQrl(value) && value.$symbol$ == SYNC_QRL;
9753
- };
9754
- const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refSymbol) => {
9988
+ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef) => {
9755
9989
  if (qDev && qSerialize) {
9756
9990
  if (captureRef) {
9757
9991
  for (const item of captureRef) {
@@ -9773,9 +10007,14 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9773
10007
  };
9774
10008
  function bindFnToContext(currentCtx, beforeFn) {
9775
10009
  // Note that we bind the current `this`
9776
- return (...args) => maybeThen(resolveLazy(), (fn) => {
9777
- if (!isFunction(fn)) {
9778
- throw qError(QError.qrlIsNotFunction);
10010
+ const bound = (...args) => {
10011
+ if (!qrl.resolved) {
10012
+ return qrl.resolve().then((fn) => {
10013
+ if (!isFunction(fn)) {
10014
+ throw qError(QError.qrlIsNotFunction);
10015
+ }
10016
+ return bound(...args);
10017
+ });
9779
10018
  }
9780
10019
  if (beforeFn && beforeFn() === false) {
9781
10020
  return;
@@ -9788,13 +10027,14 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9788
10027
  context.$qrl$ = qrl;
9789
10028
  context.$event$ ||= this;
9790
10029
  try {
9791
- return invoke.call(this, context, fn, ...args);
10030
+ return invoke.call(this, context, symbolRef, ...args);
9792
10031
  }
9793
10032
  finally {
9794
10033
  context.$qrl$ = prevQrl;
9795
10034
  context.$event$ = prevEvent;
9796
10035
  }
9797
- });
10036
+ };
10037
+ return bound;
9798
10038
  }
9799
10039
  const resolveLazy = (containerEl) => {
9800
10040
  return symbolRef !== null ? symbolRef : resolve(containerEl);
@@ -9806,8 +10046,20 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9806
10046
  }
9807
10047
  return function (...args) {
9808
10048
  let context = tryGetInvokeContext();
10049
+ // use the given qrl if it is the right one
9809
10050
  if (context) {
9810
- return fn.apply(this, args);
10051
+ // TODO check if this is necessary in production
10052
+ if (context.$qrl$?.$symbol$ === qrl.$symbol$) {
10053
+ return fn.apply(this, args);
10054
+ }
10055
+ const prevQrl = context.$qrl$;
10056
+ context.$qrl$ = qrl;
10057
+ try {
10058
+ return fn.apply(this, args);
10059
+ }
10060
+ finally {
10061
+ context.$qrl$ = prevQrl;
10062
+ }
9811
10063
  }
9812
10064
  context = newInvokeContext();
9813
10065
  context.$qrl$ = qrl;
@@ -9835,11 +10087,11 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9835
10087
  const start = now();
9836
10088
  const ctx = tryGetInvokeContext();
9837
10089
  if (symbolFn !== null) {
9838
- symbolRef = symbolFn().then((module) => (qrl.resolved = symbolRef = wrapFn(module[symbol])));
10090
+ symbolRef = symbolFn().then((module) => (qrl.resolved = wrapFn((symbolRef = module[symbol]))));
9839
10091
  }
9840
10092
  else {
9841
10093
  const imported = getPlatform().importSymbol(_containerEl, chunk, symbol);
9842
- symbolRef = maybeThen(imported, (ref) => (qrl.resolved = symbolRef = wrapFn(ref)));
10094
+ symbolRef = maybeThen(imported, (ref) => (qrl.resolved = wrapFn((symbolRef = ref))));
9843
10095
  }
9844
10096
  if (typeof symbolRef === 'object' && isPromise(symbolRef)) {
9845
10097
  symbolRef.then(() => emitUsedSymbol(symbol, ctx?.$element$, start), (err) => {
@@ -9862,10 +10114,9 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9862
10114
  return invoke;
9863
10115
  }
9864
10116
  };
9865
- const resolvedSymbol = refSymbol ?? symbol;
9866
- const hash = getSymbolHash(resolvedSymbol);
10117
+ const hash = getSymbolHash(symbol);
9867
10118
  Object.assign(qrl, {
9868
- getSymbol: () => resolvedSymbol,
10119
+ getSymbol: () => symbol,
9869
10120
  getHash: () => hash,
9870
10121
  getCaptured: () => captureRef,
9871
10122
  resolve,
@@ -9873,7 +10124,6 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9873
10124
  $setContainer$: setContainer,
9874
10125
  $chunk$: chunk,
9875
10126
  $symbol$: symbol,
9876
- $refSymbol$: refSymbol,
9877
10127
  $hash$: hash,
9878
10128
  getFn: bindFnToContext,
9879
10129
  $capture$: capture,
@@ -9882,8 +10132,8 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9882
10132
  resolved: undefined,
9883
10133
  });
9884
10134
  if (symbolRef) {
9885
- // Replace symbolRef with (a promise for) the value or wrapped function
9886
- symbolRef = maybeThen(symbolRef, (resolved) => (qrl.resolved = symbolRef = wrapFn(resolved)));
10135
+ // Unwrap any promises
10136
+ symbolRef = maybeThen(symbolRef, (resolved) => (qrl.resolved = wrapFn((symbolRef = resolved))));
9887
10137
  }
9888
10138
  if (isDev) {
9889
10139
  Object.defineProperty(qrl, '_devOnlySymbolRef', {
@@ -9897,20 +10147,6 @@ const createQRL = (chunk, symbol, symbolRef, symbolFn, capture, captureRef, refS
9897
10147
  }
9898
10148
  return qrl;
9899
10149
  };
9900
- const getSymbolHash = (symbolName) => {
9901
- const index = symbolName.lastIndexOf('_');
9902
- if (index > -1) {
9903
- return symbolName.slice(index + 1);
9904
- }
9905
- return symbolName;
9906
- };
9907
- function assertQrl(qrl) {
9908
- if (qDev) {
9909
- if (!isQrl(qrl)) {
9910
- throw new Error('Not a QRL');
9911
- }
9912
- }
9913
- }
9914
10150
  const EMITTED = /*#__PURE__*/ new Set();
9915
10151
  const emitUsedSymbol = (symbol, element, reqTime) => {
9916
10152
  if (!EMITTED.has(symbol)) {
@@ -10023,7 +10259,7 @@ const $ = (expression) => {
10023
10259
  if (!qRuntimeQrl && qDev) {
10024
10260
  throw new Error('Optimizer should replace all usages of $() with some special syntax. If you need to create a QRL manually, use inlinedQrl() instead.');
10025
10261
  }
10026
- return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null, null);
10262
+ return createQRL(null, 's' + runtimeSymbolId++, expression, null, null, null);
10027
10263
  };
10028
10264
  /** @private Use To avoid optimizer replacement */
10029
10265
  const dollar = $;
@@ -10049,7 +10285,7 @@ const sync$ = (fn) => {
10049
10285
  // eslint-disable-next-line no-new-func
10050
10286
  fn = new Function('return ' + fn.toString())();
10051
10287
  }
10052
- return createQRL('', SYNC_QRL, fn, null, null, null, null);
10288
+ return createQRL('', SYNC_QRL, fn, null, null, null);
10053
10289
  };
10054
10290
  /**
10055
10291
  * Extract function into a synchronously loadable QRL.
@@ -10066,7 +10302,7 @@ const _qrlSync = function (fn, serializedFn) {
10066
10302
  serializedFn = fn.toString();
10067
10303
  }
10068
10304
  fn.serialized = serializedFn;
10069
- return createQRL('', SYNC_QRL, fn, null, null, null, null);
10305
+ return createQRL('', SYNC_QRL, fn, null, null, null);
10070
10306
  };
10071
10307
 
10072
10308
  /** @internal */
@@ -10785,6 +11021,21 @@ const useVisibleTaskQrl = (qrl, opts) => {
10785
11021
  iCtx.$container$.$scheduler$(ChoreType.VISIBLE, task);
10786
11022
  }
10787
11023
  };
11024
+ const useRunTask = (task, eagerness) => {
11025
+ if (eagerness === 'intersection-observer') {
11026
+ useOn('qvisible', getTaskHandlerQrl(task));
11027
+ }
11028
+ else if (eagerness === 'document-ready') {
11029
+ useOnDocument('qinit', getTaskHandlerQrl(task));
11030
+ }
11031
+ else if (eagerness === 'document-idle') {
11032
+ useOnDocument('qidle', getTaskHandlerQrl(task));
11033
+ }
11034
+ };
11035
+ const getTaskHandlerQrl = (task) => {
11036
+ const taskHandler = createQRL(null, '_task', scheduleTask, null, null, [task]);
11037
+ return taskHandler;
11038
+ };
10788
11039
 
10789
11040
  // <docs markdown="../readme.md#useResource">
10790
11041
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
@@ -11095,5 +11346,5 @@ const PrefetchGraph = (opts = {}) => {
11095
11346
  return _jsxSorted('script', null, props, null, 0, 'prefetch-graph');
11096
11347
  };
11097
11348
 
11098
- export { $, Fragment, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, EMPTY_ARRAY as _EMPTY_ARRAY, EffectPropData as _EffectData, _IMMUTABLE, _SharedContainer, _VAR_PROPS, _deserialize, _fnSignal, _getContextElement, _getContextEvent, getDomContainer as _getDomContainer, _getQContainerElement, _hW, isJSXNode as _isJSXNode, isStringifiable as _isStringifiable, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, _noopQrl, _noopQrlDEV, _qrlSync, _regSymbol, _restProps, _serialize, verifySerializable as _verifySerializable, _waitUntilRendered, _walkJSX, _weakSerialize, _wrapProp, _wrapSignal, component$, componentQrl, createComputed$, createComputedQrl, createContextId, h as createElement, createSignal, event$, eventQrl, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsx as jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
11349
+ export { $, Fragment, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, SubscriptionData as _EffectData, _IMMUTABLE, _SharedContainer, _VAR_PROPS, _deserialize, _fnSignal, _getContextElement, _getContextEvent, getDomContainer as _getDomContainer, _getQContainerElement, isJSXNode as _isJSXNode, isStringifiable as _isStringifiable, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, _noopQrl, _noopQrlDEV, _qrlSync, _regSymbol, _restProps, queueQRL as _run, _serialize, scheduleTask as _task, verifySerializable as _verifySerializable, _waitUntilRendered, _walkJSX, _weakSerialize, _wrapProp, _wrapSignal, component$, componentQrl, createComputed$, createComputedQrl, createContextId, h as createElement, createSignal, event$, eventQrl, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsx as jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
11099
11350
  //# sourceMappingURL=core.mjs.map