@qwik.dev/core 2.0.0-beta.27 → 2.0.0-beta.29

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.
package/dist/core.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * @qwik.dev/core 2.0.0-beta.27-dev+7fc6984
3
+ * @qwik.dev/core 2.0.0-beta.29-dev+bc61b71
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
@@ -270,11 +270,8 @@ function assertQrl(qrl) {
270
270
  }
271
271
  }
272
272
  const getSymbolHash = (symbolName) => {
273
- const index = symbolName.lastIndexOf('_');
274
- if (index > -1) {
275
- return symbolName.slice(index + 1);
276
- }
277
- return symbolName;
273
+ const index = symbolName.lastIndexOf('_') + 1;
274
+ return symbolName.slice(index);
278
275
  };
279
276
 
280
277
  /**
@@ -385,6 +382,8 @@ function decodeVNodeDataString(str) {
385
382
 
386
383
  /** State factory of the component. */
387
384
  const OnRenderProp = 'q:renderFn';
385
+ /** Target DOM element for external projection rendering. */
386
+ const QTargetElement = 'q:targetEl';
388
387
  /** Component style content prefix */
389
388
  const ComponentStylesPrefixContent = '⚡️';
390
389
  /** `<some-element q:slot="...">` */
@@ -1008,7 +1007,7 @@ const COMMA = ',';
1008
1007
  *
1009
1008
  * @public
1010
1009
  */
1011
- const version = "2.0.0-beta.27-dev+7fc6984";
1010
+ const version = "2.0.0-beta.29-dev+bc61b71";
1012
1011
 
1013
1012
  // keep this import from core/build so the cjs build works
1014
1013
  const createPlatform = () => {
@@ -2073,7 +2072,7 @@ class ComputedSignalImpl extends SignalImpl {
2073
2072
  16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */) {
2074
2073
  // The value is used for comparison when signals trigger, which can only happen
2075
2074
  // when it was calculated before. Therefore we can pass whatever we like.
2076
- super(container || fn.$container$, NEEDS_COMPUTATION);
2075
+ super(container || tryGetInvokeContext()?.$container$, NEEDS_COMPUTATION);
2077
2076
  this.$computeQrl$ = fn;
2078
2077
  this.$flags$ = flags;
2079
2078
  }
@@ -6324,14 +6323,14 @@ const createFastGetter = (propName) => {
6324
6323
  //////////////////////////////////////////////////////////////////////////////////////////////////////
6325
6324
  const vnode_newElement = (element, elementName, key = null) => {
6326
6325
  isDev && assertEqual(fastNodeType(element), 1 /* ELEMENT_NODE */, 'Expecting element node.');
6327
- const vnode = new ElementVNode(key, 1 /* VNodeFlags.Element */ | 8 /* VNodeFlags.Inflated */ | (-1 << 11 /* VNodeFlagsIndex.shift */), // Flag
6326
+ const vnode = new ElementVNode(key, 1 /* VNodeFlags.Element */ | 8 /* VNodeFlags.Inflated */ | (-1 << 12 /* VNodeFlagsIndex.shift */), // Flag
6328
6327
  null, null, null, null, null, null, element, elementName);
6329
6328
  element.vNode = vnode;
6330
6329
  return vnode;
6331
6330
  };
6332
6331
  const vnode_newUnMaterializedElement = (element) => {
6333
6332
  isDev && assertEqual(fastNodeType(element), 1 /* ELEMENT_NODE */, 'Expecting element node.');
6334
- const vnode = new ElementVNode(null, 1 /* VNodeFlags.Element */ | (-1 << 11 /* VNodeFlagsIndex.shift */), // Flag
6333
+ const vnode = new ElementVNode(null, 1 /* VNodeFlags.Element */ | (-1 << 12 /* VNodeFlagsIndex.shift */), // Flag
6335
6334
  null, null, null, null, undefined, undefined, element, undefined);
6336
6335
  element.vNode = vnode;
6337
6336
  return vnode;
@@ -6340,7 +6339,7 @@ const vnode_newSharedText = (previousTextNode, sharedTextNode, textContent) => {
6340
6339
  isDev &&
6341
6340
  sharedTextNode &&
6342
6341
  assertEqual(fastNodeType(sharedTextNode), 3 /* TEXT_NODE */, 'Expecting text node.');
6343
- const vnode = new TextVNode(4 /* VNodeFlags.Text */ | (-1 << 11 /* VNodeFlagsIndex.shift */), // Flag
6342
+ const vnode = new TextVNode(4 /* VNodeFlags.Text */ | (-1 << 12 /* VNodeFlagsIndex.shift */), // Flag
6344
6343
  null, // Parent
6345
6344
  previousTextNode, // Previous TextNode (usually first child)
6346
6345
  null, // Next sibling
@@ -6348,7 +6347,7 @@ const vnode_newSharedText = (previousTextNode, sharedTextNode, textContent) => {
6348
6347
  return vnode;
6349
6348
  };
6350
6349
  const vnode_newText = (textNode, textContent) => {
6351
- const vnode = new TextVNode(4 /* VNodeFlags.Text */ | 8 /* VNodeFlags.Inflated */ | (-1 << 11 /* VNodeFlagsIndex.shift */), // Flags
6350
+ const vnode = new TextVNode(4 /* VNodeFlags.Text */ | 8 /* VNodeFlags.Inflated */ | (-1 << 12 /* VNodeFlagsIndex.shift */), // Flags
6352
6351
  null, // Parent
6353
6352
  null, // No previous sibling
6354
6353
  null, // We may have a next sibling.
@@ -6362,7 +6361,7 @@ const vnode_newText = (textNode, textContent) => {
6362
6361
  return vnode;
6363
6362
  };
6364
6363
  const vnode_newVirtual = () => {
6365
- const vnode = new VirtualVNode(null, 2 /* VNodeFlags.Virtual */ | (-1 << 11 /* VNodeFlagsIndex.shift */), // Flags
6364
+ const vnode = new VirtualVNode(null, 2 /* VNodeFlags.Virtual */ | (-1 << 12 /* VNodeFlagsIndex.shift */), // Flags
6366
6365
  null, null, null, null, null, null);
6367
6366
  isDev && assertFalse(vnode_isElementVNode(vnode), 'Incorrect format of TextVNode.');
6368
6367
  isDev && assertFalse(vnode_isTextVNode(vnode), 'Incorrect format of TextVNode.');
@@ -6522,8 +6521,7 @@ function registerQrlHandlers(attr, key, container, element) {
6522
6521
  const scopedKebabName = key.slice(2);
6523
6522
  const qrls = value.split('|');
6524
6523
  const handlers = qrls.map((qrl) => {
6525
- const handler = parseQRL(qrl);
6526
- handler.$container$ = container;
6524
+ const handler = parseQRL(qrl, container);
6527
6525
  // These QRLs are mostly _run and _task and don't need wrapping with retryOnPromise
6528
6526
  return handler;
6529
6527
  });
@@ -6818,7 +6816,7 @@ const vnode_locate = (rootVNode, id) => {
6818
6816
  const vnode_getChildWithIdx = (vNode, childIdx) => {
6819
6817
  let child = vnode_getFirstChild(vNode);
6820
6818
  isDev && assertDefined(child, 'Missing child.');
6821
- while (child.flags >>> 11 /* VNodeFlagsIndex.shift */ !== childIdx) {
6819
+ while (child.flags >>> 12 /* VNodeFlagsIndex.shift */ !== childIdx) {
6822
6820
  child = child.nextSibling;
6823
6821
  isDev && assertDefined(child, 'Missing child.');
6824
6822
  }
@@ -6894,6 +6892,7 @@ const vnode_createErrorDiv = (journal, document, host, err) => {
6894
6892
  }
6895
6893
  return vErrorDiv;
6896
6894
  };
6895
+ const vnode_applyJournal = _flushJournal;
6897
6896
  //////////////////////////////////////////////////////////////////////////////////////////////////////
6898
6897
  const vnode_insertElementBefore = (journal, parent, newChild, insertBefore) => {
6899
6898
  ensureElementOrVirtualVNode(parent);
@@ -6949,8 +6948,15 @@ const vnode_insertVirtualBefore = (journal, parent, newChild, insertBefore) => {
6949
6948
  }
6950
6949
  vnode_unlinkFromOldParent(journal, newChildCurrentParent, parent, newChild);
6951
6950
  const parentIsDeleted = parent.flags & 32 /* VNodeFlags.Deleted */;
6952
- const domParentVNode = parentIsElement ? parent : vnode_getDomParentVNode(parent, false);
6953
- const parentNode = domParentVNode?.node;
6951
+ const targetEl = !parentIsElement && parent.flags & 2048 /* VNodeFlags.HasTargetElement */
6952
+ ? parent.props?.[QTargetElement]
6953
+ : null;
6954
+ const domParentVNode = targetEl
6955
+ ? null
6956
+ : parentIsElement
6957
+ ? parent
6958
+ : vnode_getDomParentVNode(parent, false);
6959
+ const parentNode = targetEl || domParentVNode?.node;
6954
6960
  const adjustedInsertBefore = vnode_findInsertBefore(journal, parent, insertBefore);
6955
6961
  const adjustedInsertBeforeNode = adjustedInsertBefore?.node ?? null;
6956
6962
  const isProjection = vnode_isProjection(newChild);
@@ -7139,8 +7145,13 @@ const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
7139
7145
  }
7140
7146
  };
7141
7147
  const vnode_getDomParent = (vnode, includeProjection) => {
7142
- vnode = vnode_getDomParentVNode(vnode, includeProjection);
7143
- return (vnode && vnode.node);
7148
+ while (vnode && !vnode_isElementVNode(vnode)) {
7149
+ if (vnode.flags & 2048 /* VNodeFlags.HasTargetElement */) {
7150
+ return vnode.props?.[QTargetElement];
7151
+ }
7152
+ vnode = (vnode.parent || (includeProjection ? vnode.slotParent : null));
7153
+ }
7154
+ return vnode ? vnode.node : null;
7144
7155
  };
7145
7156
  const vnode_getDomParentVNode = (vnode, includeProjection) => {
7146
7157
  while (vnode && !vnode_isElementVNode(vnode)) {
@@ -7346,7 +7357,9 @@ const fastNextSibling = (node) => {
7346
7357
  return getNodeAfterCommentNode(node, QContainerIsland, _fastNextSibling, _fastFirstChild);
7347
7358
  }
7348
7359
  else if (node.nodeValue?.startsWith(QContainerIslandEnd)) {
7349
- return getNodeAfterCommentNode(node, QIgnoreEnd, _fastNextSibling, _fastFirstChild);
7360
+ // Search for either the next container-island or the end of the q:ignore block,
7361
+ // whichever comes first. This handles multiple islands within a single q:ignore.
7362
+ return getNodeAfterCommentNode(node, [QContainerIsland, QIgnoreEnd], _fastNextSibling, _fastFirstChild);
7350
7363
  }
7351
7364
  else if (nodeValue?.startsWith(QContainerAttr)) {
7352
7365
  while (node && (node = _fastNextSibling.call(node))) {
@@ -7362,8 +7375,23 @@ const fastNextSibling = (node) => {
7362
7375
  return node;
7363
7376
  };
7364
7377
  function getNodeAfterCommentNode(node, commentValue, nextSibling, firstChild) {
7378
+ const isSingleValue = typeof commentValue === 'string';
7379
+ const length = commentValue.length;
7365
7380
  while (node) {
7366
- if (node.nodeValue?.startsWith(commentValue)) {
7381
+ const nodeValue = node.nodeValue;
7382
+ let isMatch;
7383
+ if (isSingleValue) {
7384
+ isMatch = nodeValue?.startsWith(commentValue);
7385
+ }
7386
+ else {
7387
+ for (let i = 0; i < length; i++) {
7388
+ if (nodeValue?.startsWith(commentValue[i])) {
7389
+ isMatch = true;
7390
+ break;
7391
+ }
7392
+ }
7393
+ }
7394
+ if (isMatch) {
7367
7395
  node = nextSibling.call(node) || null;
7368
7396
  return node;
7369
7397
  }
@@ -7371,11 +7399,14 @@ function getNodeAfterCommentNode(node, commentValue, nextSibling, firstChild) {
7371
7399
  if (!nextNode) {
7372
7400
  nextNode = nextSibling.call(node);
7373
7401
  }
7374
- if (!nextNode) {
7402
+ // Go up through parents until we find one with a next sibling
7403
+ while (!nextNode) {
7375
7404
  nextNode = fastParentNode(node);
7376
- if (nextNode) {
7377
- nextNode = nextSibling.call(nextNode);
7405
+ if (!nextNode) {
7406
+ break;
7378
7407
  }
7408
+ node = nextNode;
7409
+ nextNode = nextSibling.call(nextNode);
7379
7410
  }
7380
7411
  node = nextNode;
7381
7412
  }
@@ -7388,6 +7419,16 @@ const fastFirstChild = (node) => {
7388
7419
  _fastFirstChild = fastGetter(node, 'firstChild');
7389
7420
  }
7390
7421
  node = node && _fastFirstChild.call(node);
7422
+ // Handle q:ignore as first child (e.g. qwikify$ Host with reactify$ projections).
7423
+ // Navigate depth-first to the first q:container-island and return its first element.
7424
+ if (node &&
7425
+ fastNodeType(node) === /* Node.COMMENT_NODE */ 8 &&
7426
+ node.nodeValue?.startsWith(QIgnore)) {
7427
+ if (!_fastNextSibling) {
7428
+ _fastNextSibling = fastGetter(node, 'nextSibling');
7429
+ }
7430
+ return getNodeAfterCommentNode(node, QContainerIsland, _fastNextSibling, _fastFirstChild);
7431
+ }
7391
7432
  while (node && !fastIsTextOrElement(node)) {
7392
7433
  node = fastNextSibling(node);
7393
7434
  }
@@ -7590,7 +7631,7 @@ function vnode_toString(depth = 20, offset = '', materialize = false, siblings =
7590
7631
  strings.push(qwikDebugToString(vnode_getText(vnode)));
7591
7632
  }
7592
7633
  else if (vnode_isVirtualVNode(vnode)) {
7593
- const idx = vnode.flags >>> 11 /* VNodeFlagsIndex.shift */;
7634
+ const idx = vnode.flags >>> 12 /* VNodeFlagsIndex.shift */;
7594
7635
  const attrs = ['[' + String(idx) + ']'];
7595
7636
  if (vnode.dirty) {
7596
7637
  attrs.push(` dirty:${vnode.dirty}`);
@@ -7678,7 +7719,7 @@ function materializeFromVNodeData(vParent, vData, element, child) {
7678
7719
  let vLast = null;
7679
7720
  let previousTextNode = null;
7680
7721
  const addVNode = (node) => {
7681
- node.flags = (node.flags & 2047 /* VNodeFlagsIndex.mask */) | (idx << 11 /* VNodeFlagsIndex.shift */);
7722
+ node.flags = (node.flags & 4095 /* VNodeFlagsIndex.mask */) | (idx << 12 /* VNodeFlagsIndex.shift */);
7682
7723
  idx++;
7683
7724
  vLast && (vLast.nextSibling = node);
7684
7725
  node.previousSibling = vLast;
@@ -8498,7 +8539,8 @@ function qrlToString(serializationContext, qrl, raw) {
8498
8539
  const backChannel = (globalThis.__qrl_back_channel__ ||=
8499
8540
  new Map());
8500
8541
  // During tests the resolved value is always available
8501
- backChannel.set(qrl.$symbol$, qrl.$symbolRef$);
8542
+ const lazy = qrl.$lazy$;
8543
+ backChannel.set(lazy.$symbol$, lazy.$ref$);
8502
8544
  if (!chunk) {
8503
8545
  chunk = QRL_RUNTIME_CHUNK;
8504
8546
  }
@@ -8531,7 +8573,7 @@ function qrlToString(serializationContext, qrl, raw) {
8531
8573
  }
8532
8574
  return qrlStringInline;
8533
8575
  }
8534
- function createQRLWithBackChannel(chunk, symbol, captures) {
8576
+ function createQRLWithBackChannel(chunk, symbol, captures, container) {
8535
8577
  let qrlImporter = null;
8536
8578
  if (isDev && chunk === QRL_RUNTIME_CHUNK) {
8537
8579
  const backChannel = globalThis.__qrl_back_channel__;
@@ -8541,12 +8583,12 @@ function createQRLWithBackChannel(chunk, symbol, captures) {
8541
8583
  qrlImporter = () => Promise.resolve({ [symbol]: fn });
8542
8584
  }
8543
8585
  }
8544
- return createQRL(chunk, symbol, null, qrlImporter, captures);
8586
+ return createQRL(chunk, symbol, null, qrlImporter, captures, container);
8545
8587
  }
8546
8588
  /** Parses "chunk#hash#...rootRef" */
8547
- function parseQRL(qrl) {
8589
+ function parseQRL(qrl, container) {
8548
8590
  const [chunk, symbol, captures] = qrl.split('#');
8549
- return createQRLWithBackChannel(chunk, symbol, captures || null);
8591
+ return createQRLWithBackChannel(chunk, symbol, captures || null, container);
8550
8592
  }
8551
8593
  const QRL_RUNTIME_CHUNK = 'mock-chunk';
8552
8594
 
@@ -8583,13 +8625,12 @@ const allocate = (container, typeId, value) => {
8583
8625
  const [chunkId, symbolId, captureIds] = value.split('#');
8584
8626
  const chunk = container.$getObjectById$(chunkId);
8585
8627
  const symbol = container.$getObjectById$(symbolId);
8586
- qrl = createQRLWithBackChannel(chunk, symbol, captureIds || null);
8628
+ qrl = createQRLWithBackChannel(chunk, symbol, captureIds || null, container);
8587
8629
  }
8588
8630
  else {
8589
8631
  // Sync qrl
8590
- qrl = createQRLWithBackChannel('', String(value), null);
8632
+ qrl = createQRLWithBackChannel('', String(value), null, container);
8591
8633
  }
8592
- qrl.$container$ = container;
8593
8634
  return qrl;
8594
8635
  }
8595
8636
  case 20 /* TypeIds.Task */:
@@ -8796,19 +8837,19 @@ const _noopQrl = (symbolName, lexicalScopeCapture) => {
8796
8837
  /** @internal */
8797
8838
  const _noopQrlDEV = (symbolName, opts, lexicalScopeCapture) => {
8798
8839
  const newQrl = _noopQrl(symbolName, lexicalScopeCapture);
8799
- newQrl.dev = opts;
8840
+ newQrl.$lazy$.dev = opts;
8800
8841
  return newQrl;
8801
8842
  };
8802
8843
  /** @internal */
8803
8844
  const qrlDEV = (chunkOrFn, symbol, opts, lexicalScopeCapture) => {
8804
8845
  const newQrl = qrl(chunkOrFn, symbol, lexicalScopeCapture, 1);
8805
- newQrl.dev = opts;
8846
+ newQrl.$lazy$.dev = opts;
8806
8847
  return newQrl;
8807
8848
  };
8808
8849
  /** @internal */
8809
8850
  const inlinedQrlDEV = (symbol, symbolName, opts, lexicalScopeCapture) => {
8810
8851
  const qrl = inlinedQrl(symbol, symbolName, lexicalScopeCapture);
8811
- qrl.dev = opts;
8852
+ qrl.$lazy$.dev = opts;
8812
8853
  return qrl;
8813
8854
  };
8814
8855
  /**
@@ -10104,8 +10145,7 @@ const useResourceQrl = (qrl, opts) => {
10104
10145
  }
10105
10146
  const ref = {};
10106
10147
  // Wrap the function so we can maintain a stable reference to the store
10107
- const wrapped = createQRL(null, '_rsc', _rsc, null, [qrl, ref]);
10108
- qrl.$container$ = iCtx.$container$;
10148
+ const wrapped = createQRL(null, '_rsc', _rsc, null, [qrl, ref], iCtx.$container$);
10109
10149
  const asyncSignal = createAsyncSignal(wrapped, {
10110
10150
  timeout: opts?.timeout,
10111
10151
  container: iCtx.$container$,
@@ -10235,7 +10275,299 @@ function addQwikEventToSerializationContext(serializationCtx, key, qrl) {
10235
10275
  }
10236
10276
  }
10237
10277
 
10278
+ // <docs markdown="../readme.md#useOn">
10279
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
10280
+ // (edit ../readme.md#useOn instead and run `pnpm docs.sync`)
10281
+ /**
10282
+ * Register a listener on the current component's host element.
10283
+ *
10284
+ * Used to programmatically add event listeners. Useful from custom `use*` methods, which do not
10285
+ * have access to the JSX. Otherwise, it's adding a JSX listener in the `<div>` is a better idea.
10286
+ *
10287
+ * Events are case sensitive.
10288
+ *
10289
+ * @public
10290
+ * @see `useOn`, `useOnWindow`, `useOnDocument`.
10291
+ */
10292
+ // </docs>
10293
+ const useOn = (event, eventQrl) => {
10294
+ _useOn("q-e:" /* EventNameHtmlScope.on */, event, eventQrl);
10295
+ };
10296
+ // <docs markdown="../readme.md#useOnDocument">
10297
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
10298
+ // (edit ../readme.md#useOnDocument instead and run `pnpm docs.sync`)
10299
+ /**
10300
+ * Register a listener on `document`.
10301
+ *
10302
+ * Used to programmatically add event listeners. Useful from custom `use*` methods, which do not
10303
+ * have access to the JSX.
10304
+ *
10305
+ * Events are case sensitive.
10306
+ *
10307
+ * @public
10308
+ * @see `useOn`, `useOnWindow`, `useOnDocument`.
10309
+ *
10310
+ * ```tsx
10311
+ * function useScroll() {
10312
+ * useOnDocument(
10313
+ * 'scroll',
10314
+ * $((event) => {
10315
+ * console.log('body scrolled', event);
10316
+ * })
10317
+ * );
10318
+ * }
10319
+ *
10320
+ * const Cmp = component$(() => {
10321
+ * useScroll();
10322
+ * return <div>Profit!</div>;
10323
+ * });
10324
+ * ```
10325
+ */
10326
+ // </docs>
10327
+ const useOnDocument = (event, eventQrl) => {
10328
+ _useOn("q-d:" /* EventNameHtmlScope.document */, event, eventQrl);
10329
+ };
10330
+ // <docs markdown="../readme.md#useOnWindow">
10331
+ // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
10332
+ // (edit ../readme.md#useOnWindow instead and run `pnpm docs.sync`)
10333
+ /**
10334
+ * Register a listener on `window`.
10335
+ *
10336
+ * Used to programmatically add event listeners. Useful from custom `use*` methods, which do not
10337
+ * have access to the JSX.
10338
+ *
10339
+ * Events are case sensitive.
10340
+ *
10341
+ * @public
10342
+ * @see `useOn`, `useOnWindow`, `useOnDocument`.
10343
+ *
10344
+ * ```tsx
10345
+ * function useAnalytics() {
10346
+ * useOnWindow(
10347
+ * 'popstate',
10348
+ * $((event) => {
10349
+ * console.log('navigation happened', event);
10350
+ * // report to analytics
10351
+ * })
10352
+ * );
10353
+ * }
10354
+ *
10355
+ * const Cmp = component$(() => {
10356
+ * useAnalytics();
10357
+ * return <div>Profit!</div>;
10358
+ * });
10359
+ * ```
10360
+ */
10361
+ // </docs>
10362
+ const useOnWindow = (event, eventQrl) => {
10363
+ _useOn("q-w:" /* EventNameHtmlScope.window */, event, eventQrl);
10364
+ };
10365
+ const _useOn = (prefix, eventName, eventQrl) => {
10366
+ const { isAdded, addEvent } = useOnEventsSequentialScope();
10367
+ if (isAdded) {
10368
+ return;
10369
+ }
10370
+ if (eventQrl) {
10371
+ if (Array.isArray(eventName)) {
10372
+ for (const event of eventName) {
10373
+ addEvent(prefix + fromCamelToKebabCase(event), eventQrl);
10374
+ }
10375
+ }
10376
+ else {
10377
+ addEvent(prefix + fromCamelToKebabCase(eventName), eventQrl);
10378
+ }
10379
+ }
10380
+ };
10381
+ /**
10382
+ * This hook is like the `useSequentialScope` but it is specifically for `useOn`. This is needed
10383
+ * because we want to execute the `useOn` hooks only once and store the event listeners on the host
10384
+ * element. From Qwik V2 the component is rerunning when the promise is thrown, so we need to make
10385
+ * sure that the event listeners are not added multiple times.
10386
+ *
10387
+ * - The event listeners are stored in the `USE_ON_LOCAL` property.
10388
+ * - The `USE_ON_LOCAL_SEQ_IDX` is used to keep track of the index of the hook that calls this.
10389
+ * - The `USE_ON_LOCAL_FLAGS` is used to keep track of whether the event listener has been added or
10390
+ * not.
10391
+ */
10392
+ const useOnEventsSequentialScope = () => {
10393
+ const iCtx = useInvokeContext();
10394
+ const hostElement = iCtx.$hostElement$;
10395
+ const host = hostElement;
10396
+ let onMap = iCtx.$container$.getHostProp(host, USE_ON_LOCAL);
10397
+ if (onMap === null) {
10398
+ onMap = {};
10399
+ iCtx.$container$.setHostProp(host, USE_ON_LOCAL, onMap);
10400
+ }
10401
+ let seqIdx = iCtx.$container$.getHostProp(host, USE_ON_LOCAL_SEQ_IDX);
10402
+ if (seqIdx === null) {
10403
+ seqIdx = 0;
10404
+ }
10405
+ iCtx.$container$.setHostProp(host, USE_ON_LOCAL_SEQ_IDX, seqIdx + 1);
10406
+ let addedFlags = iCtx.$container$.getHostProp(host, USE_ON_LOCAL_FLAGS);
10407
+ if (addedFlags === null) {
10408
+ addedFlags = [];
10409
+ iCtx.$container$.setHostProp(host, USE_ON_LOCAL_FLAGS, addedFlags);
10410
+ }
10411
+ while (addedFlags.length <= seqIdx) {
10412
+ addedFlags.push(false);
10413
+ }
10414
+ const addEvent = (eventName, eventQrl) => {
10415
+ addedFlags[seqIdx] = true;
10416
+ let events = onMap[eventName];
10417
+ if (!events) {
10418
+ onMap[eventName] = events = [];
10419
+ }
10420
+ events.push(eventQrl);
10421
+ };
10422
+ return {
10423
+ isAdded: addedFlags[seqIdx],
10424
+ addEvent,
10425
+ };
10426
+ };
10427
+
10428
+ /**
10429
+ * HMR event handler. Replaces the component QRL with a fresh one and marks dirty.
10430
+ *
10431
+ * @internal
10432
+ */
10433
+ const _hmr = (event, element) => {
10434
+ const ctx = newInvokeContextFromDOM(event, element);
10435
+ const container = ctx.$container$;
10436
+ let host = ctx.$hostElement$;
10437
+ if (!container || !host) {
10438
+ return;
10439
+ }
10440
+ // host is a VNode from vnode_locate. Walk up to the nearest component VirtualVNode
10441
+ // (the one with OnRenderProp) so we can replace its QRL and mark it dirty.
10442
+ if (!container.getHostProp(host, OnRenderProp)) {
10443
+ const parent = container.getParentHost(host);
10444
+ if (!parent) {
10445
+ return;
10446
+ }
10447
+ host = parent;
10448
+ }
10449
+ // Replace the component QRL with a fresh one to bypass caching
10450
+ // Maybe we should use a qrl registry to invalidate all QRLs from a parent?
10451
+ const qrl = container.getHostProp(host, OnRenderProp);
10452
+ if (qrl) {
10453
+ // This code is highly coupled to the internal implementation of QRL
10454
+ const instance = qrl.__proto__;
10455
+ const lazy = instance.$lazy$;
10456
+ const chunk = lazy.$chunk$;
10457
+ if (chunk) {
10458
+ /**
10459
+ * Bust the cache by appending a timestamp to the chunk URL. There's some chance that we
10460
+ * import the same chunk multiple times, but that's not really a problem for segments.
10461
+ */
10462
+ const bustUrl = chunk.split('?')[0] + '?t=' + Date.now();
10463
+ lazy.$chunk$ = bustUrl;
10464
+ lazy.$ref$ = undefined;
10465
+ instance.resolved = undefined;
10466
+ // Force rerender
10467
+ markVNodeDirty(container, host, 4 /* ChoreBits.COMPONENT */);
10468
+ }
10469
+ }
10470
+ };
10471
+ /** Sanitize path to a valid CSS-safe event name (no colons, dots, slashes). */
10472
+ const toEventName = (devPath) => 'qHmr' + devPath.replace(/[^a-zA-Z0-9_]/g, '_');
10473
+ let hmrQrl;
10474
+ /**
10475
+ * Injected by the optimizer into component$ bodies in HMR mode. Registers a document event listener
10476
+ * that triggers component re-render on HMR updates.
10477
+ *
10478
+ * @internal
10479
+ */
10480
+ function _useHmr(devPath) {
10481
+ const iCtx = tryGetInvokeContext();
10482
+ if (!iCtx) {
10483
+ return;
10484
+ }
10485
+ hmrQrl ||= inlinedQrl(_hmr, '_hmr');
10486
+ // The event name must be CSS-attribute-safe (no colons, dots) because
10487
+ // the qwikloader uses querySelectorAll('[q-d\\:eventName]') to find handlers.
10488
+ useOnDocument(toEventName(devPath), hmrQrl);
10489
+ }
10490
+
10491
+ /**
10492
+ * Register an external projection on a parent component VNode.
10493
+ *
10494
+ * Creates a new VirtualVNode that will render the given component QRL with the given props. The
10495
+ * VNode is stored as a projection on the parent, and a low-priority cursor is added so the cursor
10496
+ * walker will process it.
10497
+ *
10498
+ * Use `_setProjectionTarget` to set the DOM target element before the cursor fires.
10499
+ *
10500
+ * @internal
10501
+ */
10502
+ function _addProjection(container, parentVNode, componentQRL, props, slotName) {
10503
+ const vnode = vnode_newVirtual();
10504
+ vnode_setProp(vnode, QSlot, slotName);
10505
+ vnode.parent = parentVNode;
10506
+ vnode_setProp(parentVNode, slotName, vnode);
10507
+ vnode_setProp(vnode, OnRenderProp, componentQRL);
10508
+ vnode_setProp(vnode, ELEMENT_PROPS, props);
10509
+ vnode.dirty = 4 /* ChoreBits.COMPONENT */;
10510
+ addCursor(container, vnode, 1); // low priority
10511
+ return vnode;
10512
+ }
10513
+ /**
10514
+ * Set the DOM target element for an external projection VNode.
10515
+ *
10516
+ * When the cursor walker processes this VNode, DOM operations will target this element instead of
10517
+ * walking the parent chain.
10518
+ *
10519
+ * @internal
10520
+ */
10521
+ function _setProjectionTarget(vnode, targetElement) {
10522
+ vnode_setProp(vnode, QTargetElement, targetElement);
10523
+ vnode.flags |= 2048 /* VNodeFlags.HasTargetElement */;
10524
+ }
10525
+ /**
10526
+ * Update the props on an external projection VNode and trigger re-rendering.
10527
+ *
10528
+ * @internal
10529
+ */
10530
+ function _updateProjectionProps(container, vnode, newProps) {
10531
+ vnode_setProp(vnode, ELEMENT_PROPS, newProps);
10532
+ markVNodeDirty(container, vnode, 4 /* ChoreBits.COMPONENT */);
10533
+ }
10534
+ /**
10535
+ * Remove an external projection from its parent and clean up.
10536
+ *
10537
+ * @internal
10538
+ */
10539
+ function _removeProjection(container, parentVNode, vnode, slotName) {
10540
+ // Remove from parent's projections
10541
+ vnode_setProp(parentVNode, slotName, null);
10542
+ // Clean up effects, subscriptions, and child vnodes
10543
+ const journal = [];
10544
+ cleanup(container, journal, vnode);
10545
+ vnode_applyJournal(journal);
10546
+ // Clean up DOM
10547
+ if (vnode.flags & 2048 /* VNodeFlags.HasTargetElement */) {
10548
+ const targetEl = vnode_getProp(vnode, QTargetElement, null);
10549
+ if (targetEl) {
10550
+ targetEl.replaceChildren();
10551
+ }
10552
+ vnode_setProp(vnode, QTargetElement, null);
10553
+ vnode.flags &= -2049 /* VNodeFlags.HasTargetElement */;
10554
+ }
10555
+ }
10556
+
10238
10557
  let loading = Promise.resolve();
10558
+ const dangerousObjectKeys = new Set([
10559
+ 'constructor',
10560
+ 'prototype',
10561
+ 'toString',
10562
+ 'valueOf',
10563
+ 'toJSON',
10564
+ 'then',
10565
+ ]);
10566
+ const isSafeObjectKV = (key, value) => {
10567
+ return (typeof key === 'string' &&
10568
+ key !== '__proto__' &&
10569
+ (typeof value !== 'function' || !dangerousObjectKeys.has(key)));
10570
+ };
10239
10571
  const inflate = (container, target, typeId, data) => {
10240
10572
  if (typeId === 0 /* TypeIds.Plain */) {
10241
10573
  // Already processed
@@ -10258,6 +10590,9 @@ const inflate = (container, target, typeId, data) => {
10258
10590
  for (let i = 0; i < data.length; i += 2) {
10259
10591
  const key = data[i];
10260
10592
  const value = data[i + 1];
10593
+ if (!isSafeObjectKV(key, value)) {
10594
+ continue;
10595
+ }
10261
10596
  target[key] = value;
10262
10597
  }
10263
10598
  break;
@@ -10831,13 +11166,18 @@ function processVNodeData(document) {
10831
11166
  nextNode = null;
10832
11167
  }
10833
11168
  else if (nodeType === 64 /* NodeType.COMMENT_ISLAND_END */) {
11169
+ // Walk forward to find either the next container-island or the end of the q:ignore block.
11170
+ // This handles multiple islands within a single q:ignore block.
10834
11171
  nextNode = node;
11172
+ let nextNodeType;
10835
11173
  do {
10836
11174
  nextNode = walker.nextNode();
10837
11175
  if (!nextNode) {
10838
11176
  throw new Error(`Ignore block not closed!`);
10839
11177
  }
10840
- } while (getFastNodeType(nextNode) !== 32 /* NodeType.COMMENT_IGNORE_END */);
11178
+ nextNodeType = getFastNodeType(nextNode);
11179
+ } while (nextNodeType !== 32 /* NodeType.COMMENT_IGNORE_END */ &&
11180
+ nextNodeType !== 65 /* NodeType.COMMENT_ISLAND_START */);
10841
11181
  nextNode = null;
10842
11182
  }
10843
11183
  else if (nodeType === 9 /* NodeType.COMMENT_SKIP_START */) {
@@ -11042,8 +11382,7 @@ class DomContainer extends _SharedContainer {
11042
11382
  this.$stateData$[id] = vParent;
11043
11383
  }
11044
11384
  parseQRL(qrlStr) {
11045
- const qrl = parseQRL(qrlStr);
11046
- qrl.$container$ = this;
11385
+ const qrl = parseQRL(qrlStr, this);
11047
11386
  return qrl;
11048
11387
  }
11049
11388
  handleError(err, host) {
@@ -12201,6 +12540,198 @@ const NoSerializeSymbol = Symbol('noSerialize');
12201
12540
  const SerializerSymbol = Symbol('serialize');
12202
12541
 
12203
12542
  // keep these imports above the rest to prevent circular dep issues
12543
+ /**
12544
+ * Shared lazy-loading reference that holds module loading metadata. Multiple QRLs pointing to the
12545
+ * same chunk+symbol can share a single LazyRef, differing only in their captured scope.
12546
+ */
12547
+ class LazyRef {
12548
+ $chunk$;
12549
+ $symbol$;
12550
+ $symbolFn$;
12551
+ $ref$;
12552
+ $container$;
12553
+ // Don't allocate dev property immediately so that in prod we don't have this property
12554
+ dev;
12555
+ constructor($chunk$, $symbol$, $symbolFn$, $ref$, container) {
12556
+ this.$chunk$ = $chunk$;
12557
+ this.$symbol$ = $symbol$;
12558
+ this.$symbolFn$ = $symbolFn$;
12559
+ this.$ref$ = $ref$;
12560
+ if ($ref$) {
12561
+ this.$setRef$($ref$);
12562
+ }
12563
+ if (container && !$ref$ && typeof $chunk$ === 'string' && !$symbolFn$) {
12564
+ // We only store the container if we're going to import the chunk
12565
+ // Note that this container is not necessarily the same one as from the captures
12566
+ this.$container$ = container;
12567
+ }
12568
+ if (qDev) {
12569
+ // this will be filled in later
12570
+ this.dev = null;
12571
+ }
12572
+ /** Preload the chunk with somewhat lower probability when we create the QRL. */
12573
+ if (isBrowser && $chunk$) {
12574
+ p($chunk$, 0.8);
12575
+ }
12576
+ }
12577
+ /** We don't read hash very often so let's not allocate a string for every QRL */
12578
+ get $hash$() {
12579
+ return getSymbolHash(this.$symbol$);
12580
+ }
12581
+ $setRef$(ref) {
12582
+ this.$ref$ = ref;
12583
+ if (isPromise(ref)) {
12584
+ ref.then((r) => (this.$ref$ = r), (err) => {
12585
+ console.error(`qrl ${this.$symbol$} failed to load`, err);
12586
+ // We shouldn't cache rejections, we can try again later
12587
+ this.$ref$ = null;
12588
+ });
12589
+ }
12590
+ }
12591
+ /** Load the raw module export without capture binding. */
12592
+ $load$() {
12593
+ if (this.$ref$ != null) {
12594
+ return this.$ref$;
12595
+ }
12596
+ if (this.$chunk$ === '') {
12597
+ // Sync QRL
12598
+ isDev && assertDefined(this.$container$, 'Sync QRL must have container element');
12599
+ const hash = this.$container$.$instanceHash$;
12600
+ const doc = this.$container$.element?.ownerDocument || document;
12601
+ const qFuncs = getQFuncs(doc, hash);
12602
+ return (this.$ref$ = qFuncs[Number(this.$symbol$)]);
12603
+ }
12604
+ if (isBrowser && this.$chunk$) {
12605
+ /** We will run the QRL, so now the probability of the chunk is 100% */
12606
+ p(this.$chunk$, 1);
12607
+ }
12608
+ const symbol = this.$symbol$;
12609
+ const importP = this.$symbolFn$
12610
+ ? this.$symbolFn$().then((module) => module[symbol])
12611
+ : getPlatform().importSymbol(this.$container$?.element, this.$chunk$, symbol);
12612
+ this.$setRef$(importP);
12613
+ return this.$ref$;
12614
+ }
12615
+ }
12616
+ /**
12617
+ * When a method is called on the qrlFn wrapper function, `this` is the function, not the QRLClass
12618
+ * instance that holds the data. This helper returns the actual instance by checking whether `this`
12619
+ * owns `resolved` (always set on the instance).
12620
+ */
12621
+ const getInstance = (instance) => {
12622
+ return Object.prototype.hasOwnProperty.call(instance, 'resolved')
12623
+ ? instance
12624
+ : Object.getPrototypeOf(instance);
12625
+ };
12626
+ /**
12627
+ * We use a class here to avoid copying all the methods for every QRL instance. The QRL itself is a
12628
+ * function that calls the internal $callFn$ method, and we set the prototype to the class instance
12629
+ * so it has access to all the properties and methods. That's why we need to extend Function, so
12630
+ * that `.apply()` etc work.
12631
+ *
12632
+ * So a QRL is a function that has a prototype of a QRLClass instance. This is unconventional, but
12633
+ * it allows us to have a callable QRL that is also a class.
12634
+ *
12635
+ * Note the use of getInstance everywhere when writing to `this`. If you write to `this` directly,
12636
+ * it will be stored on the function itself, and we don't want that because the QRLClass instance
12637
+ * doesn't have access to it, and it uses more memory.
12638
+ */
12639
+ class QRLClass extends Function {
12640
+ $lazy$;
12641
+ resolved = undefined;
12642
+ // This is defined or undefined for the lifetime of the QRL, so we set it lazily
12643
+ $captures$;
12644
+ $container$;
12645
+ constructor($lazy$, $captures$, container) {
12646
+ super();
12647
+ this.$lazy$ = $lazy$;
12648
+ if ($captures$) {
12649
+ this.$captures$ = $captures$;
12650
+ if (typeof $captures$ === 'string') {
12651
+ // We cannot rely on the container of the lazy ref, it may be missing or different
12652
+ this.$container$ = container;
12653
+ }
12654
+ if (qDev) {
12655
+ if ($captures$ && typeof $captures$ === 'object') {
12656
+ for (const item of $captures$) {
12657
+ verifySerializable(item, 'Captured variable in the closure can not be serialized');
12658
+ }
12659
+ }
12660
+ }
12661
+ }
12662
+ // If it is plain value with deserialized or missing captures, resolve it immediately
12663
+ // Otherwise we keep using the async path so we can wait for qrls to load
12664
+ if ($lazy$.$ref$ != null && typeof this.$captures$ !== 'string' && !isPromise($lazy$.$ref$)) {
12665
+ // we can pass this instead of using getInstance because we know we are not the qrlFn
12666
+ this.resolved = bindCaptures(this, $lazy$.$ref$);
12667
+ }
12668
+ }
12669
+ w(captures) {
12670
+ const newQrl = new QRLClass(this.$lazy$, captures, this.$captures$ ? this.$container$ : undefined);
12671
+ return makeQrlFn(newQrl);
12672
+ }
12673
+ s(ref) {
12674
+ const qrl = getInstance(this);
12675
+ qrl.$lazy$.$setRef$(ref);
12676
+ qrl.resolved = bindCaptures(qrl, ref);
12677
+ }
12678
+ // --- Getter proxies for backward compat ---
12679
+ get $chunk$() {
12680
+ return this.$lazy$.$chunk$;
12681
+ }
12682
+ get $symbol$() {
12683
+ return this.$lazy$.$symbol$;
12684
+ }
12685
+ get $hash$() {
12686
+ return this.$lazy$.$hash$;
12687
+ }
12688
+ get dev() {
12689
+ return this.$lazy$.dev;
12690
+ }
12691
+ $callFn$(withThis, ...args) {
12692
+ if (this.resolved) {
12693
+ return this.resolved.apply(withThis, args);
12694
+ }
12695
+ // Not resolved yet: we'll return a promise
12696
+ // grab the context while we are sync
12697
+ const ctx = tryGetInvokeContext();
12698
+ return this.resolve(ctx?.$container$).then(() => invokeApply.call(withThis, ctx, this.resolved, args));
12699
+ }
12700
+ async resolve(container) {
12701
+ // We need to write to the QRLClass instance, not the function
12702
+ const qrl = getInstance(this);
12703
+ return maybeThen($resolve$(qrl, container), () => qrl.resolved);
12704
+ }
12705
+ getSymbol() {
12706
+ return this.$symbol$;
12707
+ }
12708
+ getHash() {
12709
+ return this.$hash$;
12710
+ }
12711
+ getCaptured() {
12712
+ const qrl = getInstance(this);
12713
+ ensureQrlCaptures(qrl);
12714
+ return qrl.$captures$;
12715
+ }
12716
+ getFn(currentCtx, beforeFn) {
12717
+ const qrl = getInstance(this);
12718
+ const bound = (...args) => {
12719
+ if (!qrl.resolved) {
12720
+ return qrl.resolve().then((fn) => {
12721
+ if (qDev && !isFunction(fn)) {
12722
+ throw qError(5 /* QError.qrlIsNotFunction */);
12723
+ }
12724
+ return bound(...args);
12725
+ });
12726
+ }
12727
+ if (beforeFn && beforeFn() === false) {
12728
+ return undefined;
12729
+ }
12730
+ return invokeApply(currentCtx, qrl.resolved, args);
12731
+ };
12732
+ return bound;
12733
+ }
12734
+ }
12204
12735
  /**
12205
12736
  * The current captured scope during QRL invocation. This is used to provide the lexical scope for
12206
12737
  * QRL functions. It is used one time per invocation, synchronously, so it is safe to store it in
@@ -12223,114 +12754,62 @@ const deserializeCaptures = (container, captures) => {
12223
12754
  const ensureQrlCaptures = (qrl) => {
12224
12755
  // We read the captures once, synchronously, so no need to keep previous
12225
12756
  _captures = qrl.$captures$;
12757
+ const container = qrl.$container$;
12226
12758
  if (typeof _captures === 'string') {
12227
- if (!qrl.$container$) {
12759
+ if (!container) {
12228
12760
  throw qError(13 /* QError.qrlMissingContainer */);
12229
12761
  }
12230
12762
  const prevLoading = loading;
12231
- _captures = qrl.$captures$ = deserializeCaptures(qrl.$container$, _captures);
12763
+ _captures = qrl.$captures$ = deserializeCaptures(container, _captures);
12232
12764
  if (loading !== prevLoading) {
12233
12765
  // return the loading promise so callers can await it
12234
12766
  return loading;
12235
12767
  }
12236
12768
  }
12237
12769
  };
12238
- function bindFnToContext(qrl, currentCtx, beforeFn) {
12239
- // Note that we bind the current `this`
12240
- const bound = (...args) => {
12241
- if (!qrl.resolved) {
12242
- return qrl.resolve().then((fn) => {
12243
- if (!isFunction(fn)) {
12244
- throw qError(5 /* QError.qrlIsNotFunction */);
12245
- }
12246
- return bound(...args);
12247
- });
12248
- }
12249
- if (beforeFn && beforeFn() === false) {
12250
- return;
12251
- }
12252
- return invokeApply.call(this, currentCtx, qrl.resolved, args);
12253
- };
12254
- return bound;
12255
- }
12256
12770
  // Wrap functions to provide their lexical scope
12257
- const bindCaptures = (qrl, fn) => {
12258
- if (typeof fn !== 'function' || !qrl.$captures$) {
12259
- return fn;
12771
+ const bindCaptures = (qrl, ref) => {
12772
+ if (typeof ref !== 'function' || !qrl.$captures$) {
12773
+ return ref;
12260
12774
  }
12261
- return function withCaptures(...args) {
12775
+ return function boundCaptures(...args) {
12262
12776
  ensureQrlCaptures(qrl);
12263
- return fn.apply(this, args);
12777
+ return ref.apply(this, args);
12264
12778
  };
12265
12779
  };
12266
- const makeResolveFunction = (qrl, symbolFn) => {
12267
- let symbolRef;
12268
- // Always return a promise, even for sync QRLs
12269
- return async (container) => {
12270
- if (symbolRef != null) {
12271
- // Resolving (Promise) or already resolved (value)
12272
- return symbolRef;
12273
- }
12780
+ const $resolve$ = (qrl, container) => {
12781
+ const lazy = qrl.$lazy$;
12782
+ const shouldDeserialize = typeof qrl.$captures$ === 'string';
12783
+ if (shouldDeserialize && !qrl.$container$) {
12274
12784
  if (container) {
12275
12785
  qrl.$container$ = container;
12276
12786
  }
12277
- else if (!qrl.$container$) {
12278
- const ctx = tryGetInvokeContext();
12279
- if (ctx?.$container$) {
12280
- qrl.$container$ = ctx.$container$;
12281
- }
12282
- }
12283
- if (qrl.$chunk$ === '') {
12284
- // Sync QRL
12285
- isDev && assertDefined(qrl.$container$, 'Sync QRL must have container element');
12286
- const hash = qrl.$container$.$instanceHash$;
12287
- const doc = qrl.$container$.element?.ownerDocument || document;
12288
- const qFuncs = getQFuncs(doc, hash);
12289
- // No need to wrap, syncQRLs can't have captured scope
12290
- return (qrl.resolved = symbolRef = qFuncs[Number(qrl.$symbol$)]);
12291
- }
12292
- if (isBrowser && qrl.$chunk$) {
12293
- /** We run the QRL, so now the probability of the chunk is 100% */
12294
- p(qrl.$chunk$, 1);
12295
- }
12296
- const start = now();
12297
- const symbol = qrl.$symbol$;
12298
- const importP = symbolFn
12299
- ? symbolFn().then((module) => module[symbol])
12300
- : getPlatform().importSymbol(qrl.$container$?.element, qrl.$chunk$, symbol);
12301
- symbolRef = maybeThen(importP, (resolved) => {
12302
- // We memoize the result on the symbolFn
12303
- // Make sure not to memoize the wrapped function!
12304
- if (symbolFn) {
12305
- symbolFn[symbol] = resolved;
12306
- }
12307
- return (symbolRef = qrl.resolved = bindCaptures(qrl, resolved));
12308
- });
12309
- if (isPromise(symbolRef)) {
12310
- const ctx = tryGetInvokeContext();
12311
- symbolRef.then(() => emitUsedSymbol(symbol, ctx?.$hostElement$ instanceof ElementVNode ? ctx?.$hostElement$.node : undefined, start), (err) => {
12312
- console.error(`qrl ${symbol} failed to load`, err);
12313
- // We shouldn't cache rejections, we can try again later
12314
- symbolRef = null;
12315
- });
12316
- }
12317
- // Try to deserialize captures if any
12318
- if (qrl.$container$) {
12319
- await ensureQrlCaptures(qrl);
12787
+ else {
12788
+ qrl.$container$ = tryGetInvokeContext()?.$container$;
12320
12789
  }
12321
- return symbolRef;
12322
- };
12790
+ }
12791
+ if (qrl.resolved) {
12792
+ return;
12793
+ }
12794
+ // Capture context while still sync
12795
+ const start = now();
12796
+ const ctx = tryGetInvokeContext();
12797
+ // Load raw value via LazyRef - may be sync (e.g. sync QRLs) or async
12798
+ const rawOrPromise = lazy.$load$();
12799
+ const maybePromise = maybeThen(rawOrPromise, (raw) => {
12800
+ qrl.resolved = bindCaptures(qrl, raw);
12801
+ });
12802
+ if (maybePromise) {
12803
+ // We're importing; emit symbol usage event
12804
+ const symbol = lazy.$symbol$;
12805
+ emitUsedSymbol(symbol, ctx?.$hostElement$ instanceof ElementVNode ? ctx?.$hostElement$.node : undefined, start);
12806
+ }
12807
+ const capturedPromise = shouldDeserialize && qrl.$container$ && ensureQrlCaptures(qrl);
12808
+ if (capturedPromise) {
12809
+ return capturedPromise.then(() => maybePromise);
12810
+ }
12811
+ return maybePromise;
12323
12812
  };
12324
- function getSymbol() {
12325
- return this.$symbol$;
12326
- }
12327
- function getHash() {
12328
- return this.$hash$;
12329
- }
12330
- function getCaptured() {
12331
- ensureQrlCaptures(this);
12332
- return this.$captures$;
12333
- }
12334
12813
  /**
12335
12814
  * Creates a QRL instance to represent a lazily loaded value. Normally this is a function, but it
12336
12815
  * can be any value.
@@ -12344,64 +12823,19 @@ function getCaptured() {
12344
12823
  *
12345
12824
  * @internal
12346
12825
  */
12347
- const createQRL = (chunk, symbol, symbolRef, symbolFn, captures) => {
12348
- // In dev mode we need to preserve the original symbolRef without wrapping
12349
- const origSymbolRef = symbolRef;
12350
- if (qDev && qSerialize) {
12351
- if (captures && typeof captures === 'object') {
12352
- for (const item of captures) {
12353
- verifySerializable(item, 'Captured variable in the closure can not be serialized');
12354
- }
12355
- }
12356
- }
12357
- const qrl = async function qrlFn(...args) {
12358
- if (qrl.resolved) {
12359
- return qrl.resolved.apply(this, args);
12360
- }
12361
- // grab the context while we are sync
12362
- const ctx = tryGetInvokeContext();
12363
- await qrl.resolve(ctx?.$container$);
12364
- return invokeApply.call(this, ctx, qrl.resolved, args);
12826
+ const createQRL = (chunk, symbol, symbolRef, symbolFn, captures, container) => {
12827
+ const lazy = new LazyRef(chunk, symbol, symbolFn, symbolRef, container);
12828
+ const qrl = new QRLClass(lazy, captures, container);
12829
+ return makeQrlFn(qrl);
12830
+ };
12831
+ const makeQrlFn = (qrl) => {
12832
+ // The QRL has to be callable, so we create a function that calls the internal $callFn$
12833
+ const qrlFn = async function (...args) {
12834
+ return qrl.$callFn$(this, ...args);
12365
12835
  };
12366
- // Retrieve memoized result from symbolFn
12367
- if (symbolFn && symbol in symbolFn) {
12368
- symbolRef = symbolFn[symbol];
12369
- }
12370
- const resolve = symbolRef != null ? async () => symbolRef : makeResolveFunction(qrl, symbolFn);
12371
- const hash = getSymbolHash(symbol);
12372
- Object.assign(qrl, {
12373
- getSymbol,
12374
- getHash,
12375
- getCaptured,
12376
- // This can be called with other `this`
12377
- getFn: function (currentCtx, beforeFn) {
12378
- return bindFnToContext.call(this, qrl, currentCtx, beforeFn);
12379
- },
12380
- resolve,
12381
- resolved: undefined,
12382
- $chunk$: chunk,
12383
- $symbol$: symbol,
12384
- $hash$: hash,
12385
- $captures$: captures,
12386
- $container$: null,
12387
- });
12388
- if (qDev) {
12389
- qrl.dev = null;
12390
- qrl.$symbolRef$ = origSymbolRef;
12391
- seal(qrl);
12392
- }
12393
- // Now that the qrl is fully constructed, we can resolve/wrap the symbolRef if we received it. If it is a plain value without computed captures, the qrl will be resolved immediately.
12394
- if (symbolRef != null) {
12395
- symbolRef = maybeThen(ensureQrlCaptures(qrl), () => maybeThen(symbolRef, (resolved) => {
12396
- symbolRef = qrl.resolved = bindCaptures(qrl, resolved);
12397
- return symbolRef;
12398
- }));
12399
- }
12400
- if (isBrowser && chunk) {
12401
- /** Preloading the chunk when we create the QRL. */
12402
- p(chunk, 0.8);
12403
- }
12404
- return qrl;
12836
+ // ...and set the prototype to the QRL instance so it has all the properties and methods without copying them
12837
+ Object.setPrototypeOf(qrlFn, qrl);
12838
+ return qrlFn;
12405
12839
  };
12406
12840
  const EMITTED = /*#__PURE__*/ new Set();
12407
12841
  const emitUsedSymbol = (symbol, element, reqTime) => {
@@ -12576,6 +13010,7 @@ const componentQrl = (componentQrl) => {
12576
13010
  QwikComponent[SERIALIZABLE_STATE] = [componentQrl];
12577
13011
  return QwikComponent;
12578
13012
  };
13013
+ /** @internal */
12579
13014
  const SERIALIZABLE_STATE = Symbol('serializable-data');
12580
13015
  const isQwikComponent = (component) => {
12581
13016
  return typeof component == 'function' && component[SERIALIZABLE_STATE] !== undefined;
@@ -13231,156 +13666,6 @@ const _useStyles = (styleQrl, transform, scoped) => {
13231
13666
  return styleId;
13232
13667
  };
13233
13668
 
13234
- // <docs markdown="../readme.md#useOn">
13235
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
13236
- // (edit ../readme.md#useOn instead and run `pnpm docs.sync`)
13237
- /**
13238
- * Register a listener on the current component's host element.
13239
- *
13240
- * Used to programmatically add event listeners. Useful from custom `use*` methods, which do not
13241
- * have access to the JSX. Otherwise, it's adding a JSX listener in the `<div>` is a better idea.
13242
- *
13243
- * Events are case sensitive.
13244
- *
13245
- * @public
13246
- * @see `useOn`, `useOnWindow`, `useOnDocument`.
13247
- */
13248
- // </docs>
13249
- const useOn = (event, eventQrl) => {
13250
- _useOn("q-e:" /* EventNameHtmlScope.on */, event, eventQrl);
13251
- };
13252
- // <docs markdown="../readme.md#useOnDocument">
13253
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
13254
- // (edit ../readme.md#useOnDocument instead and run `pnpm docs.sync`)
13255
- /**
13256
- * Register a listener on `document`.
13257
- *
13258
- * Used to programmatically add event listeners. Useful from custom `use*` methods, which do not
13259
- * have access to the JSX.
13260
- *
13261
- * Events are case sensitive.
13262
- *
13263
- * @public
13264
- * @see `useOn`, `useOnWindow`, `useOnDocument`.
13265
- *
13266
- * ```tsx
13267
- * function useScroll() {
13268
- * useOnDocument(
13269
- * 'scroll',
13270
- * $((event) => {
13271
- * console.log('body scrolled', event);
13272
- * })
13273
- * );
13274
- * }
13275
- *
13276
- * const Cmp = component$(() => {
13277
- * useScroll();
13278
- * return <div>Profit!</div>;
13279
- * });
13280
- * ```
13281
- */
13282
- // </docs>
13283
- const useOnDocument = (event, eventQrl) => {
13284
- _useOn("q-d:" /* EventNameHtmlScope.document */, event, eventQrl);
13285
- };
13286
- // <docs markdown="../readme.md#useOnWindow">
13287
- // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
13288
- // (edit ../readme.md#useOnWindow instead and run `pnpm docs.sync`)
13289
- /**
13290
- * Register a listener on `window`.
13291
- *
13292
- * Used to programmatically add event listeners. Useful from custom `use*` methods, which do not
13293
- * have access to the JSX.
13294
- *
13295
- * Events are case sensitive.
13296
- *
13297
- * @public
13298
- * @see `useOn`, `useOnWindow`, `useOnDocument`.
13299
- *
13300
- * ```tsx
13301
- * function useAnalytics() {
13302
- * useOnWindow(
13303
- * 'popstate',
13304
- * $((event) => {
13305
- * console.log('navigation happened', event);
13306
- * // report to analytics
13307
- * })
13308
- * );
13309
- * }
13310
- *
13311
- * const Cmp = component$(() => {
13312
- * useAnalytics();
13313
- * return <div>Profit!</div>;
13314
- * });
13315
- * ```
13316
- */
13317
- // </docs>
13318
- const useOnWindow = (event, eventQrl) => {
13319
- _useOn("q-w:" /* EventNameHtmlScope.window */, event, eventQrl);
13320
- };
13321
- const _useOn = (prefix, eventName, eventQrl) => {
13322
- const { isAdded, addEvent } = useOnEventsSequentialScope();
13323
- if (isAdded) {
13324
- return;
13325
- }
13326
- if (eventQrl) {
13327
- if (Array.isArray(eventName)) {
13328
- for (const event of eventName) {
13329
- addEvent(prefix + fromCamelToKebabCase(event), eventQrl);
13330
- }
13331
- }
13332
- else {
13333
- addEvent(prefix + fromCamelToKebabCase(eventName), eventQrl);
13334
- }
13335
- }
13336
- };
13337
- /**
13338
- * This hook is like the `useSequentialScope` but it is specifically for `useOn`. This is needed
13339
- * because we want to execute the `useOn` hooks only once and store the event listeners on the host
13340
- * element. From Qwik V2 the component is rerunning when the promise is thrown, so we need to make
13341
- * sure that the event listeners are not added multiple times.
13342
- *
13343
- * - The event listeners are stored in the `USE_ON_LOCAL` property.
13344
- * - The `USE_ON_LOCAL_SEQ_IDX` is used to keep track of the index of the hook that calls this.
13345
- * - The `USE_ON_LOCAL_FLAGS` is used to keep track of whether the event listener has been added or
13346
- * not.
13347
- */
13348
- const useOnEventsSequentialScope = () => {
13349
- const iCtx = useInvokeContext();
13350
- const hostElement = iCtx.$hostElement$;
13351
- const host = hostElement;
13352
- let onMap = iCtx.$container$.getHostProp(host, USE_ON_LOCAL);
13353
- if (onMap === null) {
13354
- onMap = {};
13355
- iCtx.$container$.setHostProp(host, USE_ON_LOCAL, onMap);
13356
- }
13357
- let seqIdx = iCtx.$container$.getHostProp(host, USE_ON_LOCAL_SEQ_IDX);
13358
- if (seqIdx === null) {
13359
- seqIdx = 0;
13360
- }
13361
- iCtx.$container$.setHostProp(host, USE_ON_LOCAL_SEQ_IDX, seqIdx + 1);
13362
- let addedFlags = iCtx.$container$.getHostProp(host, USE_ON_LOCAL_FLAGS);
13363
- if (addedFlags === null) {
13364
- addedFlags = [];
13365
- iCtx.$container$.setHostProp(host, USE_ON_LOCAL_FLAGS, addedFlags);
13366
- }
13367
- while (addedFlags.length <= seqIdx) {
13368
- addedFlags.push(false);
13369
- }
13370
- const addEvent = (eventName, eventQrl) => {
13371
- addedFlags[seqIdx] = true;
13372
- let events = onMap[eventName];
13373
- if (!events) {
13374
- onMap[eventName] = events = [];
13375
- }
13376
- events.push(eventQrl);
13377
- };
13378
- return {
13379
- isAdded: addedFlags[seqIdx],
13380
- addEvent,
13381
- };
13382
- };
13383
-
13384
13669
  const getSignal = (initialState) => {
13385
13670
  const value = isFunction(initialState) && !isQwikComponent(initialState)
13386
13671
  ? invoke(undefined, initialState)
@@ -13855,5 +14140,5 @@ if (import.meta.hot) {
13855
14140
  });
13856
14141
  }
13857
14142
 
13858
- export { $, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, _UNINITIALIZED, _VAR_PROPS, _captures, _chk, createQRL as _createQRL, _deserialize, _dumpState, _executeSsrChores, _fnSignal, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, _getVarProps, _hasStoreEffects, isJSXNode as _isJSXNode, isStore as _isStore, isStringifiable as _isStringifiable, isTask as _isTask, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, mapApp_findIndx as _mapApp_findIndx, mapArray_get as _mapArray_get, mapArray_set as _mapArray_set, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, qrlToString as _qrlToString, _regSymbol, _res, _resolveContextWithoutSequentialScope, _restProps, _rsc, _run, _serialize, setEvent as _setEvent, scheduleTask as _task, _val, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getFirstChild as _vnode_getFirstChild, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _wrapProp, _wrapSignal, component$, componentQrl, createAsync$, createAsyncSignal as createAsyncQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, forceStoreEffects, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsync$, useAsyncQrl, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useSerializer$, useSerializerQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
14143
+ export { $, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, createQRL as _createQRL, _deserialize, _dumpState, _executeSsrChores, _fnSignal, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, _getVarProps, _hasStoreEffects, _hmr, isJSXNode as _isJSXNode, isStore as _isStore, isStringifiable as _isStringifiable, isTask as _isTask, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, mapApp_findIndx as _mapApp_findIndx, mapArray_get as _mapArray_get, mapArray_set as _mapArray_set, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, qrlToString as _qrlToString, _regSymbol, _removeProjection, _res, _resolveContextWithoutSequentialScope, _restProps, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, scheduleTask as _task, _updateProjectionProps, _useHmr, _val, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getFirstChild as _vnode_getFirstChild, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _wrapProp, _wrapSignal, component$, componentQrl, createAsync$, createAsyncSignal as createAsyncQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, forceStoreEffects, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsync$, useAsyncQrl, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useSerializer$, useSerializerQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
13859
14144
  //# sourceMappingURL=core.mjs.map