@qwik.dev/core 2.0.0-beta.35 → 2.0.0-beta.36

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.35-dev+4603135
3
+ * @qwik.dev/core 2.0.0-beta.36-dev+3268fab
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
@@ -328,6 +328,25 @@ const VNodeDataChar = {
328
328
  BACK_REFS: /* ********** */ 96, // '`' - `q:brefs' - Effect dependencies/subscriptions
329
329
  SEPARATOR: /* ********* */ 124, // `|` - Separator char to encode any key/value pairs.
330
330
  SLOT: /* ************** */ 126};
331
+ /**
332
+ * Convert a segment-local vnode index into a key for the root container's shared qVNodeRefs map.
333
+ *
334
+ * Out-of-order Suspense segments each start counting vnode refs from 0, but the client merges all
335
+ * segment refs into one root-level map. This uses a Cantor-style pairing function for the
336
+ * zero-based pair `(segmentIndex, localIndex)`, then makes it negative so segment refs cannot
337
+ * collide with root refs, which are non-negative.
338
+ *
339
+ * Examples:
340
+ *
341
+ * - Segment 1, local 0 -> -1
342
+ * - Segment 2, local 0 -> -2
343
+ * - Segment 1, local 1 -> -3
344
+ */
345
+ const getSegmentVNodeRefId = (segmentId, localIndex) => {
346
+ const segmentIndex = parseInt(segmentId, 10) - 1;
347
+ const diagonal = segmentIndex + localIndex;
348
+ return -((diagonal * (diagonal + 1)) / 2 + localIndex + 1);
349
+ };
331
350
 
332
351
  function escapeHTML(html) {
333
352
  let escapedHTML = '';
@@ -391,6 +410,9 @@ const ComponentStylesPrefixContent = '⚡️';
391
410
  const QSlot = 'q:slot';
392
411
  const QSlotParent = 'q:sparent';
393
412
  const QSlotS = 'q:s';
413
+ const QStatePatchAttrSelector = '[q\\:patch]';
414
+ const QSuspenseResolved = 'q:r';
415
+ const QSuspenseResultParent = 'q:rp';
394
416
  const QStyle = 'q:style';
395
417
  const QStyleSelector = 'style[q\\:style]';
396
418
  const QStyleSSelector = 'style[q\\:sstyle]';
@@ -412,6 +434,7 @@ const QIgnore = 'q:ignore';
412
434
  const QIgnoreEnd = '/' + QIgnore;
413
435
  const QContainerAttr = 'q:container';
414
436
  const QContainerAttrEnd = '/' + QContainerAttr;
437
+ const QCursorBoundary = 'q:cursorBoundary';
415
438
  const QTemplate = 'q:template';
416
439
  // the same selector should be inside the qwik loader
417
440
  // and the same selector should be inside the qwik router spa-shim and spa-init
@@ -444,7 +467,6 @@ const ELEMENT_KEY = 'q:key';
444
467
  const ELEMENT_PROPS = 'q:props';
445
468
  const ELEMENT_SEQ = 'q:seq';
446
469
  const ELEMENT_SEQ_IDX = 'q:seqIdx';
447
- const Q_PREFIX = 'q:';
448
470
  const ITERATION_ITEM_SINGLE = 'q:p'; // Single iteration parameter (not an array)
449
471
  const ITERATION_ITEM_MULTI = 'q:ps'; // Multiple iteration parameters (array)
450
472
  /** Non serializable markers - always begins with `:` character */
@@ -452,8 +474,7 @@ const NON_SERIALIZABLE_MARKER_PREFIX = ':';
452
474
  const USE_ON_LOCAL = NON_SERIALIZABLE_MARKER_PREFIX + 'on';
453
475
  const USE_ON_LOCAL_SEQ_IDX = NON_SERIALIZABLE_MARKER_PREFIX + 'onIdx';
454
476
  const USE_ON_LOCAL_FLAGS = NON_SERIALIZABLE_MARKER_PREFIX + 'onFlags';
455
- const QCursorBoundary = Q_PREFIX + 'cursorBoundary';
456
- const QNearestCursorBoundary = NON_SERIALIZABLE_MARKER_PREFIX + 'nearestCursorBoundary';
477
+ const NEAREST_CURSOR_BOUNDARY = NON_SERIALIZABLE_MARKER_PREFIX + 'nearestCursorBoundary';
457
478
  const Q_PROPS_SEPARATOR = ':';
458
479
  const dangerouslySetInnerHTML = 'dangerouslySetInnerHTML';
459
480
  const qwikInspectorAttr = 'data-qwik-inspector';
@@ -932,8 +953,6 @@ const config = {
932
953
  const rel = isBrowser && doc.createElement('link').relList?.supports?.('modulepreload')
933
954
  ? 'modulePreload'
934
955
  : 'preload';
935
- // Global state
936
- performance.now();
937
956
  const isJSRegex = /\.[mc]?js$/;
938
957
  const yieldInterval = 1000 / 60;
939
958
 
@@ -1178,6 +1197,29 @@ const isObjectEmpty = (obj) => {
1178
1197
  return true;
1179
1198
  };
1180
1199
 
1200
+ /** @internal */
1201
+ const stringifyRootRefPath = (path) => {
1202
+ let text = String(path[0]);
1203
+ for (let i = 1; i < path.length; i++) {
1204
+ text += ' ' + path[i];
1205
+ }
1206
+ return text;
1207
+ };
1208
+ /** @internal */
1209
+ const writeStringRootRef = (writer, id) => writer.write(String(id));
1210
+ /** @internal */
1211
+ const writeStringRootRefPath = (writer, path) => writer.write(stringifyRootRefPath(path));
1212
+ /** @internal */
1213
+ const createStringStreamWriter = (write) => ({
1214
+ write,
1215
+ writeRootRef(id) {
1216
+ return writeStringRootRef(this, id);
1217
+ },
1218
+ writeRootRefPath(path) {
1219
+ return writeStringRootRefPath(this, path);
1220
+ },
1221
+ });
1222
+
1181
1223
  // Pre-allocated common strings to reduce allocation overhead
1182
1224
  const CLOSE_TAG = '</';
1183
1225
  const ESCAPED_CLOSE_TAG = '<\\/';
@@ -1191,7 +1233,7 @@ const COMMA = ',';
1191
1233
  *
1192
1234
  * @public
1193
1235
  */
1194
- const version = "2.0.0-beta.35-dev+4603135";
1236
+ const version = "2.0.0-beta.36-dev+3268fab";
1195
1237
 
1196
1238
  const isNode = (value) => {
1197
1239
  return value && typeof value.nodeType === 'number';
@@ -1203,6 +1245,20 @@ const isElement$1 = (value) => {
1203
1245
  return value.nodeType === 1;
1204
1246
  };
1205
1247
 
1248
+ /** @internal */
1249
+ const getRootContainer = (container) => {
1250
+ const rootContainer = container.$rootContainer$;
1251
+ return rootContainer || container;
1252
+ };
1253
+ /** @internal */
1254
+ const isOutOfOrderSegmentContainer = (container) => {
1255
+ return container.$isOutOfOrderSegment$;
1256
+ };
1257
+ /** @internal */
1258
+ const isSameContainer = (left, right) => {
1259
+ return getRootContainer(left) === (right ? getRootContainer(right) : null);
1260
+ };
1261
+
1206
1262
  /** @internal */
1207
1263
  const _EFFECT_BACK_REF = Symbol('backRef');
1208
1264
  /** Class for back reference to the EffectSubscription */
@@ -1554,17 +1610,27 @@ function pauseCursor(cursor, container) {
1554
1610
  container.$pendingCount$++;
1555
1611
  }
1556
1612
  function resumeCursor(cursor, container) {
1557
- const index = pausedCursorQueue.indexOf(cursor);
1558
- if (index !== -1) {
1559
- const lastIndex = pausedCursorQueue.length - 1;
1560
- if (index !== lastIndex) {
1561
- pausedCursorQueue[index] = pausedCursorQueue[lastIndex];
1562
- }
1563
- pausedCursorQueue.pop();
1564
- container.$pendingCount$--;
1613
+ removeCursorFromPausedQueue(cursor, container);
1614
+ if (!(cursor.flags & 256 /* VNodeFlags.Cursor */)) {
1615
+ return;
1565
1616
  }
1566
1617
  addCursorToQueue(container, cursor);
1567
1618
  }
1619
+ function abandonCursor(container, targetCursorData, cursor) {
1620
+ const oldCursorData = getCursorData(cursor);
1621
+ if (!oldCursorData || oldCursorData === targetCursorData) {
1622
+ return;
1623
+ }
1624
+ removeCursorFromQueue(cursor, container, false);
1625
+ removeCursorFromPausedQueue(cursor, container);
1626
+ mergeCursorJournalAndBoundaries(targetCursorData, oldCursorData);
1627
+ oldCursorData.afterFlushTasks = null;
1628
+ oldCursorData.extraPromises = null;
1629
+ oldCursorData.journal = null;
1630
+ oldCursorData.boundaries = null;
1631
+ oldCursorData.position = null;
1632
+ oldCursorData.promise = null;
1633
+ }
1568
1634
  /**
1569
1635
  * Removes a cursor from the global queue.
1570
1636
  *
@@ -1585,7 +1651,22 @@ function removeCursorFromQueue(cursor, container, keepCursorFlag) {
1585
1651
  // globalCursorQueue.pop();
1586
1652
  globalCursorQueue.splice(index, 1);
1587
1653
  container.$pendingCount$--;
1654
+ return true;
1655
+ }
1656
+ return false;
1657
+ }
1658
+ function removeCursorFromPausedQueue(cursor, container) {
1659
+ const index = pausedCursorQueue.indexOf(cursor);
1660
+ if (index === -1) {
1661
+ return false;
1588
1662
  }
1663
+ const lastIndex = pausedCursorQueue.length - 1;
1664
+ if (index !== lastIndex) {
1665
+ pausedCursorQueue[index] = pausedCursorQueue[lastIndex];
1666
+ }
1667
+ pausedCursorQueue.pop();
1668
+ container.$pendingCount$--;
1669
+ return true;
1589
1670
  }
1590
1671
 
1591
1672
  const cursorDatas = new WeakMap();
@@ -1594,6 +1675,7 @@ const NODE_PROPS_DATA_KEY = ':nodeProps';
1594
1675
  const NODE_DIFF_DATA_KEY = ':nodeDiff';
1595
1676
  const ERROR_DATA_KEY = ':errorData';
1596
1677
  const HOST_SIGNAL = ':signal';
1678
+ const INLINE_COMPONENT_DATA_KEY = ':inlineComponentData';
1597
1679
  /**
1598
1680
  * Sets the cursor position in a cursor vNode.
1599
1681
  *
@@ -1610,6 +1692,9 @@ function mergeCursors(container, newCursorData, oldCursor) {
1610
1692
  // delete from global cursors queue
1611
1693
  removeCursorFromQueue(oldCursor, container);
1612
1694
  const oldCursorData = getCursorData(oldCursor);
1695
+ mergeCursorData(newCursorData, oldCursorData);
1696
+ }
1697
+ function mergeCursorData(newCursorData, oldCursorData) {
1613
1698
  if (oldCursorData === newCursorData) {
1614
1699
  // same cursor data, no need to merge
1615
1700
  return;
@@ -1636,6 +1721,12 @@ function mergeCursors(container, newCursorData, oldCursor) {
1636
1721
  newCursorData.extraPromises = oldExtraPromises;
1637
1722
  }
1638
1723
  }
1724
+ mergeCursorJournalAndBoundaries(newCursorData, oldCursorData);
1725
+ }
1726
+ function mergeCursorJournalAndBoundaries(newCursorData, oldCursorData) {
1727
+ if (oldCursorData === newCursorData) {
1728
+ return;
1729
+ }
1639
1730
  // merge journal
1640
1731
  const oldJournal = oldCursorData.journal;
1641
1732
  if (oldJournal && oldJournal.length > 0) {
@@ -1876,20 +1967,28 @@ class SignalImpl {
1876
1967
  }
1877
1968
  else {
1878
1969
  isDev &&
1879
- assertTrue(!ctx.$container$ || ctx.$container$ === this.$container$, 'Do not use signals across containers');
1970
+ assertTrue(!ctx.$container$ || isSameContainer(ctx.$container$, this.$container$), 'Do not use signals across containers');
1880
1971
  }
1881
1972
  const effectSubscriber = ctx.$effectSubscriber$;
1882
1973
  if (effectSubscriber) {
1883
1974
  // Let's make sure that we have a reference to this effect.
1884
1975
  // Adding reference is essentially adding a subscription, so if the signal
1885
1976
  // changes we know who to notify.
1886
- ensureContainsSubscription((this.$effects$ ||= new Set()), effectSubscriber);
1977
+ const isOnServer = qTest ? isServerPlatform() : isServer;
1978
+ const effects = (this.$effects$ ||= new Set());
1979
+ const shouldRecordExternalRootEffect = __EXPERIMENTAL__.suspense && isOnServer;
1980
+ ensureContainsSubscription(effects, effectSubscriber);
1887
1981
  // But when effect is scheduled in needs to be able to know which signals
1888
1982
  // to unsubscribe from. So we need to store the reference from the effect back
1889
1983
  // to this signal.
1890
1984
  ensureContainsBackRef(effectSubscriber, this);
1891
- (qTest ? !isDomContainer(this.$container$) : isServer) &&
1892
- addQrlToSerializationCtx(effectSubscriber, this.$container$);
1985
+ if (isOnServer) {
1986
+ const serializationContainer = getEffectSerializationContainer(ctx.$container$, this.$container$);
1987
+ if (shouldRecordExternalRootEffect) {
1988
+ serializationContainer?.$recordExternalRootEffect$?.(this, effectSubscriber, null);
1989
+ }
1990
+ addQrlToSerializationCtx(effectSubscriber, serializationContainer);
1991
+ }
1893
1992
  }
1894
1993
  return val;
1895
1994
  }
@@ -3016,7 +3115,7 @@ class WrappedSignalImpl extends SignalImpl {
3016
3115
  catch (_) {
3017
3116
  if (this.$container$ && this.$hostElement$) {
3018
3117
  this.$container$.setHostProp(this.$hostElement$, HOST_SIGNAL, this);
3019
- markVNodeDirty(this.$container$, this.$hostElement$, 16 /* ChoreBits.COMPUTE */);
3118
+ markVNodeDirty(this.$container$, this.$hostElement$, 32 /* ChoreBits.COMPUTE */);
3020
3119
  }
3021
3120
  }
3022
3121
  // if the computation not failed, we can run the effects directly
@@ -3072,59 +3171,7 @@ class SubscriptionData {
3072
3171
  }
3073
3172
  }
3074
3173
 
3075
- /**
3076
- * Qwikloader provides the captures string of the QRL when calling a handler. In that case we must
3077
- * load the QRL captured scope ourselves. Otherwise, we are being called as a QRL and the captures
3078
- * are already set.
3079
- */
3080
- const maybeScopeFromQL = (captureIds, element) => {
3081
- if (typeof captureIds === 'string') {
3082
- const container = getDomContainer(element);
3083
- setCaptures(deserializeCaptures(container, captureIds));
3084
- }
3085
- return null;
3086
- };
3087
- /**
3088
- * Handles events for bind:value
3089
- *
3090
- * @internal
3091
- */
3092
- function _val(_, element) {
3093
- maybeScopeFromQL(this, element);
3094
- const signal = _captures[0];
3095
- signal.value = element.type === 'number' ? element.valueAsNumber : element.value;
3096
- }
3097
- /**
3098
- * Handles events for bind:checked
3099
- *
3100
- * @internal
3101
- */
3102
- function _chk(_, element) {
3103
- maybeScopeFromQL(this, element);
3104
- const signal = _captures[0];
3105
- signal.value = element.checked;
3106
- }
3107
- /**
3108
- * Resumes selected state (e.g. polling AsyncSignals) by deserializing captures. Used for
3109
- * document:onQIdle to resume async signals with active polling.
3110
- *
3111
- * @internal
3112
- */
3113
- function _res(_, element) {
3114
- maybeScopeFromQL(this, element);
3115
- // Captures are deserialized, now trigger computation on AsyncSignals
3116
- if (_captures) {
3117
- for (let i = 0; i < _captures.length; i++) {
3118
- const capture = _captures[i];
3119
- if (capture instanceof AsyncSignalImpl && capture.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
3120
- capture.$computeIfNeeded$();
3121
- }
3122
- // note that polling async signals will automatically schedule themselves so no action needed
3123
- }
3124
- }
3125
- }
3126
-
3127
- const _hasOwnProperty$2 = Object.prototype.hasOwnProperty;
3174
+ const _hasOwnProperty$3 = Object.prototype.hasOwnProperty;
3128
3175
  // TODO store props as the arrays the vnodes also use?
3129
3176
  class JSXNodeImpl {
3130
3177
  type;
@@ -3180,9 +3227,9 @@ const isJSXNode = (n) => {
3180
3227
  return true;
3181
3228
  }
3182
3229
  if (isObject(n) &&
3183
- _hasOwnProperty$2.call(n, 'key') &&
3184
- _hasOwnProperty$2.call(n, 'props') &&
3185
- _hasOwnProperty$2.call(n, 'type')) {
3230
+ _hasOwnProperty$3.call(n, 'key') &&
3231
+ _hasOwnProperty$3.call(n, 'props') &&
3232
+ _hasOwnProperty$3.call(n, 'type')) {
3186
3233
  if (isPropsProxy(n)) {
3187
3234
  return false;
3188
3235
  }
@@ -3196,11 +3243,63 @@ const isJSXNode = (n) => {
3196
3243
  }
3197
3244
  };
3198
3245
 
3246
+ /**
3247
+ * Qwikloader provides the captures string of the QRL when calling a handler. In that case we must
3248
+ * load the QRL captured scope ourselves. Otherwise, we are being called as a QRL and the captures
3249
+ * are already set.
3250
+ */
3251
+ const maybeScopeFromQL = (captureIds, element) => {
3252
+ if (typeof captureIds === 'string') {
3253
+ const container = getDomContainer(element);
3254
+ setCaptures(deserializeCaptures(container, captureIds));
3255
+ }
3256
+ return null;
3257
+ };
3258
+ /**
3259
+ * Handles events for bind:value
3260
+ *
3261
+ * @internal
3262
+ */
3263
+ function _val(_, element) {
3264
+ maybeScopeFromQL(this, element);
3265
+ const signal = _captures[0];
3266
+ signal.value = element.type === 'number' ? element.valueAsNumber : element.value;
3267
+ }
3268
+ /**
3269
+ * Handles events for bind:checked
3270
+ *
3271
+ * @internal
3272
+ */
3273
+ function _chk(_, element) {
3274
+ maybeScopeFromQL(this, element);
3275
+ const signal = _captures[0];
3276
+ signal.value = element.checked;
3277
+ }
3278
+ /**
3279
+ * Resumes selected state (e.g. polling AsyncSignals) by deserializing captures. Used for
3280
+ * document:onQIdle to resume async signals with active polling.
3281
+ *
3282
+ * @internal
3283
+ */
3284
+ function _res(_, element) {
3285
+ maybeScopeFromQL(this, element);
3286
+ // Captures are deserialized, now trigger computation on AsyncSignals
3287
+ if (_captures) {
3288
+ for (let i = 0; i < _captures.length; i++) {
3289
+ const capture = _captures[i];
3290
+ if (capture instanceof AsyncSignalImpl && capture.$flags$ & 64 /* AsyncSignalFlags.CLIENT_ONLY */) {
3291
+ capture.$computeIfNeeded$();
3292
+ }
3293
+ // note that polling async signals will automatically schedule themselves so no action needed
3294
+ }
3295
+ }
3296
+ }
3297
+
3199
3298
  const BIND_VALUE = 'bind:value';
3200
3299
  const BIND_CHECKED = 'bind:checked';
3201
3300
  const PASSIVE = 'passive:';
3202
3301
  const PREVENT_DEFAULT = 'preventdefault:';
3203
- const _hasOwnProperty$1 = Object.prototype.hasOwnProperty;
3302
+ const _hasOwnProperty$2 = Object.prototype.hasOwnProperty;
3204
3303
  const removePassiveMarkers = (props, passiveKeys, preventDefaultKeys, passiveEvents, canMutate = false) => {
3205
3304
  let mutableProps = props;
3206
3305
  let copied = canMutate;
@@ -3387,10 +3486,10 @@ const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
3387
3486
  varProps ||= {};
3388
3487
  if (bindCheckedSignal) {
3389
3488
  // Delete from both varProps and constProps if present
3390
- if (varProps && _hasOwnProperty$1.call(varProps, BIND_CHECKED)) {
3489
+ if (varProps && _hasOwnProperty$2.call(varProps, BIND_CHECKED)) {
3391
3490
  delete varProps[BIND_CHECKED];
3392
3491
  }
3393
- if (constProps && _hasOwnProperty$1.call(constProps, BIND_CHECKED)) {
3492
+ if (constProps && _hasOwnProperty$2.call(constProps, BIND_CHECKED)) {
3394
3493
  if (!constPropsCopied) {
3395
3494
  constProps = { ...constProps };
3396
3495
  constPropsCopied = true;
@@ -3400,7 +3499,7 @@ const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
3400
3499
  varProps.checked = bindCheckedSignal;
3401
3500
  const handler = createQRL(null, '_chk', _chk, null, [bindCheckedSignal]);
3402
3501
  // Move q-e:input from constProps if it exists
3403
- if (constProps && _hasOwnProperty$1.call(constProps, 'q-e:input')) {
3502
+ if (constProps && _hasOwnProperty$2.call(constProps, 'q-e:input')) {
3404
3503
  if (!constPropsCopied) {
3405
3504
  constProps = { ...constProps };
3406
3505
  constPropsCopied = true;
@@ -3413,10 +3512,10 @@ const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
3413
3512
  }
3414
3513
  else if (bindValueSignal) {
3415
3514
  // Delete from both varProps and constProps if present
3416
- if (varProps && _hasOwnProperty$1.call(varProps, BIND_VALUE)) {
3515
+ if (varProps && _hasOwnProperty$2.call(varProps, BIND_VALUE)) {
3417
3516
  delete varProps[BIND_VALUE];
3418
3517
  }
3419
- if (constProps && _hasOwnProperty$1.call(constProps, BIND_VALUE)) {
3518
+ if (constProps && _hasOwnProperty$2.call(constProps, BIND_VALUE)) {
3420
3519
  if (!constPropsCopied) {
3421
3520
  constProps = { ...constProps };
3422
3521
  constPropsCopied = true;
@@ -3426,7 +3525,7 @@ const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
3426
3525
  varProps.value = bindValueSignal;
3427
3526
  const handler = createQRL(null, '_val', _val, null, [bindValueSignal]);
3428
3527
  // Move q-e:input from constProps if it exists
3429
- if (constProps && _hasOwnProperty$1.call(constProps, 'q-e:input')) {
3528
+ if (constProps && _hasOwnProperty$2.call(constProps, 'q-e:input')) {
3430
3529
  if (!constPropsCopied) {
3431
3530
  constProps = { ...constProps };
3432
3531
  constPropsCopied = true;
@@ -3439,7 +3538,7 @@ const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
3439
3538
  }
3440
3539
  }
3441
3540
  // Transform className -> class
3442
- if (varProps && _hasOwnProperty$1.call(varProps, 'className')) {
3541
+ if (varProps && _hasOwnProperty$2.call(varProps, 'className')) {
3443
3542
  if (!varPropsCopied) {
3444
3543
  varProps = { ...varProps };
3445
3544
  varPropsCopied = true;
@@ -3451,7 +3550,7 @@ const _jsxSplit = (type, varProps, constProps, children, flags, key, dev) => {
3451
3550
  logOnceWarn(`jsx${dev ? ` ${dev.fileName}${dev?.lineNumber ? `:${dev.lineNumber}` : ''}` : ''}: \`className\` is deprecated. Use \`class\` instead.`);
3452
3551
  }
3453
3552
  }
3454
- if (constProps && _hasOwnProperty$1.call(constProps, 'className')) {
3553
+ if (constProps && _hasOwnProperty$2.call(constProps, 'className')) {
3455
3554
  if (!constPropsCopied) {
3456
3555
  constProps = { ...constProps };
3457
3556
  constPropsCopied = true;
@@ -3563,365 +3662,69 @@ const RenderOnce = (props, key) => {
3563
3662
  };
3564
3663
 
3565
3664
  /**
3566
- * Use `executeComponent` to execute a component.
3567
- *
3568
- * Component execution can be complex because of:
3569
- *
3570
- * - It can by async
3571
- * - It can contain many tasks which need to be awaited
3572
- * - Each task can run multiple times if they track signals which change.
3573
- * - The JSX may be re-generated multiple times of a task needs to be rerun due to signal change.
3574
- * - It needs to keep track of hook state.
3575
- *
3576
- * For `component$`: `renderHost` === `subscriptionHost` For inlined-components: the
3577
- * `subscriptionHost` is a parent `component$` which needs to re-execute.
3665
+ * Allows to project the children of the current component. `<Slot/>` can only be used within the
3666
+ * context of a component defined with `component$`.
3578
3667
  *
3579
- * @param container
3580
- * @param renderHost - VNode into which the component is rendered into.
3581
- * @param subscriptionHost - VNode which will be re-executed if the component needs to re-render.
3582
- * @param componentQRL
3583
- * @param props
3584
- * @returns
3668
+ * @public
3585
3669
  */
3586
- const executeComponent = (container, renderHost, subscriptionHost, componentQRL, props) => {
3587
- const iCtx = newRenderInvokeContext(container.$locale$, subscriptionHost || renderHost, container);
3588
- if (subscriptionHost) {
3589
- iCtx.$effectSubscriber$ = getSubscriber(subscriptionHost, ":" /* EffectProperty.COMPONENT */);
3590
- iCtx.$container$ = container;
3670
+ const Slot = (props) => {
3671
+ return _jsxSorted(Virtual, null, { [QSlotS]: '' }, props.children, 0, props.name ?? '');
3672
+ };
3673
+
3674
+ /** @public */
3675
+ const SkipRender = Symbol('skip render');
3676
+ /** @public */
3677
+ const SSRRaw = () => null;
3678
+ /** @public */
3679
+ const SSRComment = () => null;
3680
+ /** @public */
3681
+ const SSRStreamBlock = (props) => {
3682
+ return props.children;
3683
+ };
3684
+ /** @public */
3685
+ const SSRStream = (props, key) => jsx(RenderOnce, { children: jsx(InternalSSRStream, props) }, key);
3686
+ const InternalSSRStream = () => null;
3687
+
3688
+ let _setAttribute = null;
3689
+ const fastSetAttribute = (target, name, value) => {
3690
+ if (!_setAttribute) {
3691
+ _setAttribute = target.setAttribute;
3591
3692
  }
3592
- let componentFn;
3593
- container.ensureProjectionResolved(renderHost);
3594
- let isInlineComponent = false;
3595
- if (componentQRL === null) {
3596
- componentQRL = container.getHostProp(renderHost, OnRenderProp);
3597
- isDev && assertDefined(componentQRL, 'No Component found at this location');
3693
+ _setAttribute.call(target, name, value);
3694
+ };
3695
+ let _setAttributeNS = null;
3696
+ const fastSetAttributeNS = (target, namespace, name, value) => {
3697
+ if (!_setAttributeNS) {
3698
+ _setAttributeNS = target.setAttributeNS;
3598
3699
  }
3599
- if (isQrl(componentQRL)) {
3600
- props = props || container.getHostProp(renderHost, ELEMENT_PROPS) || EMPTY_OBJ;
3601
- // TODO is this possible? JSXNode handles this, no?
3602
- if ('children' in props) {
3603
- delete props.children;
3700
+ _setAttributeNS.call(target, namespace, name, value);
3701
+ };
3702
+ function directSetAttribute(element, attrName, attrValue, isSvg) {
3703
+ if (attrValue != null) {
3704
+ if (isSvg) {
3705
+ // only svg elements can have namespace attributes
3706
+ const namespace = getAttributeNamespace(attrName);
3707
+ if (namespace) {
3708
+ fastSetAttributeNS(element, namespace, attrName, attrValue);
3709
+ return;
3710
+ }
3604
3711
  }
3605
- componentFn = componentQRL.getFn(iCtx);
3712
+ fastSetAttribute(element, attrName, attrValue);
3606
3713
  }
3607
- else if (isQwikComponent(componentQRL)) {
3608
- const qComponentFn = componentQRL;
3609
- componentFn = () => invokeApply(iCtx, qComponentFn, [props || EMPTY_OBJ, null, 0]);
3714
+ }
3715
+
3716
+ function getFileLocationFromJsx(jsxDev) {
3717
+ if (!jsxDev) {
3718
+ return null;
3610
3719
  }
3611
- else {
3612
- isInlineComponent = true;
3613
- const inlineComponent = componentQRL;
3614
- componentFn = () => invokeApply(iCtx, inlineComponent, [props || EMPTY_OBJ]);
3615
- }
3616
- const isSsr = qTest ? isServerPlatform() : isServer;
3617
- const executeComponentWithPromiseExceptionRetry = (retryCount = 0) => safeCall(() => {
3618
- if (!isInlineComponent) {
3619
- container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
3620
- container.setHostProp(renderHost, USE_ON_LOCAL_SEQ_IDX, null);
3621
- }
3622
- if (retryCount > 0 && vnode_isVNode(renderHost)) {
3623
- clearAllEffects(container, renderHost);
3624
- }
3625
- return maybeThen(componentFn(props), (jsx) => maybeThen(iCtx.$waitOn$, () => jsx));
3626
- }, (jsx) => {
3627
- // In SSR, check if the component was marked dirty (COMPONENT bit) during execution.
3628
- // This happens when something completes and updates reactive primitives
3629
- // while we're waiting on $waitOn$. If so, we need to re-execute the component
3630
- // to get fresh JSX with updated values.
3631
- if (isSsr && !isInlineComponent) {
3632
- const ssrNode = renderHost;
3633
- if (ssrNode.dirty & 4 /* ChoreBits.COMPONENT */) {
3634
- ssrNode.dirty &= ~4 /* ChoreBits.COMPONENT */;
3635
- if (retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
3636
- return executeComponentWithPromiseExceptionRetry(retryCount + 1);
3637
- }
3638
- }
3639
- }
3640
- const useOnEvents = container.getHostProp(renderHost, USE_ON_LOCAL);
3641
- if (useOnEvents) {
3642
- return addUseOnEvents(jsx, useOnEvents);
3643
- }
3644
- return jsx;
3645
- }, (err) => {
3646
- if (isPromise(err) && retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
3647
- return err.then(() => executeComponentWithPromiseExceptionRetry(++retryCount));
3648
- }
3649
- else {
3650
- if (retryCount >= MAX_RETRY_ON_PROMISE_COUNT) {
3651
- throw new Error(`Max retry count of component execution reached`);
3652
- }
3653
- throw err;
3654
- }
3655
- });
3656
- return executeComponentWithPromiseExceptionRetry();
3657
- };
3658
- /**
3659
- * Adds `useOn` events to the JSX output.
3660
- *
3661
- * @param jsx The JSX output to modify.
3662
- * @param useOnEvents The `useOn` events to add.
3663
- * @returns The modified JSX output.
3664
- */
3665
- function addUseOnEvents(jsx, useOnEvents) {
3666
- const jsxElement = findFirstElementNode(jsx);
3667
- let jsxResult = jsx;
3668
- const qVisibleEvent = 'q-e:qvisible';
3669
- return maybeThen(jsxElement, (jsxElement) => {
3670
- // headless components are components that don't render a real DOM element
3671
- const isHeadless = !jsxElement;
3672
- // placeholder element is a <script> element that is used to add events to the document or window
3673
- let placeholderElement = null;
3674
- for (const key in useOnEvents) {
3675
- if (Object.prototype.hasOwnProperty.call(useOnEvents, key)) {
3676
- let targetElement = jsxElement;
3677
- let eventKey = key;
3678
- if (isHeadless) {
3679
- // if the component is headless, we need to add the event to the placeholder element
3680
- if (key === qVisibleEvent ||
3681
- key.startsWith("q-d:" /* EventNameHtmlScope.document */) ||
3682
- key.startsWith("q-w:" /* EventNameHtmlScope.window */)) {
3683
- if (!placeholderElement) {
3684
- const [createdElement, newJsx] = injectPlaceholderElement(jsxResult);
3685
- jsxResult = newJsx;
3686
- placeholderElement = createdElement;
3687
- }
3688
- targetElement = placeholderElement;
3689
- }
3690
- else {
3691
- if (isDev) {
3692
- const sourceLocation = getUseOnSourceLocation(useOnEvents[key].qrls);
3693
- logWarn('You are trying to add an event "' +
3694
- key +
3695
- '" using `useOn` hook, ' +
3696
- 'but a node to which you can add an event is not found. ' +
3697
- 'Please make sure that the component outputs a DOM element.' +
3698
- (sourceLocation ? ` Offending \`useOn\`: ${sourceLocation}.` : ''));
3699
- }
3700
- continue;
3701
- }
3702
- }
3703
- if (targetElement) {
3704
- if (targetElement.type === 'script' && key === qVisibleEvent) {
3705
- eventKey = 'q-d:qinit';
3706
- if (isDev) {
3707
- const sourceLocation = getUseOnSourceLocation(useOnEvents[key].qrls);
3708
- logWarn(`You are trying to add the event "${key}" ` +
3709
- 'using the `useVisibleTask$` hook with the "intersection-observer" strategy, ' +
3710
- 'but this only works when the component outputs a DOM element. Falling back to ' +
3711
- '"document-ready" instead.' +
3712
- (sourceLocation ? ` Offending \`useVisibleTask$\`: ${sourceLocation}.` : ''));
3713
- }
3714
- }
3715
- addUseOnEvent(targetElement, eventKey, useOnEvents[key]);
3716
- }
3717
- }
3718
- }
3719
- return jsxResult;
3720
- });
3721
- }
3722
- function getUseOnSourceLocation(eventQrls) {
3723
- for (let i = 0; i < eventQrls.length; i++) {
3724
- const eventQrl = eventQrls[i];
3725
- const task = eventQrl?.getCaptured()?.[0];
3726
- if (isTask(task)) {
3727
- const dev = task.$qrl$.dev;
3728
- if (dev?.file) {
3729
- return typeof dev.lo === 'number' && typeof dev.hi === 'number'
3730
- ? `${dev.file}:${dev.lo}-${dev.hi}`
3731
- : dev.file;
3732
- }
3733
- }
3734
- }
3735
- return null;
3736
- }
3737
- /**
3738
- * Adds an event to the JSX element.
3739
- *
3740
- * @param jsxElement The JSX element to add the event to.
3741
- * @param key The event name.
3742
- * @param value The event value.
3743
- */
3744
- function addUseOnEvent(jsxElement, key, value) {
3745
- // These handlers are always there, so they go in constProps
3746
- const props = (jsxElement.constProps ||= {});
3747
- const propValue = props[key];
3748
- const qrls = value.qrls;
3749
- if (propValue == null) {
3750
- props[key] = qrls;
3751
- }
3752
- else if (Array.isArray(propValue)) {
3753
- propValue.push(...qrls);
3754
- }
3755
- else {
3756
- props[key] = [propValue, ...qrls];
3757
- }
3758
- const varProp = jsxElement.varProps[key];
3759
- if (varProp) {
3760
- // we need to demote the handlers to varProps
3761
- if (Array.isArray(propValue)) {
3762
- propValue.push(...props[key]);
3763
- }
3764
- else {
3765
- jsxElement.varProps[key] = [propValue, ...qrls];
3766
- }
3767
- props[key] = undefined;
3768
- }
3769
- const capture = value.capture;
3770
- const preventdefault = value.preventdefault;
3771
- const stoppropagation = value.stoppropagation;
3772
- if (!capture && !preventdefault && !stoppropagation) {
3773
- return;
3774
- }
3775
- const [, eventName] = getEventDataFromHtmlAttribute(key);
3776
- capture && addUseOnModifier(jsxElement, eventName, 'capture');
3777
- preventdefault && addUseOnModifier(jsxElement, eventName, 'preventdefault');
3778
- stoppropagation && addUseOnModifier(jsxElement, eventName, 'stoppropagation');
3779
- }
3780
- function addUseOnModifier(jsxElement, eventName, modifier) {
3781
- const key = `${modifier}:${eventName}`;
3782
- const varProps = jsxElement.varProps;
3783
- if (varProps === EMPTY_OBJ) {
3784
- jsxElement.varProps = {};
3785
- }
3786
- jsxElement.varProps[key] = true;
3787
- }
3788
- /**
3789
- * Finds the first element node in the JSX output.
3790
- *
3791
- * @param jsx The JSX output to search.
3792
- * @returns The first element node or null if no element node is found.
3793
- */
3794
- function findFirstElementNode(jsx) {
3795
- const queue = [jsx];
3796
- while (queue.length) {
3797
- const jsx = queue.shift();
3798
- if (isJSXNode(jsx)) {
3799
- if (typeof jsx.type === 'string') {
3800
- return jsx;
3801
- }
3802
- queue.push(jsx.children);
3803
- }
3804
- else if (isArray(jsx)) {
3805
- queue.push(...jsx);
3806
- }
3807
- else if (isPromise(jsx)) {
3808
- return maybeThen(jsx, (jsx) => findFirstElementNode(jsx));
3809
- }
3810
- else if (isSignal(jsx)) {
3811
- return findFirstElementNode(jsx.untrackedValue);
3812
- }
3813
- }
3814
- return null;
3815
- }
3816
- /**
3817
- * Injects a placeholder <script> element into the JSX output.
3818
- *
3819
- * This is necessary for headless components (components that don't render a real DOM element) to
3820
- * have an anchor point for `useOn` event listeners that target the document or window.
3821
- *
3822
- * @param jsx The JSX output to modify.
3823
- * @returns A tuple containing the created placeholder element and the modified JSX output.
3824
- */
3825
- function injectPlaceholderElement(jsx) {
3826
- // For regular JSX nodes, we can append the placeholder to its children.
3827
- if (isJSXNode(jsx)) {
3828
- const placeholder = createPlaceholderScriptNode();
3829
- // Inline components don't always render children, so we wrap them in Fragment which does.
3830
- if (jsx.type !== Fragment && !isQwikComponent(jsx.type)) {
3831
- return [placeholder, _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null)];
3832
- }
3833
- if (jsx.children == null) {
3834
- jsx.children = placeholder;
3835
- }
3836
- else if (isArray(jsx.children)) {
3837
- jsx.children.push(placeholder);
3838
- }
3839
- else {
3840
- jsx.children = [jsx.children, placeholder];
3841
- }
3842
- return [placeholder, jsx];
3843
- }
3844
- // For primitives, we can't add children, so we wrap them in a fragment.
3845
- if (isPrimitiveOrNullUndefined(jsx)) {
3846
- const placeholder = createPlaceholderScriptNode();
3847
- return [placeholder, _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null)];
3848
- }
3849
- // For an array of nodes, we inject the placeholder into the first element.
3850
- if (isArray(jsx) && jsx.length > 0) {
3851
- const [createdElement, _] = injectPlaceholderElement(jsx[0]);
3852
- return [createdElement, jsx];
3853
- }
3854
- // For anything else we do nothing.
3855
- return [null, jsx];
3856
- }
3857
- /** @returns An empty <script> element for adding qwik metadata attributes to */
3858
- function createPlaceholderScriptNode() {
3859
- return new JSXNodeImpl('script', null, { hidden: '' }, null, 0, null);
3860
- }
3861
-
3862
- /**
3863
- * Allows to project the children of the current component. `<Slot/>` can only be used within the
3864
- * context of a component defined with `component$`.
3865
- *
3866
- * @public
3867
- */
3868
- const Slot = (props) => {
3869
- return _jsxSorted(Virtual, null, { [QSlotS]: '' }, props.children, 0, props.name ?? '');
3870
- };
3871
-
3872
- /** @public */
3873
- const SkipRender = Symbol('skip render');
3874
- /** @public */
3875
- const SSRRaw = () => null;
3876
- /** @public */
3877
- const SSRComment = () => null;
3878
- /** @public */
3879
- const SSRStreamBlock = (props) => {
3880
- return props.children;
3881
- };
3882
- /** @public */
3883
- const SSRStream = (props, key) => jsx(RenderOnce, { children: jsx(InternalSSRStream, props) }, key);
3884
- const InternalSSRStream = () => null;
3885
-
3886
- let _setAttribute = null;
3887
- const fastSetAttribute = (target, name, value) => {
3888
- if (!_setAttribute) {
3889
- _setAttribute = target.setAttribute;
3890
- }
3891
- _setAttribute.call(target, name, value);
3892
- };
3893
- let _setAttributeNS = null;
3894
- const fastSetAttributeNS = (target, namespace, name, value) => {
3895
- if (!_setAttributeNS) {
3896
- _setAttributeNS = target.setAttributeNS;
3897
- }
3898
- _setAttributeNS.call(target, namespace, name, value);
3899
- };
3900
- function directSetAttribute(element, attrName, attrValue, isSvg) {
3901
- if (attrValue != null) {
3902
- if (isSvg) {
3903
- // only svg elements can have namespace attributes
3904
- const namespace = getAttributeNamespace(attrName);
3905
- if (namespace) {
3906
- fastSetAttributeNS(element, namespace, attrName, attrValue);
3907
- return;
3908
- }
3909
- }
3910
- fastSetAttribute(element, attrName, attrValue);
3911
- }
3912
- }
3913
-
3914
- function getFileLocationFromJsx(jsxDev) {
3915
- if (!jsxDev) {
3916
- return null;
3917
- }
3918
- const sanitizedFileName = jsxDev.fileName?.replace(/\\/g, '/');
3919
- if (sanitizedFileName) {
3920
- return `${sanitizedFileName}:${jsxDev.lineNumber}:${jsxDev.columnNumber}`;
3720
+ const sanitizedFileName = jsxDev.fileName?.replace(/\\/g, '/');
3721
+ if (sanitizedFileName) {
3722
+ return `${sanitizedFileName}:${jsxDev.lineNumber}:${jsxDev.columnNumber}`;
3921
3723
  }
3922
3724
  return null;
3923
3725
  }
3924
3726
 
3727
+ const _hasOwnProperty$1 = Object.prototype.hasOwnProperty;
3925
3728
  function isSlotProp(prop) {
3926
3729
  return !prop.startsWith('q:') && !prop.startsWith(NON_SERIALIZABLE_MARKER_PREFIX);
3927
3730
  }
@@ -3946,6 +3749,16 @@ const _restProps = (props, omit = [], target = {}) => {
3946
3749
  }
3947
3750
  return createPropsProxy(new JSXNodeImpl(null, varPropsTarget, constPropsTarget, null, 0, null));
3948
3751
  };
3752
+ function resolveSlotName(host, jsx, container) {
3753
+ const constProps = jsx.constProps;
3754
+ if (constProps && typeof constProps == 'object' && _hasOwnProperty$1.call(constProps, 'name')) {
3755
+ const constValue = constProps.name;
3756
+ if (host && constValue instanceof WrappedSignalImpl) {
3757
+ return trackSignalAndAssignHost(constValue, host, ":" /* EffectProperty.COMPONENT */, container);
3758
+ }
3759
+ }
3760
+ return directGetPropsProxyProp(jsx, 'name') || QDefaultSlot;
3761
+ }
3949
3762
 
3950
3763
  class DeleteOperation {
3951
3764
  target;
@@ -4172,14 +3985,14 @@ function getNearestCursorBoundaryProp(vNode) {
4172
3985
  if (!__EXPERIMENTAL__.suspense) {
4173
3986
  return null;
4174
3987
  }
4175
- return (vnode_getProp(vNode, QNearestCursorBoundary, null) ||
3988
+ return (vnode_getProp(vNode, NEAREST_CURSOR_BOUNDARY, null) ||
4176
3989
  null);
4177
3990
  }
4178
3991
  function clearNearestCursorBoundary(vNode) {
4179
3992
  if (!__EXPERIMENTAL__.suspense) {
4180
3993
  return;
4181
3994
  }
4182
- vnode_setProp(vNode, QNearestCursorBoundary, null);
3995
+ vnode_setProp(vNode, NEAREST_CURSOR_BOUNDARY, null);
4183
3996
  }
4184
3997
  function getNearestCursorBoundary(container, vNode) {
4185
3998
  if (!__EXPERIMENTAL__.suspense) {
@@ -4188,7 +4001,7 @@ function getNearestCursorBoundary(container, vNode) {
4188
4001
  return getNearestCursorBoundaryProp(vNode) || getOwnCursorBoundary(container, vNode);
4189
4002
  }
4190
4003
  function setNearestCursorBoundary(vNode, boundary) {
4191
- __EXPERIMENTAL__.suspense && vnode_setProp(vNode, QNearestCursorBoundary, boundary);
4004
+ __EXPERIMENTAL__.suspense && vnode_setProp(vNode, NEAREST_CURSOR_BOUNDARY, boundary);
4192
4005
  }
4193
4006
  /** Updates the nearest cursor boundary cache on a vnode and any already-dirty descendants. */
4194
4007
  function updateDirtySubtreeCursorBoundary(container, vNode, boundary) {
@@ -4732,7 +4545,7 @@ function expectProjection(diffContext) {
4732
4545
  function expectSlot(diffContext) {
4733
4546
  const jsxNode = diffContext.$jsxValue$;
4734
4547
  const vHost = vnode_getProjectionParentComponent(diffContext.$vParent$);
4735
- const slotNameKey = getSlotNameKey(diffContext, vHost);
4548
+ const slotNameKey = resolveSlotName(vHost, jsxNode, diffContext.$container$);
4736
4549
  const cursorBoundary = directGetPropsProxyProp(jsxNode, QCursorBoundary) || null;
4737
4550
  const vProjectedNode = vHost
4738
4551
  ? vnode_getProp(vHost, slotNameKey,
@@ -4775,17 +4588,6 @@ function expectSlot(diffContext) {
4775
4588
  }
4776
4589
  return true;
4777
4590
  }
4778
- function getSlotNameKey(diffContext, vHost) {
4779
- const jsxNode = diffContext.$jsxValue$;
4780
- const constProps = jsxNode.constProps;
4781
- if (constProps && typeof constProps == 'object' && _hasOwnProperty.call(constProps, 'name')) {
4782
- const constValue = constProps.name;
4783
- if (vHost && constValue instanceof WrappedSignalImpl) {
4784
- return trackSignalAndAssignHost(constValue, vHost, ":" /* EffectProperty.COMPONENT */, diffContext.$container$);
4785
- }
4786
- }
4787
- return directGetPropsProxyProp(jsxNode, 'name') || QDefaultSlot;
4788
- }
4789
4591
  function cleanupSideBuffer(diffContext) {
4790
4592
  const sideBuffer = diffContext.$vSideBuffer$;
4791
4593
  if (sideBuffer) {
@@ -5161,603 +4963,910 @@ function retrieveChildWithKey(diffContext, nodeName, key) {
5161
4963
  diffContext.$vSiblings$.set(getSideBufferKey(name, vKey), vNode);
5162
4964
  }
5163
4965
  }
5164
- vNode = vNode.nextSibling;
4966
+ vNode = vNode.nextSibling;
4967
+ }
4968
+ }
4969
+ else {
4970
+ if (key === null) {
4971
+ for (let i = 0; i < diffContext.$vSiblingsArray$.length; i += 2) {
4972
+ if (diffContext.$vSiblingsArray$[i] === nodeName) {
4973
+ vNodeWithKey = diffContext.$vSiblingsArray$[i + 1];
4974
+ diffContext.$vSiblingsArray$.splice(i, 2);
4975
+ break;
4976
+ }
4977
+ }
4978
+ }
4979
+ else {
4980
+ const siblingsKey = getSideBufferKey(nodeName, key);
4981
+ const sibling = diffContext.$vSiblings$.get(siblingsKey);
4982
+ if (sibling) {
4983
+ vNodeWithKey = sibling;
4984
+ diffContext.$vSiblings$.delete(siblingsKey);
4985
+ }
4986
+ }
4987
+ }
4988
+ collectSideBufferSiblings(diffContext, vNodeWithKey);
4989
+ return vNodeWithKey;
4990
+ }
4991
+ function collectSideBufferSiblings(diffContext, targetNode) {
4992
+ if (!targetNode) {
4993
+ if (diffContext.$vCurrent$) {
4994
+ const name = vnode_isElementVNode(diffContext.$vCurrent$)
4995
+ ? vnode_getElementName(diffContext.$vCurrent$)
4996
+ : null;
4997
+ const vKey = getKey(diffContext.$vCurrent$) ||
4998
+ getComponentHash(diffContext.$vCurrent$, diffContext.$container$.$getObjectById$);
4999
+ if (vKey != null) {
5000
+ const sideBufferKey = getSideBufferKey(name, vKey);
5001
+ diffContext.$vSideBuffer$ ||= new Map();
5002
+ diffContext.$vSideBuffer$.set(sideBufferKey, diffContext.$vCurrent$);
5003
+ diffContext.$vSiblings$?.delete(sideBufferKey);
5004
+ }
5005
+ }
5006
+ return;
5007
+ }
5008
+ // Walk from vCurrent up to the target node and collect all keyed siblings
5009
+ const boundary = getLevelBoundary(diffContext);
5010
+ let vNode = diffContext.$vCurrent$;
5011
+ while (vNode && vNode !== targetNode && vNode !== boundary) {
5012
+ const name = vnode_isElementVNode(vNode) ? vnode_getElementName(vNode) : null;
5013
+ const vKey = getKey(vNode) ||
5014
+ getComponentHash(vNode, diffContext.$container$.$getObjectById$);
5015
+ if (vKey != null) {
5016
+ const sideBufferKey = getSideBufferKey(name, vKey);
5017
+ diffContext.$vSideBuffer$ ||= new Map();
5018
+ diffContext.$vSideBuffer$.set(sideBufferKey, vNode);
5019
+ diffContext.$vSiblings$?.delete(sideBufferKey);
5020
+ }
5021
+ vNode = vNode.nextSibling;
5022
+ }
5023
+ }
5024
+ function getSideBufferKey(nodeName, key) {
5025
+ if (key == null) {
5026
+ return null;
5027
+ }
5028
+ return nodeName ? nodeName + ':' + key : key;
5029
+ }
5030
+ function deleteFromSideBuffer(diffContext, nodeName, key) {
5031
+ const sbKey = getSideBufferKey(nodeName, key);
5032
+ if (sbKey && diffContext.$vSideBuffer$?.has(sbKey)) {
5033
+ diffContext.$vSideBuffer$.delete(sbKey);
5034
+ return true;
5035
+ }
5036
+ return false;
5037
+ }
5038
+ /**
5039
+ * Shared utility to resolve a keyed node by:
5040
+ *
5041
+ * 1. Scanning forward siblings via `retrieveChildWithKey`
5042
+ * 2. Falling back to the side buffer using the provided `sideBufferKey`
5043
+ * 3. Creating a new node via `createNew` when not found
5044
+ *
5045
+ * If a node is moved from the side buffer, it is inserted before `vCurrent` under
5046
+ * `parentForInsert`. The function updates `vCurrent`/`vNewNode` accordingly and returns the value
5047
+ * from `createNew` when a new node is created.
5048
+ */
5049
+ function moveOrCreateKeyedNode(diffContext, nodeName, lookupKey, sideBufferKey, parentForInsert, addCurrentToSideBufferOnSideInsert) {
5050
+ // 1) Try to find the node among upcoming siblings
5051
+ diffContext.$vNewNode$ = retrieveChildWithKey(diffContext, nodeName, lookupKey);
5052
+ if (diffContext.$vNewNode$) {
5053
+ if (!sideBufferKey) {
5054
+ vnode_insertBefore(diffContext.$journal$, parentForInsert, diffContext.$vNewNode$, getCurrentInsertBefore(diffContext));
5055
+ }
5056
+ diffContext.$vCurrent$ = diffContext.$vNewNode$;
5057
+ diffContext.$vNewNode$ = null;
5058
+ return false;
5059
+ }
5060
+ // 2) Try side buffer
5061
+ if (sideBufferKey != null) {
5062
+ const buffered = diffContext.$vSideBuffer$?.get(sideBufferKey) || null;
5063
+ if (buffered) {
5064
+ diffContext.$vSideBuffer$.delete(sideBufferKey);
5065
+ if (addCurrentToSideBufferOnSideInsert && diffContext.$vCurrent$) {
5066
+ const currentKey = getKey(diffContext.$vCurrent$) ||
5067
+ getComponentHash(diffContext.$vCurrent$, diffContext.$container$.$getObjectById$);
5068
+ if (currentKey != null) {
5069
+ const currentName = vnode_isElementVNode(diffContext.$vCurrent$)
5070
+ ? vnode_getElementName(diffContext.$vCurrent$)
5071
+ : null;
5072
+ const currentSideKey = getSideBufferKey(currentName, currentKey);
5073
+ if (currentSideKey != null) {
5074
+ diffContext.$vSideBuffer$ ||= new Map();
5075
+ diffContext.$vSideBuffer$.set(currentSideKey, diffContext.$vCurrent$);
5076
+ }
5077
+ }
5078
+ }
5079
+ // Only move if the node is not already in the correct position
5080
+ if (buffered !== diffContext.$vCurrent$) {
5081
+ vnode_insertBefore(diffContext.$journal$, parentForInsert, buffered, getCurrentInsertBefore(diffContext));
5082
+ }
5083
+ diffContext.$vCurrent$ = buffered;
5084
+ diffContext.$vNewNode$ = null;
5085
+ return false;
5086
+ }
5087
+ }
5088
+ // 3) Create new
5089
+ return true;
5090
+ }
5091
+ function expectVirtual(diffContext, type, jsxKey) {
5092
+ const checkKey = type === "F" /* VirtualType.Fragment */;
5093
+ const currentKey = getKey(diffContext.$vCurrent$);
5094
+ const currentIsVirtual = diffContext.$vCurrent$ && vnode_isVirtualVNode(diffContext.$vCurrent$);
5095
+ const isSameNode = currentIsVirtual && currentKey === jsxKey && (checkKey ? !!jsxKey : true);
5096
+ if (isSameNode) {
5097
+ // All is good.
5098
+ deleteFromSideBuffer(diffContext, null, currentKey);
5099
+ return;
5100
+ }
5101
+ // For fragments without a key, always create a new virtual node (ensures rerender semantics)
5102
+ if (jsxKey === null || diffContext.$isCreationMode$) {
5103
+ vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
5104
+ diffContext.$vNewNode$.key = jsxKey;
5105
+ isDev && vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, type);
5106
+ return;
5107
+ }
5108
+ if (moveOrCreateKeyedNode(diffContext, null, jsxKey, getSideBufferKey(null, jsxKey), diffContext.$vParent$, true)) {
5109
+ vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
5110
+ diffContext.$vNewNode$.key = jsxKey;
5111
+ isDev && vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, type);
5112
+ }
5113
+ }
5114
+ function expectComponent(diffContext, component) {
5115
+ const componentMeta = component[SERIALIZABLE_STATE];
5116
+ let host = (diffContext.$vNewNode$ || diffContext.$vCurrent$);
5117
+ const jsxNode = diffContext.$jsxValue$;
5118
+ if (componentMeta) {
5119
+ const jsxProps = jsxNode.props;
5120
+ // QComponent
5121
+ let shouldRender = false;
5122
+ const [componentQRL] = componentMeta;
5123
+ const componentHash = componentQRL.$hash$;
5124
+ const vNodeComponentHash = getComponentHash(host, diffContext.$container$.$getObjectById$);
5125
+ const lookupKey = jsxNode.key || componentHash;
5126
+ const vNodeLookupKey = getKey(host) || vNodeComponentHash;
5127
+ const lookupKeysAreEqual = lookupKey === vNodeLookupKey;
5128
+ const hashesAreEqual = componentHash === vNodeComponentHash;
5129
+ if (lookupKeysAreEqual) {
5130
+ if (hashesAreEqual) {
5131
+ deleteFromSideBuffer(diffContext, null, lookupKey);
5132
+ }
5133
+ else {
5134
+ insertNewComponent(diffContext, host, componentQRL, jsxProps);
5135
+ host = diffContext.$vNewNode$;
5136
+ shouldRender = true;
5137
+ }
5138
+ }
5139
+ else {
5140
+ if (moveOrCreateKeyedNode(diffContext, null, lookupKey, lookupKey, diffContext.$vParent$)) {
5141
+ insertNewComponent(diffContext, host, componentQRL, jsxProps);
5142
+ shouldRender = true;
5143
+ }
5144
+ host = (diffContext.$vNewNode$ || diffContext.$vCurrent$);
5145
+ }
5146
+ if (host) {
5147
+ const vNodeProps = vnode_getProp(host, ELEMENT_PROPS, diffContext.$container$.$getObjectById$);
5148
+ if (!shouldRender) {
5149
+ const propsChanged = handleProps(host, jsxProps, vNodeProps, diffContext.$container$);
5150
+ // if props changed but key is null we need to insert a new component, because we need to execute hooks etc
5151
+ if (propsChanged && jsxNode.key == null) {
5152
+ insertNewComponent(diffContext, host, componentQRL, jsxProps);
5153
+ host = diffContext.$vNewNode$;
5154
+ shouldRender = true;
5155
+ }
5156
+ shouldRender ||= propsChanged;
5157
+ }
5158
+ if (shouldRender) {
5159
+ // Assign the new QRL instance to the host.
5160
+ // Unfortunately it is created every time, something to fix in the optimizer.
5161
+ vnode_setProp(host, OnRenderProp, componentQRL);
5162
+ /**
5163
+ * Mark host as not deleted. The host could have been marked as deleted if it there was a
5164
+ * cleanup run. Now we found it and want to reuse it, so we need to mark it as not deleted.
5165
+ */
5166
+ host.flags &= -33 /* VNodeFlags.Deleted */;
5167
+ markVNodeDirty(diffContext.$container$, host, 4 /* ChoreBits.COMPONENT */, diffContext.$cursor$);
5168
+ }
5165
5169
  }
5170
+ descendContentToProject(diffContext, jsxNode.children, host);
5166
5171
  }
5167
5172
  else {
5168
- if (key === null) {
5169
- for (let i = 0; i < diffContext.$vSiblingsArray$.length; i += 2) {
5170
- if (diffContext.$vSiblingsArray$[i] === nodeName) {
5171
- vNodeWithKey = diffContext.$vSiblingsArray$[i + 1];
5172
- diffContext.$vSiblingsArray$.splice(i, 2);
5173
- break;
5174
- }
5173
+ const lookupKey = jsxNode.key;
5174
+ const vNodeLookupKey = getKey(host);
5175
+ const lookupKeysAreEqual = lookupKey === vNodeLookupKey;
5176
+ const vNodeComponentHash = getComponentHash(host, diffContext.$container$.$getObjectById$);
5177
+ const isInlineComponent = vNodeComponentHash == null;
5178
+ if ((host && !isInlineComponent) || !host) {
5179
+ insertNewInlineComponent(diffContext);
5180
+ host = diffContext.$vNewNode$;
5181
+ }
5182
+ else if (!lookupKeysAreEqual) {
5183
+ if (moveOrCreateKeyedNode(diffContext, null, lookupKey, lookupKey, diffContext.$vParent$)) {
5184
+ // We did not find the inline component, create it.
5185
+ insertNewInlineComponent(diffContext);
5175
5186
  }
5187
+ host = (diffContext.$vNewNode$ || diffContext.$vCurrent$);
5176
5188
  }
5177
5189
  else {
5178
- const siblingsKey = getSideBufferKey(nodeName, key);
5179
- const sibling = diffContext.$vSiblings$.get(siblingsKey);
5180
- if (sibling) {
5181
- vNodeWithKey = sibling;
5182
- diffContext.$vSiblings$.delete(siblingsKey);
5190
+ // delete the key from the side buffer if it is the same component
5191
+ deleteFromSideBuffer(diffContext, null, lookupKey);
5192
+ }
5193
+ if (host) {
5194
+ let componentHost = host;
5195
+ // Find the closest component host which has `OnRender` prop. This is need for subscriptions context.
5196
+ while (componentHost &&
5197
+ (vnode_isVirtualVNode(componentHost)
5198
+ ? vnode_getProp(componentHost, OnRenderProp, null) === null
5199
+ : true)) {
5200
+ componentHost = componentHost.parent || vnode_getProjectionParentComponent(componentHost);
5183
5201
  }
5202
+ setInlineComponentData(host, component, componentHost, jsxNode.props);
5203
+ markVNodeDirty(diffContext.$container$, host, 8 /* ChoreBits.INLINE_COMPONENT */, diffContext.$cursor$);
5184
5204
  }
5185
5205
  }
5186
- collectSideBufferSiblings(diffContext, vNodeWithKey);
5187
- return vNodeWithKey;
5188
5206
  }
5189
- function collectSideBufferSiblings(diffContext, targetNode) {
5190
- if (!targetNode) {
5191
- if (diffContext.$vCurrent$) {
5192
- const name = vnode_isElementVNode(diffContext.$vCurrent$)
5193
- ? vnode_getElementName(diffContext.$vCurrent$)
5194
- : null;
5195
- const vKey = getKey(diffContext.$vCurrent$) ||
5196
- getComponentHash(diffContext.$vCurrent$, diffContext.$container$.$getObjectById$);
5197
- if (vKey != null) {
5198
- const sideBufferKey = getSideBufferKey(name, vKey);
5199
- diffContext.$vSideBuffer$ ||= new Map();
5200
- diffContext.$vSideBuffer$.set(sideBufferKey, diffContext.$vCurrent$);
5201
- diffContext.$vSiblings$?.delete(sideBufferKey);
5202
- }
5203
- }
5204
- return;
5207
+ function insertNewComponent(diffContext, host, componentQRL, jsxProps) {
5208
+ if (host) {
5209
+ clearAllEffects(diffContext.$container$, host);
5205
5210
  }
5206
- // Walk from vCurrent up to the target node and collect all keyed siblings
5207
- const boundary = getLevelBoundary(diffContext);
5208
- let vNode = diffContext.$vCurrent$;
5209
- while (vNode && vNode !== targetNode && vNode !== boundary) {
5210
- const name = vnode_isElementVNode(vNode) ? vnode_getElementName(vNode) : null;
5211
- const vKey = getKey(vNode) ||
5212
- getComponentHash(vNode, diffContext.$container$.$getObjectById$);
5213
- if (vKey != null) {
5214
- const sideBufferKey = getSideBufferKey(name, vKey);
5215
- diffContext.$vSideBuffer$ ||= new Map();
5216
- diffContext.$vSideBuffer$.set(sideBufferKey, vNode);
5217
- diffContext.$vSiblings$?.delete(sideBufferKey);
5211
+ vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
5212
+ const jsxNode = diffContext.$jsxValue$;
5213
+ isDev && vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "C" /* VirtualType.Component */);
5214
+ vnode_setProp(diffContext.$vNewNode$, OnRenderProp, componentQRL);
5215
+ vnode_setProp(diffContext.$vNewNode$, ELEMENT_PROPS, jsxProps);
5216
+ diffContext.$vNewNode$.key = jsxNode.key;
5217
+ }
5218
+ function insertNewInlineComponent(diffContext) {
5219
+ vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
5220
+ const jsxNode = diffContext.$jsxValue$;
5221
+ isDev &&
5222
+ vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "I" /* VirtualType.InlineComponent */);
5223
+ vnode_setProp(diffContext.$vNewNode$, ELEMENT_PROPS, jsxNode.props);
5224
+ if (jsxNode.key) {
5225
+ diffContext.$vNewNode$.key = jsxNode.key;
5226
+ }
5227
+ }
5228
+ function expectText(diffContext, text) {
5229
+ if (diffContext.$vCurrent$ !== null) {
5230
+ const type = vnode_getType(diffContext.$vCurrent$);
5231
+ if (type === 3 /* Text */) {
5232
+ if (text !== vnode_getText(diffContext.$vCurrent$)) {
5233
+ vnode_setText(diffContext.$journal$, diffContext.$vCurrent$, text);
5234
+ return;
5235
+ }
5236
+ return;
5218
5237
  }
5219
- vNode = vNode.nextSibling;
5220
5238
  }
5239
+ vnode_insertElementBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newText((qTest ? diffContext.$container$.document : document).createTextNode(text), text)), getCurrentInsertBefore(diffContext));
5221
5240
  }
5222
- function getSideBufferKey(nodeName, key) {
5223
- if (key == null) {
5241
+ /**
5242
+ * Retrieve the key from the VNode.
5243
+ *
5244
+ * @param vNode - VNode to retrieve the key from
5245
+ * @returns Key
5246
+ */
5247
+ function getKey(vNode) {
5248
+ if (vNode == null || vnode_isTextVNode(vNode)) {
5224
5249
  return null;
5225
5250
  }
5226
- return nodeName ? nodeName + ':' + key : key;
5251
+ return vNode.key;
5227
5252
  }
5228
- function deleteFromSideBuffer(diffContext, nodeName, key) {
5229
- const sbKey = getSideBufferKey(nodeName, key);
5230
- if (sbKey && diffContext.$vSideBuffer$?.has(sbKey)) {
5231
- diffContext.$vSideBuffer$.delete(sbKey);
5232
- return true;
5253
+ /**
5254
+ * Retrieve the component hash from the VNode.
5255
+ *
5256
+ * @param vNode - VNode to retrieve the key from
5257
+ * @param getObject - Function to retrieve the object by id for QComponent QRL
5258
+ * @returns Hash
5259
+ */
5260
+ function getComponentHash(vNode, getObject) {
5261
+ if (vNode == null || vnode_isTextVNode(vNode)) {
5262
+ return null;
5233
5263
  }
5234
- return false;
5264
+ const qrl = vnode_getProp(vNode, OnRenderProp, getObject);
5265
+ return qrl ? qrl.$hash$ : null;
5235
5266
  }
5236
5267
  /**
5237
- * Shared utility to resolve a keyed node by:
5268
+ * Marker class for JSX projection.
5238
5269
  *
5239
- * 1. Scanning forward siblings via `retrieveChildWithKey`
5240
- * 2. Falling back to the side buffer using the provided `sideBufferKey`
5241
- * 3. Creating a new node via `createNew` when not found
5270
+ * Assume you have component like so
5242
5271
  *
5243
- * If a node is moved from the side buffer, it is inserted before `vCurrent` under
5244
- * `parentForInsert`. The function updates `vCurrent`/`vNewNode` accordingly and returns the value
5245
- * from `createNew` when a new node is created.
5272
+ * ```
5273
+ * <SomeComponent>
5274
+ * some-text
5275
+ * <span q:slot="name">some more text</span>
5276
+ * more-text
5277
+ * </SomeComponent>
5278
+ * ```
5279
+ *
5280
+ * Before the `<SomeCompetent/>` is processed its children are transformed into:
5281
+ *
5282
+ * ```
5283
+ * <Projection q:slot="">
5284
+ * some-text
5285
+ * more-text
5286
+ * </Projection>
5287
+ * <Projection q:slot="name">
5288
+ * <span q:slot="name">some more text</span>
5289
+ * </Projection>
5290
+ * ```
5246
5291
  */
5247
- function moveOrCreateKeyedNode(diffContext, nodeName, lookupKey, sideBufferKey, parentForInsert, addCurrentToSideBufferOnSideInsert) {
5248
- // 1) Try to find the node among upcoming siblings
5249
- diffContext.$vNewNode$ = retrieveChildWithKey(diffContext, nodeName, lookupKey);
5250
- if (diffContext.$vNewNode$) {
5251
- if (!sideBufferKey) {
5252
- vnode_insertBefore(diffContext.$journal$, parentForInsert, diffContext.$vNewNode$, getCurrentInsertBefore(diffContext));
5253
- }
5254
- diffContext.$vCurrent$ = diffContext.$vNewNode$;
5255
- diffContext.$vNewNode$ = null;
5292
+ function Projection() { }
5293
+ function handleProps(host, jsxProps, vNodeProps, container) {
5294
+ let shouldRender = false;
5295
+ if (vNodeProps) {
5296
+ const constPropsDifferent = handleChangedProps(jsxProps[_CONST_PROPS], vNodeProps[_CONST_PROPS], vNodeProps[_PROPS_HANDLER], container, false);
5297
+ shouldRender ||= constPropsDifferent;
5298
+ const varPropsDifferent = handleChangedProps(jsxProps[_VAR_PROPS], vNodeProps[_VAR_PROPS], vNodeProps[_PROPS_HANDLER], container, true);
5299
+ shouldRender ||= varPropsDifferent;
5300
+ // Update the owner after all props have been synced
5301
+ vNodeProps[_OWNER] = jsxProps[_OWNER];
5302
+ }
5303
+ else if (jsxProps) {
5304
+ // If there is no props instance, create a new one.
5305
+ // We can do this because we are not using the props instance for anything else.
5306
+ vnode_setProp(host, ELEMENT_PROPS, jsxProps);
5307
+ vNodeProps = jsxProps;
5308
+ }
5309
+ return shouldRender;
5310
+ }
5311
+ function handleChangedProps(src, dst, propsHandler, container, triggerEffects = true) {
5312
+ if (isPropsEmpty(src) && isPropsEmpty(dst)) {
5256
5313
  return false;
5257
5314
  }
5258
- // 2) Try side buffer
5259
- if (sideBufferKey != null) {
5260
- const buffered = diffContext.$vSideBuffer$?.get(sideBufferKey) || null;
5261
- if (buffered) {
5262
- diffContext.$vSideBuffer$.delete(sideBufferKey);
5263
- if (addCurrentToSideBufferOnSideInsert && diffContext.$vCurrent$) {
5264
- const currentKey = getKey(diffContext.$vCurrent$) ||
5265
- getComponentHash(diffContext.$vCurrent$, diffContext.$container$.$getObjectById$);
5266
- if (currentKey != null) {
5267
- const currentName = vnode_isElementVNode(diffContext.$vCurrent$)
5268
- ? vnode_getElementName(diffContext.$vCurrent$)
5269
- : null;
5270
- const currentSideKey = getSideBufferKey(currentName, currentKey);
5271
- if (currentSideKey != null) {
5272
- diffContext.$vSideBuffer$ ||= new Map();
5273
- diffContext.$vSideBuffer$.set(currentSideKey, diffContext.$vCurrent$);
5315
+ propsHandler.$container$ = container;
5316
+ let changed = false;
5317
+ // Update changed/added props from src
5318
+ if (src) {
5319
+ for (const key in src) {
5320
+ if (key === 'children' || key === QBackRefs) {
5321
+ continue;
5322
+ }
5323
+ if (!dst || src[key] !== dst[key]) {
5324
+ if (triggerEffects) {
5325
+ if (dst) {
5326
+ // Update the value in dst BEFORE triggering effects
5327
+ // so effects see the new value
5328
+ // Note: Value is not triggering effects, because we are modyfing direct VAR_PROPS object
5329
+ dst[key] = src[key];
5330
+ }
5331
+ const didTigger = triggerPropsProxyEffect(propsHandler, key);
5332
+ if (!didTigger) {
5333
+ // If the effect was not triggered, then the prop has changed and we should rerender
5334
+ changed = true;
5274
5335
  }
5275
5336
  }
5337
+ else {
5338
+ // Early return for const props (no effects)
5339
+ return true;
5340
+ }
5276
5341
  }
5277
- // Only move if the node is not already in the correct position
5278
- if (buffered !== diffContext.$vCurrent$) {
5279
- vnode_insertBefore(diffContext.$journal$, parentForInsert, buffered, getCurrentInsertBefore(diffContext));
5342
+ }
5343
+ }
5344
+ // Remove props that are in dst but not in src
5345
+ if (dst) {
5346
+ for (const key in dst) {
5347
+ if (key === 'children' || key === QBackRefs) {
5348
+ continue;
5349
+ }
5350
+ if (!src || !_hasOwnProperty.call(src, key)) {
5351
+ if (triggerEffects) {
5352
+ delete dst[key];
5353
+ const didTigger = triggerPropsProxyEffect(propsHandler, key);
5354
+ if (!didTigger) {
5355
+ // If the effect was not triggered, then the prop has changed and we should rerender
5356
+ changed = true;
5357
+ }
5358
+ }
5280
5359
  }
5281
- diffContext.$vCurrent$ = buffered;
5282
- diffContext.$vNewNode$ = null;
5283
- return false;
5284
5360
  }
5285
5361
  }
5286
- // 3) Create new
5287
- return true;
5362
+ return changed;
5288
5363
  }
5289
- function expectVirtual(diffContext, type, jsxKey) {
5290
- const checkKey = type === "F" /* VirtualType.Fragment */;
5291
- const currentKey = getKey(diffContext.$vCurrent$);
5292
- const currentIsVirtual = diffContext.$vCurrent$ && vnode_isVirtualVNode(diffContext.$vCurrent$);
5293
- const isSameNode = currentIsVirtual && currentKey === jsxKey && (checkKey ? !!jsxKey : true);
5294
- if (isSameNode) {
5295
- // All is good.
5296
- deleteFromSideBuffer(diffContext, null, currentKey);
5297
- return;
5364
+ function isPropsEmpty(props) {
5365
+ if (!props) {
5366
+ return true;
5298
5367
  }
5299
- // For fragments without a key, always create a new virtual node (ensures rerender semantics)
5300
- if (jsxKey === null || diffContext.$isCreationMode$) {
5301
- vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
5302
- diffContext.$vNewNode$.key = jsxKey;
5303
- isDev && vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, type);
5368
+ return isObjectEmpty(props);
5369
+ }
5370
+ /**
5371
+ * If vnode is removed, it is necessary to release all subscriptions associated with it.
5372
+ *
5373
+ * This function will traverse the vnode tree in depth-first order and release all subscriptions.
5374
+ *
5375
+ * The function takes into account:
5376
+ *
5377
+ * - Projection nodes by not recursing into them.
5378
+ * - Component nodes by recursing into the component content nodes (which may be projected).
5379
+ *
5380
+ * @param cursorRoot - Optional cursor root (vStartNode) to propagate dirty bits to during diff.
5381
+ */
5382
+ function cleanup(container, journal, vNode, cursorRoot = null) {
5383
+ let vCursor = vNode;
5384
+ const cursorRootData = cursorRoot && isCursor(cursorRoot) ? getCursorData(cursorRoot) : null;
5385
+ // Depth first traversal
5386
+ if (vnode_isTextVNode(vNode)) {
5387
+ markVNodeAsDeleted(vCursor);
5388
+ // Text nodes don't have subscriptions or children;
5304
5389
  return;
5305
5390
  }
5306
- if (moveOrCreateKeyedNode(diffContext, null, jsxKey, getSideBufferKey(null, jsxKey), diffContext.$vParent$, true)) {
5307
- vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
5308
- diffContext.$vNewNode$.key = jsxKey;
5309
- isDev && vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, type);
5310
- }
5311
- }
5312
- function expectComponent(diffContext, component) {
5313
- const componentMeta = component[SERIALIZABLE_STATE];
5314
- let host = (diffContext.$vNewNode$ || diffContext.$vCurrent$);
5315
- const jsxNode = diffContext.$jsxValue$;
5316
- if (componentMeta) {
5317
- const jsxProps = jsxNode.props;
5318
- // QComponent
5319
- let shouldRender = false;
5320
- const [componentQRL] = componentMeta;
5321
- const componentHash = componentQRL.$hash$;
5322
- const vNodeComponentHash = getComponentHash(host, diffContext.$container$.$getObjectById$);
5323
- const lookupKey = jsxNode.key || componentHash;
5324
- const vNodeLookupKey = getKey(host) || vNodeComponentHash;
5325
- const lookupKeysAreEqual = lookupKey === vNodeLookupKey;
5326
- const hashesAreEqual = componentHash === vNodeComponentHash;
5327
- if (lookupKeysAreEqual) {
5328
- if (hashesAreEqual) {
5329
- deleteFromSideBuffer(diffContext, null, lookupKey);
5330
- }
5331
- else {
5332
- insertNewComponent(diffContext, host, componentQRL, jsxProps);
5333
- host = diffContext.$vNewNode$;
5334
- shouldRender = true;
5335
- }
5391
+ let vParent = null;
5392
+ do {
5393
+ if (cursorRootData && vCursor !== cursorRoot && isCursor(vCursor)) {
5394
+ abandonCursor(container, cursorRootData, vCursor);
5336
5395
  }
5337
- else {
5338
- if (moveOrCreateKeyedNode(diffContext, null, lookupKey, lookupKey, diffContext.$vParent$)) {
5339
- insertNewComponent(diffContext, host, componentQRL, jsxProps);
5340
- shouldRender = true;
5396
+ const type = vCursor.flags;
5397
+ if (type & 3 /* VNodeFlags.ELEMENT_OR_VIRTUAL_MASK */) {
5398
+ clearAllEffects(container, vCursor);
5399
+ markVNodeAsDeleted(vCursor);
5400
+ const isComponent = type & 2 /* VNodeFlags.Virtual */ &&
5401
+ vnode_getProp(vCursor, OnRenderProp, null) !== null;
5402
+ if (isComponent) {
5403
+ // cleanup q:seq content
5404
+ const seq = container.getHostProp(vCursor, ELEMENT_SEQ);
5405
+ if (seq) {
5406
+ for (let i = 0; i < seq.length; i++) {
5407
+ const obj = seq[i];
5408
+ if (isObject(obj)) {
5409
+ const objIsTask = isTask(obj);
5410
+ if (objIsTask && obj.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */) {
5411
+ obj.$flags$ |= 16 /* TaskFlags.NEEDS_CLEANUP */;
5412
+ markVNodeDirty(container, vCursor, 128 /* ChoreBits.CLEANUP */, cursorRoot);
5413
+ // don't call cleanupDestroyable yet, do it by the scheduler
5414
+ continue;
5415
+ }
5416
+ else if (obj instanceof SignalImpl || isStore(obj)) {
5417
+ clearAllEffects(container, obj);
5418
+ }
5419
+ if (objIsTask || obj instanceof AsyncSignalImpl) {
5420
+ cleanupDestroyable(obj);
5421
+ }
5422
+ }
5423
+ }
5424
+ }
5425
+ // SPECIAL CASE: If we are a component, we need to descend into the projected content and release the content.
5426
+ const attrs = vCursor.props;
5427
+ if (attrs) {
5428
+ const keys = Object.keys(attrs);
5429
+ for (let i = 0; i < keys.length; i++) {
5430
+ const key = keys[i];
5431
+ if (isSlotProp(key)) {
5432
+ const value = attrs[key];
5433
+ if (value) {
5434
+ attrs[key] = null; // prevent infinite loop
5435
+ const projection = typeof value === 'string'
5436
+ ? vnode_locate(container.rootVNode, value)
5437
+ : value;
5438
+ let projectionChild = vnode_getFirstChild(projection);
5439
+ while (projectionChild) {
5440
+ cleanup(container, journal, projectionChild, cursorRoot);
5441
+ projectionChild = projectionChild.nextSibling;
5442
+ }
5443
+ cleanupStaleUnclaimedProjection(container, journal, projection);
5444
+ }
5445
+ }
5446
+ }
5447
+ }
5341
5448
  }
5342
- host = (diffContext.$vNewNode$ || diffContext.$vCurrent$);
5343
- }
5344
- if (host) {
5345
- const vNodeProps = vnode_getProp(host, ELEMENT_PROPS, diffContext.$container$.$getObjectById$);
5346
- if (!shouldRender) {
5347
- const propsChanged = handleProps(host, jsxProps, vNodeProps, diffContext.$container$);
5348
- // if props changed but key is null we need to insert a new component, because we need to execute hooks etc
5349
- if (propsChanged && jsxNode.key == null) {
5350
- insertNewComponent(diffContext, host, componentQRL, jsxProps);
5351
- host = diffContext.$vNewNode$;
5352
- shouldRender = true;
5449
+ const isProjection = vnode_isProjection(vCursor);
5450
+ // Descend into children
5451
+ if (!isProjection) {
5452
+ // Only if it is not a projection
5453
+ const vFirstChild = vnode_getFirstChild(vCursor);
5454
+ if (vFirstChild) {
5455
+ vCursor = vFirstChild;
5456
+ continue;
5353
5457
  }
5354
- shouldRender ||= propsChanged;
5355
5458
  }
5356
- if (shouldRender) {
5357
- // Assign the new QRL instance to the host.
5358
- // Unfortunately it is created every time, something to fix in the optimizer.
5359
- vnode_setProp(host, OnRenderProp, componentQRL);
5459
+ // TODO: probably can be removed
5460
+ else if (vCursor === vNode) {
5360
5461
  /**
5361
- * Mark host as not deleted. The host could have been marked as deleted if it there was a
5362
- * cleanup run. Now we found it and want to reuse it, so we need to mark it as not deleted.
5462
+ * If it is a projection and we are at the root, then we should only walk the children to
5463
+ * materialize the projection content. This is because we could have references in the vnode
5464
+ * refs map which need to be materialized before cleanup.
5363
5465
  */
5364
- host.flags &= -33 /* VNodeFlags.Deleted */;
5365
- markVNodeDirty(diffContext.$container$, host, 4 /* ChoreBits.COMPONENT */, diffContext.$cursor$);
5466
+ const vFirstChild = vnode_getFirstChild(vCursor);
5467
+ if (vFirstChild) {
5468
+ vnode_walkVNode(vFirstChild);
5469
+ return;
5470
+ }
5471
+ clearProjectionFromSlotParent(container, vCursor);
5366
5472
  }
5367
5473
  }
5368
- descendContentToProject(diffContext, jsxNode.children, host);
5369
- }
5370
- else {
5371
- const lookupKey = jsxNode.key;
5372
- const vNodeLookupKey = getKey(host);
5373
- const lookupKeysAreEqual = lookupKey === vNodeLookupKey;
5374
- const vNodeComponentHash = getComponentHash(host, diffContext.$container$.$getObjectById$);
5375
- const isInlineComponent = vNodeComponentHash == null;
5376
- if ((host && !isInlineComponent) || !host) {
5377
- insertNewInlineComponent(diffContext);
5378
- host = diffContext.$vNewNode$;
5474
+ else if (type & 4 /* VNodeFlags.Text */) {
5475
+ markVNodeAsDeleted(vCursor);
5379
5476
  }
5380
- else if (!lookupKeysAreEqual) {
5381
- if (moveOrCreateKeyedNode(diffContext, null, lookupKey, lookupKey, diffContext.$vParent$)) {
5382
- // We did not find the inline component, create it.
5383
- insertNewInlineComponent(diffContext);
5384
- }
5385
- host = (diffContext.$vNewNode$ || diffContext.$vCurrent$);
5477
+ // Out of children
5478
+ if (vCursor === vNode) {
5479
+ // we are where we started, this means that vNode has no children, so we are done.
5480
+ return;
5386
5481
  }
5387
- else {
5388
- // delete the key from the side buffer if it is the same component
5389
- deleteFromSideBuffer(diffContext, null, lookupKey);
5482
+ // Out of children, go to next sibling
5483
+ const vNextSibling = vCursor.nextSibling;
5484
+ if (vNextSibling) {
5485
+ vCursor = vNextSibling;
5486
+ continue;
5390
5487
  }
5391
- if (host) {
5392
- let componentHost = host;
5393
- // Find the closest component host which has `OnRender` prop. This is need for subscriptions context.
5394
- while (componentHost &&
5395
- (vnode_isVirtualVNode(componentHost)
5396
- ? vnode_getProp(componentHost, OnRenderProp, null) === null
5397
- : true)) {
5398
- componentHost = componentHost.parent || vnode_getProjectionParentComponent(componentHost);
5488
+ // Out of siblings, go to parent
5489
+ vParent = vCursor.parent;
5490
+ while (vParent) {
5491
+ if (vParent === vNode) {
5492
+ // We are back where we started, we are done.
5493
+ return;
5494
+ }
5495
+ const vNextParentSibling = vParent.nextSibling;
5496
+ if (vNextParentSibling) {
5497
+ vCursor = vNextParentSibling;
5498
+ break;
5399
5499
  }
5400
- const jsxOutput = executeComponent(diffContext.$container$, host, (componentHost || diffContext.$container$.rootVNode), component, jsxNode.props);
5401
- diffContext.$asyncQueue$.push(jsxOutput, host);
5500
+ vParent = vParent.parent;
5501
+ }
5502
+ if (vParent == null) {
5503
+ // We are done.
5504
+ return;
5402
5505
  }
5506
+ } while (true);
5507
+ }
5508
+ function clearProjectionFromSlotParent(container, vNode) {
5509
+ if (!vNode.slotParent) {
5510
+ return;
5511
+ }
5512
+ const slotName = container.getHostProp(vNode, QSlot);
5513
+ if (slotName != null && container.getHostProp(vNode.slotParent, slotName) === vNode) {
5514
+ vnode_setProp(vNode.slotParent, slotName, null);
5403
5515
  }
5404
5516
  }
5405
- function insertNewComponent(diffContext, host, componentQRL, jsxProps) {
5406
- if (host) {
5407
- clearAllEffects(diffContext.$container$, host);
5517
+ function cleanupStaleUnclaimedProjection(container, journal, projection) {
5518
+ // we are removing a node where the projection would go after slot render.
5519
+ // This is not needed, so we need to cleanup still unclaimed projection
5520
+ const projectionParent = projection.parent;
5521
+ if (projectionParent) {
5522
+ const projectionParentType = projectionParent.flags;
5523
+ if (projectionParentType & 1 /* VNodeFlags.Element */ &&
5524
+ vnode_getElementName(projectionParent) === QTemplate) {
5525
+ // if parent is the q:template element then projection is still unclaimed - remove it
5526
+ clearProjectionFromSlotParent(container, projection);
5527
+ vnode_remove(journal, projectionParent, projection, true);
5528
+ }
5408
5529
  }
5409
- vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
5410
- const jsxNode = diffContext.$jsxValue$;
5411
- isDev && vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "C" /* VirtualType.Component */);
5412
- vnode_setProp(diffContext.$vNewNode$, OnRenderProp, componentQRL);
5413
- vnode_setProp(diffContext.$vNewNode$, ELEMENT_PROPS, jsxProps);
5414
- diffContext.$vNewNode$.key = jsxNode.key;
5415
5530
  }
5416
- function insertNewInlineComponent(diffContext) {
5417
- vnode_insertVirtualBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newVirtual()), getInsertBefore(diffContext));
5418
- const jsxNode = diffContext.$jsxValue$;
5419
- isDev &&
5420
- vnode_setProp(diffContext.$vNewNode$, DEBUG_TYPE, "I" /* VirtualType.InlineComponent */);
5421
- vnode_setProp(diffContext.$vNewNode$, ELEMENT_PROPS, jsxNode.props);
5422
- if (jsxNode.key) {
5423
- diffContext.$vNewNode$.key = jsxNode.key;
5531
+ function markVNodeAsDeleted(vCursor) {
5532
+ /**
5533
+ * Marks vCursor as deleted. We need to do this to prevent chores from running after the vnode is
5534
+ * removed. (for example signal subscriptions)
5535
+ */
5536
+ vCursor.flags |= 32 /* VNodeFlags.Deleted */;
5537
+ }
5538
+ function areWrappedSignalsEqual(oldSignal, newSignal) {
5539
+ if (oldSignal === newSignal) {
5540
+ return true;
5424
5541
  }
5542
+ return (newSignal.$func$ === oldSignal.$func$ && areArgumentsEqual(newSignal.$args$, oldSignal.$args$));
5425
5543
  }
5426
- function expectText(diffContext, text) {
5427
- if (diffContext.$vCurrent$ !== null) {
5428
- const type = vnode_getType(diffContext.$vCurrent$);
5429
- if (type === 3 /* Text */) {
5430
- if (text !== vnode_getText(diffContext.$vCurrent$)) {
5431
- vnode_setText(diffContext.$journal$, diffContext.$vCurrent$, text);
5432
- return;
5433
- }
5434
- return;
5544
+ function areArgumentsEqual(oldArgs, newArgs) {
5545
+ if (oldArgs === newArgs) {
5546
+ return true;
5547
+ }
5548
+ if (!oldArgs || !newArgs || oldArgs.length !== newArgs.length) {
5549
+ return false;
5550
+ }
5551
+ for (let i = 0; i < oldArgs.length; i++) {
5552
+ if (oldArgs[i] !== newArgs[i]) {
5553
+ return false;
5435
5554
  }
5436
5555
  }
5437
- vnode_insertElementBefore(diffContext.$journal$, diffContext.$vParent$, (diffContext.$vNewNode$ = vnode_newText((qTest ? diffContext.$container$.document : document).createTextNode(text), text)), getCurrentInsertBefore(diffContext));
5556
+ return true;
5438
5557
  }
5439
- /**
5440
- * Retrieve the key from the VNode.
5441
- *
5442
- * @param vNode - VNode to retrieve the key from
5443
- * @returns Key
5444
- */
5445
- function getKey(vNode) {
5446
- if (vNode == null || vnode_isTextVNode(vNode)) {
5447
- return null;
5558
+ function containsWrappedSignal(data, signal) {
5559
+ if (!(signal instanceof WrappedSignalImpl)) {
5560
+ return false;
5448
5561
  }
5449
- return vNode.key;
5450
- }
5451
- /**
5452
- * Retrieve the component hash from the VNode.
5453
- *
5454
- * @param vNode - VNode to retrieve the key from
5455
- * @param getObject - Function to retrieve the object by id for QComponent QRL
5456
- * @returns Hash
5457
- */
5458
- function getComponentHash(vNode, getObject) {
5459
- if (vNode == null || vnode_isTextVNode(vNode)) {
5460
- return null;
5562
+ for (let i = 0; i < data.length; i++) {
5563
+ const item = data[i];
5564
+ if (item instanceof WrappedSignalImpl && areWrappedSignalsEqual(item, signal)) {
5565
+ return true;
5566
+ }
5461
5567
  }
5462
- const qrl = vnode_getProp(vNode, OnRenderProp, getObject);
5463
- return qrl ? qrl.$hash$ : null;
5568
+ return false;
5464
5569
  }
5570
+
5465
5571
  /**
5466
- * Marker class for JSX projection.
5572
+ * Use `executeComponent` to execute a component.
5467
5573
  *
5468
- * Assume you have component like so
5574
+ * Component execution can be complex because of:
5469
5575
  *
5470
- * ```
5471
- * <SomeComponent>
5472
- * some-text
5473
- * <span q:slot="name">some more text</span>
5474
- * more-text
5475
- * </SomeComponent>
5476
- * ```
5576
+ * - It can by async
5577
+ * - It can contain many tasks which need to be awaited
5578
+ * - Each task can run multiple times if they track signals which change.
5579
+ * - The JSX may be re-generated multiple times of a task needs to be rerun due to signal change.
5580
+ * - It needs to keep track of hook state.
5477
5581
  *
5478
- * Before the `<SomeCompetent/>` is processed its children are transformed into:
5582
+ * For `component$`: `renderHost` === `subscriptionHost` For inlined-components: the
5583
+ * `subscriptionHost` is a parent `component$` which needs to re-execute.
5479
5584
  *
5480
- * ```
5481
- * <Projection q:slot="">
5482
- * some-text
5483
- * more-text
5484
- * </Projection>
5485
- * <Projection q:slot="name">
5486
- * <span q:slot="name">some more text</span>
5487
- * </Projection>
5488
- * ```
5585
+ * @param container
5586
+ * @param renderHost - VNode into which the component is rendered into.
5587
+ * @param subscriptionHost - VNode which will be re-executed if the component needs to re-render.
5588
+ * @param componentQRL
5589
+ * @param props
5590
+ * @returns
5489
5591
  */
5490
- function Projection() { }
5491
- function handleProps(host, jsxProps, vNodeProps, container) {
5492
- let shouldRender = false;
5493
- if (vNodeProps) {
5494
- const constPropsDifferent = handleChangedProps(jsxProps[_CONST_PROPS], vNodeProps[_CONST_PROPS], vNodeProps[_PROPS_HANDLER], container, false);
5495
- shouldRender ||= constPropsDifferent;
5496
- const varPropsDifferent = handleChangedProps(jsxProps[_VAR_PROPS], vNodeProps[_VAR_PROPS], vNodeProps[_PROPS_HANDLER], container, true);
5497
- shouldRender ||= varPropsDifferent;
5498
- // Update the owner after all props have been synced
5499
- vNodeProps[_OWNER] = jsxProps[_OWNER];
5592
+ const executeComponent = (container, renderHost, subscriptionHost, componentQRL, props) => {
5593
+ const iCtx = newRenderInvokeContext(container.$locale$, renderHost, container);
5594
+ if (subscriptionHost) {
5595
+ iCtx.$effectSubscriber$ = getSubscriber(subscriptionHost, ":" /* EffectProperty.COMPONENT */);
5596
+ iCtx.$container$ = container;
5500
5597
  }
5501
- else if (jsxProps) {
5502
- // If there is no props instance, create a new one.
5503
- // We can do this because we are not using the props instance for anything else.
5504
- vnode_setProp(host, ELEMENT_PROPS, jsxProps);
5505
- vNodeProps = jsxProps;
5598
+ let componentFn;
5599
+ container.ensureProjectionResolved(renderHost);
5600
+ let isInlineComponent = false;
5601
+ if (componentQRL === null) {
5602
+ componentQRL = container.getHostProp(renderHost, OnRenderProp);
5603
+ isDev && assertDefined(componentQRL, 'No Component found at this location');
5506
5604
  }
5507
- return shouldRender;
5508
- }
5509
- function handleChangedProps(src, dst, propsHandler, container, triggerEffects = true) {
5510
- if (isPropsEmpty(src) && isPropsEmpty(dst)) {
5511
- return false;
5605
+ if (isQrl(componentQRL)) {
5606
+ props = props || container.getHostProp(renderHost, ELEMENT_PROPS) || EMPTY_OBJ;
5607
+ // TODO is this possible? JSXNode handles this, no?
5608
+ if ('children' in props) {
5609
+ delete props.children;
5610
+ }
5611
+ componentFn = componentQRL.getFn(iCtx);
5512
5612
  }
5513
- propsHandler.$container$ = container;
5514
- let changed = false;
5515
- // Update changed/added props from src
5516
- if (src) {
5517
- for (const key in src) {
5518
- if (key === 'children' || key === QBackRefs) {
5519
- continue;
5520
- }
5521
- if (!dst || src[key] !== dst[key]) {
5522
- if (triggerEffects) {
5523
- if (dst) {
5524
- // Update the value in dst BEFORE triggering effects
5525
- // so effects see the new value
5526
- // Note: Value is not triggering effects, because we are modyfing direct VAR_PROPS object
5527
- dst[key] = src[key];
5528
- }
5529
- const didTigger = triggerPropsProxyEffect(propsHandler, key);
5530
- if (!didTigger) {
5531
- // If the effect was not triggered, then the prop has changed and we should rerender
5532
- changed = true;
5533
- }
5534
- }
5535
- else {
5536
- // Early return for const props (no effects)
5537
- return true;
5613
+ else if (isQwikComponent(componentQRL)) {
5614
+ const qComponentFn = componentQRL;
5615
+ componentFn = () => invokeApply(iCtx, qComponentFn, [props || EMPTY_OBJ, null, 0]);
5616
+ }
5617
+ else {
5618
+ isInlineComponent = true;
5619
+ const inlineComponent = componentQRL;
5620
+ componentFn = () => invokeApply(iCtx, inlineComponent, [props || EMPTY_OBJ]);
5621
+ }
5622
+ const isSsr = qTest ? isServerPlatform() : isServer;
5623
+ const executeComponentWithPromiseExceptionRetry = (retryCount = 0) => safeCall(() => {
5624
+ if (!isInlineComponent) {
5625
+ container.setHostProp(renderHost, ELEMENT_SEQ_IDX, null);
5626
+ container.setHostProp(renderHost, USE_ON_LOCAL_SEQ_IDX, null);
5627
+ }
5628
+ if (retryCount > 0 && vnode_isVNode(renderHost)) {
5629
+ clearAllEffects(container, renderHost);
5630
+ }
5631
+ return maybeThen(componentFn(props), (jsx) => maybeThen(iCtx.$waitOn$, () => jsx));
5632
+ }, (jsx) => {
5633
+ // In SSR, check if the component was marked dirty (COMPONENT bit) during execution.
5634
+ // This happens when something completes and updates reactive primitives
5635
+ // while we're waiting on $waitOn$. If so, we need to re-execute the component
5636
+ // to get fresh JSX with updated values.
5637
+ if (isSsr && !isInlineComponent) {
5638
+ const ssrNode = renderHost;
5639
+ if (ssrNode.dirty & 4 /* ChoreBits.COMPONENT */) {
5640
+ ssrNode.dirty &= ~4 /* ChoreBits.COMPONENT */;
5641
+ if (retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
5642
+ return executeComponentWithPromiseExceptionRetry(retryCount + 1);
5538
5643
  }
5539
5644
  }
5540
5645
  }
5541
- }
5542
- // Remove props that are in dst but not in src
5543
- if (dst) {
5544
- for (const key in dst) {
5545
- if (key === 'children' || key === QBackRefs) {
5546
- continue;
5547
- }
5548
- if (!src || !_hasOwnProperty.call(src, key)) {
5549
- if (triggerEffects) {
5550
- delete dst[key];
5551
- const didTigger = triggerPropsProxyEffect(propsHandler, key);
5552
- if (!didTigger) {
5553
- // If the effect was not triggered, then the prop has changed and we should rerender
5554
- changed = true;
5555
- }
5556
- }
5646
+ const useOnEvents = container.getHostProp(renderHost, USE_ON_LOCAL);
5647
+ if (useOnEvents) {
5648
+ return addUseOnEvents(container, jsx, useOnEvents);
5649
+ }
5650
+ return jsx;
5651
+ }, (err) => {
5652
+ if (isPromise(err) && retryCount < MAX_RETRY_ON_PROMISE_COUNT) {
5653
+ return err.then(() => executeComponentWithPromiseExceptionRetry(++retryCount));
5654
+ }
5655
+ else {
5656
+ if (retryCount >= MAX_RETRY_ON_PROMISE_COUNT) {
5657
+ throw new Error(`Max retry count of component execution reached`);
5557
5658
  }
5659
+ throw err;
5558
5660
  }
5559
- }
5560
- return changed;
5561
- }
5562
- function isPropsEmpty(props) {
5563
- if (!props) {
5564
- return true;
5565
- }
5566
- return isObjectEmpty(props);
5567
- }
5568
- /**
5569
- * If vnode is removed, it is necessary to release all subscriptions associated with it.
5570
- *
5571
- * This function will traverse the vnode tree in depth-first order and release all subscriptions.
5572
- *
5573
- * The function takes into account:
5574
- *
5575
- * - Projection nodes by not recursing into them.
5576
- * - Component nodes by recursing into the component content nodes (which may be projected).
5661
+ });
5662
+ return executeComponentWithPromiseExceptionRetry();
5663
+ };
5664
+ /**
5665
+ * Adds `useOn` events to the JSX output.
5577
5666
  *
5578
- * @param cursorRoot - Optional cursor root (vStartNode) to propagate dirty bits to during diff.
5667
+ * @param jsx The JSX output to modify.
5668
+ * @param useOnEvents The `useOn` events to add.
5669
+ * @returns The modified JSX output.
5579
5670
  */
5580
- function cleanup(container, journal, vNode, cursorRoot = null) {
5581
- let vCursor = vNode;
5582
- // Depth first traversal
5583
- if (vnode_isTextVNode(vNode)) {
5584
- markVNodeAsDeleted(vCursor);
5585
- // Text nodes don't have subscriptions or children;
5586
- return;
5587
- }
5588
- let vParent = null;
5589
- do {
5590
- const type = vCursor.flags;
5591
- if (type & 3 /* VNodeFlags.ELEMENT_OR_VIRTUAL_MASK */) {
5592
- clearAllEffects(container, vCursor);
5593
- markVNodeAsDeleted(vCursor);
5594
- const isComponent = type & 2 /* VNodeFlags.Virtual */ &&
5595
- vnode_getProp(vCursor, OnRenderProp, null) !== null;
5596
- if (isComponent) {
5597
- // cleanup q:seq content
5598
- const seq = container.getHostProp(vCursor, ELEMENT_SEQ);
5599
- if (seq) {
5600
- for (let i = 0; i < seq.length; i++) {
5601
- const obj = seq[i];
5602
- if (isObject(obj)) {
5603
- const objIsTask = isTask(obj);
5604
- if (objIsTask && obj.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */) {
5605
- obj.$flags$ |= 16 /* TaskFlags.NEEDS_CLEANUP */;
5606
- markVNodeDirty(container, vCursor, 64 /* ChoreBits.CLEANUP */, cursorRoot);
5607
- // don't call cleanupDestroyable yet, do it by the scheduler
5608
- continue;
5609
- }
5610
- else if (obj instanceof SignalImpl || isStore(obj)) {
5611
- clearAllEffects(container, obj);
5612
- }
5613
- if (objIsTask || obj instanceof AsyncSignalImpl) {
5614
- cleanupDestroyable(obj);
5671
+ function addUseOnEvents(container, jsx, useOnEvents) {
5672
+ const jsxElement = findFirstElementNode(jsx);
5673
+ let jsxResult = jsx;
5674
+ const qVisibleEvent = 'q-e:qvisible';
5675
+ return maybeThen(jsxElement, (jsxElement) => {
5676
+ // headless components are components that don't render a real DOM element
5677
+ const isHeadless = !jsxElement;
5678
+ // placeholder element is a <script> element that is used to add events to the document or window
5679
+ let placeholderElement = null;
5680
+ for (const key in useOnEvents) {
5681
+ if (Object.prototype.hasOwnProperty.call(useOnEvents, key)) {
5682
+ let targetElement = jsxElement;
5683
+ let eventKey = key;
5684
+ if (isHeadless) {
5685
+ // if the component is headless, we need to add the event to the placeholder element
5686
+ if (key === qVisibleEvent ||
5687
+ key.startsWith("q-d:" /* EventNameHtmlScope.document */) ||
5688
+ key.startsWith("q-w:" /* EventNameHtmlScope.window */)) {
5689
+ if (!placeholderElement) {
5690
+ placeholderElement = createPlaceholderScriptNode();
5691
+ // A headless component that projects the document root (e.g. wrapping `<head>`/`<body>`
5692
+ // in a `<Slot/>`) would otherwise place the placeholder `<script>` as a direct child of
5693
+ // `<html>`, which is invalid. On the server let the container defer it into `<head>`.
5694
+ const isSsr = qTest ? isServerPlatform() : isServer;
5695
+ if (!(isSsr && container.$deferRootPlaceholder$(placeholderElement))) {
5696
+ jsxResult = injectPlaceholderElement(jsxResult, placeholderElement);
5615
5697
  }
5616
5698
  }
5699
+ targetElement = placeholderElement;
5617
5700
  }
5618
- }
5619
- // SPECIAL CASE: If we are a component, we need to descend into the projected content and release the content.
5620
- const attrs = vCursor.props;
5621
- if (attrs) {
5622
- const keys = Object.keys(attrs);
5623
- for (let i = 0; i < keys.length; i++) {
5624
- const key = keys[i];
5625
- if (isSlotProp(key)) {
5626
- const value = attrs[key];
5627
- if (value) {
5628
- attrs[key] = null; // prevent infinite loop
5629
- const projection = typeof value === 'string'
5630
- ? vnode_locate(container.rootVNode, value)
5631
- : value;
5632
- let projectionChild = vnode_getFirstChild(projection);
5633
- while (projectionChild) {
5634
- cleanup(container, journal, projectionChild, cursorRoot);
5635
- projectionChild = projectionChild.nextSibling;
5636
- }
5637
- cleanupStaleUnclaimedProjection(journal, projection);
5638
- }
5701
+ else {
5702
+ if (isDev) {
5703
+ const sourceLocation = getUseOnSourceLocation(useOnEvents[key].qrls);
5704
+ logWarn('You are trying to add an event "' +
5705
+ key +
5706
+ '" using `useOn` hook, ' +
5707
+ 'but a node to which you can add an event is not found. ' +
5708
+ 'Please make sure that the component outputs a DOM element.' +
5709
+ (sourceLocation ? ` Offending \`useOn\`: ${sourceLocation}.` : ''));
5639
5710
  }
5711
+ continue;
5640
5712
  }
5641
5713
  }
5642
- }
5643
- const isProjection = vnode_isProjection(vCursor);
5644
- // Descend into children
5645
- if (!isProjection) {
5646
- // Only if it is not a projection
5647
- const vFirstChild = vnode_getFirstChild(vCursor);
5648
- if (vFirstChild) {
5649
- vCursor = vFirstChild;
5650
- continue;
5651
- }
5652
- }
5653
- // TODO: probably can be removed
5654
- else if (vCursor === vNode) {
5655
- /**
5656
- * If it is a projection and we are at the root, then we should only walk the children to
5657
- * materialize the projection content. This is because we could have references in the vnode
5658
- * refs map which need to be materialized before cleanup.
5659
- */
5660
- const vFirstChild = vnode_getFirstChild(vCursor);
5661
- if (vFirstChild) {
5662
- vnode_walkVNode(vFirstChild, (vNode) => {
5663
- /**
5664
- * Instead of an ID, we store a direct reference to the VNode. This is necessary to
5665
- * locate the slot's parent in a detached subtree, as the ID would become invalid.
5666
- */
5667
- if (vNode.flags & 2 /* VNodeFlags.Virtual */) {
5668
- // The QSlotParent is used to find the slot parent during scheduling
5669
- vNode.slotParent;
5714
+ if (targetElement) {
5715
+ if (targetElement.type === 'script' && key === qVisibleEvent) {
5716
+ eventKey = 'q-d:qinit';
5717
+ if (isDev) {
5718
+ const sourceLocation = getUseOnSourceLocation(useOnEvents[key].qrls);
5719
+ logWarn(`You are trying to add the event "${key}" ` +
5720
+ 'using the `useVisibleTask$` hook with the "intersection-observer" strategy, ' +
5721
+ 'but this only works when the component outputs a DOM element. Falling back to ' +
5722
+ '"document-ready" instead.' +
5723
+ (sourceLocation ? ` Offending \`useVisibleTask$\`: ${sourceLocation}.` : ''));
5670
5724
  }
5671
- });
5672
- return;
5725
+ }
5726
+ addUseOnEvent(targetElement, eventKey, useOnEvents[key]);
5673
5727
  }
5674
5728
  }
5675
5729
  }
5676
- else if (type & 4 /* VNodeFlags.Text */) {
5677
- markVNodeAsDeleted(vCursor);
5678
- }
5679
- // Out of children
5680
- if (vCursor === vNode) {
5681
- // we are where we started, this means that vNode has no children, so we are done.
5682
- return;
5683
- }
5684
- // Out of children, go to next sibling
5685
- const vNextSibling = vCursor.nextSibling;
5686
- if (vNextSibling) {
5687
- vCursor = vNextSibling;
5688
- continue;
5689
- }
5690
- // Out of siblings, go to parent
5691
- vParent = vCursor.parent;
5692
- while (vParent) {
5693
- if (vParent === vNode) {
5694
- // We are back where we started, we are done.
5695
- return;
5696
- }
5697
- const vNextParentSibling = vParent.nextSibling;
5698
- if (vNextParentSibling) {
5699
- vCursor = vNextParentSibling;
5700
- break;
5730
+ return jsxResult;
5731
+ });
5732
+ }
5733
+ function getUseOnSourceLocation(eventQrls) {
5734
+ for (let i = 0; i < eventQrls.length; i++) {
5735
+ const eventQrl = eventQrls[i];
5736
+ const task = eventQrl?.getCaptured()?.[0];
5737
+ if (isTask(task)) {
5738
+ const dev = task.$qrl$.dev;
5739
+ if (dev?.file) {
5740
+ return typeof dev.lo === 'number' && typeof dev.hi === 'number'
5741
+ ? `${dev.file}:${dev.lo}-${dev.hi}`
5742
+ : dev.file;
5701
5743
  }
5702
- vParent = vParent.parent;
5703
- }
5704
- if (vParent == null) {
5705
- // We are done.
5706
- return;
5707
5744
  }
5708
- } while (true);
5745
+ }
5746
+ return null;
5709
5747
  }
5710
- function cleanupStaleUnclaimedProjection(journal, projection) {
5711
- // we are removing a node where the projection would go after slot render.
5712
- // This is not needed, so we need to cleanup still unclaimed projection
5713
- const projectionParent = projection.parent;
5714
- if (projectionParent) {
5715
- const projectionParentType = projectionParent.flags;
5716
- if (projectionParentType & 1 /* VNodeFlags.Element */ &&
5717
- vnode_getElementName(projectionParent) === QTemplate) {
5718
- // if parent is the q:template element then projection is still unclaimed - remove it
5719
- vnode_remove(journal, projectionParent, projection, true);
5748
+ /**
5749
+ * Adds an event to the JSX element.
5750
+ *
5751
+ * @param jsxElement The JSX element to add the event to.
5752
+ * @param key The event name.
5753
+ * @param value The event value.
5754
+ */
5755
+ function addUseOnEvent(jsxElement, key, value) {
5756
+ // These handlers are always there, so they go in constProps
5757
+ const props = (jsxElement.constProps ||= {});
5758
+ const propValue = props[key];
5759
+ const qrls = value.qrls;
5760
+ if (propValue == null) {
5761
+ props[key] = qrls;
5762
+ }
5763
+ else if (Array.isArray(propValue)) {
5764
+ propValue.push(...qrls);
5765
+ }
5766
+ else {
5767
+ props[key] = [propValue, ...qrls];
5768
+ }
5769
+ const varProp = jsxElement.varProps[key];
5770
+ if (varProp) {
5771
+ // we need to demote the handlers to varProps
5772
+ if (Array.isArray(propValue)) {
5773
+ propValue.push(...props[key]);
5720
5774
  }
5775
+ else {
5776
+ jsxElement.varProps[key] = [propValue, ...qrls];
5777
+ }
5778
+ props[key] = undefined;
5721
5779
  }
5780
+ const capture = value.capture;
5781
+ const preventdefault = value.preventdefault;
5782
+ const stoppropagation = value.stoppropagation;
5783
+ if (!capture && !preventdefault && !stoppropagation) {
5784
+ return;
5785
+ }
5786
+ const [, eventName] = getEventDataFromHtmlAttribute(key);
5787
+ capture && addUseOnModifier(jsxElement, eventName, 'capture');
5788
+ preventdefault && addUseOnModifier(jsxElement, eventName, 'preventdefault');
5789
+ stoppropagation && addUseOnModifier(jsxElement, eventName, 'stoppropagation');
5722
5790
  }
5723
- function markVNodeAsDeleted(vCursor) {
5724
- /**
5725
- * Marks vCursor as deleted. We need to do this to prevent chores from running after the vnode is
5726
- * removed. (for example signal subscriptions)
5727
- */
5728
- vCursor.flags |= 32 /* VNodeFlags.Deleted */;
5791
+ function addUseOnModifier(jsxElement, eventName, modifier) {
5792
+ const key = `${modifier}:${eventName}`;
5793
+ const varProps = jsxElement.varProps;
5794
+ if (varProps === EMPTY_OBJ) {
5795
+ jsxElement.varProps = {};
5796
+ }
5797
+ jsxElement.varProps[key] = true;
5729
5798
  }
5730
- function areWrappedSignalsEqual(oldSignal, newSignal) {
5731
- if (oldSignal === newSignal) {
5732
- return true;
5799
+ /**
5800
+ * Finds the first element node in the JSX output.
5801
+ *
5802
+ * @param jsx The JSX output to search.
5803
+ * @returns The first element node or null if no element node is found.
5804
+ */
5805
+ function findFirstElementNode(jsx) {
5806
+ const queue = [jsx];
5807
+ while (queue.length) {
5808
+ const jsx = queue.shift();
5809
+ if (isJSXNode(jsx)) {
5810
+ if (typeof jsx.type === 'string') {
5811
+ return jsx;
5812
+ }
5813
+ queue.push(jsx.children);
5814
+ }
5815
+ else if (isArray(jsx)) {
5816
+ queue.push(...jsx);
5817
+ }
5818
+ else if (isPromise(jsx)) {
5819
+ return maybeThen(jsx, (jsx) => findFirstElementNode(jsx));
5820
+ }
5821
+ else if (isSignal(jsx)) {
5822
+ return findFirstElementNode(jsx.untrackedValue);
5823
+ }
5733
5824
  }
5734
- return (newSignal.$func$ === oldSignal.$func$ && areArgumentsEqual(newSignal.$args$, oldSignal.$args$));
5825
+ return null;
5735
5826
  }
5736
- function areArgumentsEqual(oldArgs, newArgs) {
5737
- if (oldArgs === newArgs) {
5738
- return true;
5739
- }
5740
- if (!oldArgs || !newArgs || oldArgs.length !== newArgs.length) {
5741
- return false;
5742
- }
5743
- for (let i = 0; i < oldArgs.length; i++) {
5744
- if (oldArgs[i] !== newArgs[i]) {
5745
- return false;
5827
+ /**
5828
+ * Injects a placeholder <script> element into the JSX output.
5829
+ *
5830
+ * This is necessary for headless components (components that don't render a real DOM element) to
5831
+ * have an anchor point for `useOn` event listeners that target the document or window.
5832
+ *
5833
+ * @param jsx The JSX output to modify.
5834
+ * @param placeholder The placeholder element to inject.
5835
+ * @returns The modified JSX output.
5836
+ */
5837
+ function injectPlaceholderElement(jsx, placeholder) {
5838
+ // For regular JSX nodes, we can append the placeholder to its children.
5839
+ if (isJSXNode(jsx)) {
5840
+ // Inline components don't always render children, so we wrap them in Fragment which does.
5841
+ if (jsx.type !== Fragment && !isQwikComponent(jsx.type)) {
5842
+ return _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null);
5843
+ }
5844
+ if (jsx.children == null) {
5845
+ jsx.children = placeholder;
5846
+ }
5847
+ else if (isArray(jsx.children)) {
5848
+ jsx.children.push(placeholder);
5849
+ }
5850
+ else {
5851
+ jsx.children = [jsx.children, placeholder];
5746
5852
  }
5853
+ return jsx;
5747
5854
  }
5748
- return true;
5749
- }
5750
- function containsWrappedSignal(data, signal) {
5751
- if (!(signal instanceof WrappedSignalImpl)) {
5752
- return false;
5855
+ // For primitives, we can't add children, so we wrap them in a fragment.
5856
+ if (isPrimitiveOrNullUndefined(jsx)) {
5857
+ return _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null);
5753
5858
  }
5754
- for (let i = 0; i < data.length; i++) {
5755
- const item = data[i];
5756
- if (item instanceof WrappedSignalImpl && areWrappedSignalsEqual(item, signal)) {
5757
- return true;
5758
- }
5859
+ // For an array of nodes, we inject the placeholder into the first element.
5860
+ if (isArray(jsx) && jsx.length > 0) {
5861
+ injectPlaceholderElement(jsx[0], placeholder);
5862
+ return jsx;
5759
5863
  }
5760
- return false;
5864
+ // For anything else we do nothing.
5865
+ return jsx;
5866
+ }
5867
+ /** @returns An empty <script> element for adding qwik metadata attributes to */
5868
+ function createPlaceholderScriptNode() {
5869
+ return new JSXNodeImpl('script', null, { hidden: '' }, null, 0, null);
5761
5870
  }
5762
5871
 
5763
5872
  function collectChildren(first, last = null) {
@@ -6033,6 +6142,22 @@ function executeTasks(vNode, container, cursorData) {
6033
6142
  }
6034
6143
  return taskPromise;
6035
6144
  }
6145
+ function setInlineComponentData(vNode, componentFn, subscriptionHost, jsxProps) {
6146
+ const props = (vNode.props ||= {});
6147
+ props[INLINE_COMPONENT_DATA_KEY] = {
6148
+ componentFn,
6149
+ subscriptionHost,
6150
+ props: jsxProps,
6151
+ };
6152
+ }
6153
+ function getInlineComponentData(vNode) {
6154
+ const props = vNode.props;
6155
+ const data = props?.[INLINE_COMPONENT_DATA_KEY] || null;
6156
+ if (data) {
6157
+ delete props[INLINE_COMPONENT_DATA_KEY];
6158
+ }
6159
+ return data;
6160
+ }
6036
6161
  function getNodeDiffPayload(vNode) {
6037
6162
  const props = vNode.props;
6038
6163
  return props?.[NODE_DIFF_DATA_KEY];
@@ -6050,7 +6175,7 @@ function setErrorPayload(vNode, error) {
6050
6175
  props[ERROR_DATA_KEY] = error;
6051
6176
  }
6052
6177
  function executeErrorWrap(vNode, journal) {
6053
- vNode.dirty &= -257 /* ChoreBits.ERROR_WRAP */;
6178
+ vNode.dirty &= -513 /* ChoreBits.ERROR_WRAP */;
6054
6179
  const err = getErrorPayload(vNode);
6055
6180
  if (!err) {
6056
6181
  return;
@@ -6071,7 +6196,7 @@ function executeErrorWrap(vNode, journal) {
6071
6196
  // avoid an infinite loop — those children are now under errored-host, not this host.
6072
6197
  // This is safe because CHILDREN is always processed before ERROR_WRAP in the walker,
6073
6198
  // so any pre-existing child work has already completed.
6074
- vNode.dirty &= -33 /* ChoreBits.CHILDREN */;
6199
+ vNode.dirty &= -65 /* ChoreBits.CHILDREN */;
6075
6200
  vNode.dirtyChildren = null;
6076
6201
  }
6077
6202
  function executeNodeDiff(vNode, container, journal, cursor) {
@@ -6104,7 +6229,19 @@ function executeComponentChore(vNode, container, journal, cursor) {
6104
6229
  return;
6105
6230
  }
6106
6231
  const props = container.getHostProp(host, ELEMENT_PROPS) || null;
6107
- const result = safeCall(() => executeComponent(container, host, host, componentQRL, props), (jsx) => {
6232
+ return executeComponentFunction(container, host, host, componentQRL, props, journal, cursor);
6233
+ }
6234
+ function executeInlineComponentChore(vNode, container, journal, cursor) {
6235
+ vNode.dirty &= -9 /* ChoreBits.INLINE_COMPONENT */;
6236
+ const host = vNode;
6237
+ const inlineComponentData = getInlineComponentData(vNode);
6238
+ if (!inlineComponentData) {
6239
+ return;
6240
+ }
6241
+ return executeComponentFunction(container, host, inlineComponentData.subscriptionHost || container.rootVNode, inlineComponentData.componentFn, inlineComponentData.props, journal, cursor);
6242
+ }
6243
+ function executeComponentFunction(container, host, subscriptionHost, componentFn, props, journal, cursor) {
6244
+ const result = safeCall(() => executeComponent(container, host, subscriptionHost, componentFn, props), (jsx) => {
6108
6245
  const styleScopedId = container.getHostProp(host, QScopedStyle);
6109
6246
  return retryOnPromise(() => vnode_diff(container, journal, jsx, host, cursor, addComponentStylePrefix(styleScopedId)));
6110
6247
  }, (err) => {
@@ -6170,7 +6307,7 @@ function setNodeProp(domVNode, journal, property, value, isConst, scopedStyleIdP
6170
6307
  * @returns Void
6171
6308
  */
6172
6309
  function executeNodeProps(vNode, journal) {
6173
- vNode.dirty &= -9 /* ChoreBits.NODE_PROPS */;
6310
+ vNode.dirty &= -17 /* ChoreBits.NODE_PROPS */;
6174
6311
  if (!(vNode.flags & 1 /* VNodeFlags.Element */)) {
6175
6312
  return;
6176
6313
  }
@@ -6201,7 +6338,7 @@ function executeNodeProps(vNode, journal) {
6201
6338
  * @returns Void
6202
6339
  */
6203
6340
  function executeCleanup(vNode, container) {
6204
- vNode.dirty &= -65 /* ChoreBits.CLEANUP */;
6341
+ vNode.dirty &= -129 /* ChoreBits.CLEANUP */;
6205
6342
  // TODO add promises to extraPromises
6206
6343
  const elementSeq = container.getHostProp(vNode, ELEMENT_SEQ);
6207
6344
  if (!elementSeq || elementSeq.length === 0) {
@@ -6228,7 +6365,7 @@ function executeCleanup(vNode, container) {
6228
6365
  * @returns Promise if computation is async, void otherwise
6229
6366
  */
6230
6367
  function executeCompute(vNode, container) {
6231
- vNode.dirty &= -17 /* ChoreBits.COMPUTE */;
6368
+ vNode.dirty &= -33 /* ChoreBits.COMPUTE */;
6232
6369
  const target = container.getHostProp(vNode, HOST_SIGNAL);
6233
6370
  if (!target) {
6234
6371
  return;
@@ -6254,7 +6391,7 @@ function executeCompute(vNode, container) {
6254
6391
  * @returns Promise if reconcile is async, void otherwise
6255
6392
  */
6256
6393
  function executeReconcile(vNode, container, journal, cursor) {
6257
- vNode.dirty &= -129 /* ChoreBits.RECONCILE */;
6394
+ vNode.dirty &= -257 /* ChoreBits.RECONCILE */;
6258
6395
  const host = vNode;
6259
6396
  const props = container.getHostProp(host, ELEMENT_PROPS) || null;
6260
6397
  if (!props) {
@@ -6428,16 +6565,9 @@ function executeAfterFlush(container, cursorData) {
6428
6565
  cursorData.afterFlushTasks = null;
6429
6566
  return;
6430
6567
  }
6431
- let visibleTaskPromise;
6432
6568
  for (let i = 0; i < visibleTasks.length; i++) {
6433
6569
  const task = visibleTasks[i];
6434
- const result = runTask(task, container, task.$el$);
6435
- if (isPromise(result)) {
6436
- visibleTaskPromise = visibleTaskPromise ? visibleTaskPromise.then(() => result) : result;
6437
- }
6438
- }
6439
- if (visibleTaskPromise) {
6440
- (cursorData.extraPromises ||= []).push(visibleTaskPromise);
6570
+ runTask(task, container, task.$el$);
6441
6571
  }
6442
6572
  cursorData.afterFlushTasks = null;
6443
6573
  }
@@ -6559,7 +6689,7 @@ function walkCursor(cursor, until) {
6559
6689
  return;
6560
6690
  }
6561
6691
  // Skip if the vNode is not dirty
6562
- if (!(currentVNode.dirty & 511 /* ChoreBits.DIRTY_MASK */)) {
6692
+ if (!(currentVNode.dirty & 1023 /* ChoreBits.DIRTY_MASK */)) {
6563
6693
  // Move to next node
6564
6694
  __EXPERIMENTAL__.suspense && clearNearestCursorBoundary(currentVNode);
6565
6695
  setCursorPosition(container, cursorData, getNextVNode(currentVNode, cursor, container));
@@ -6568,10 +6698,10 @@ function walkCursor(cursor, until) {
6568
6698
  // Skip if the vNode is deleted
6569
6699
  if (currentVNode.flags & 32 /* VNodeFlags.Deleted */) {
6570
6700
  // if deleted, run cleanup if needed
6571
- if (currentVNode.dirty & 64 /* ChoreBits.CLEANUP */) {
6701
+ if (currentVNode.dirty & 128 /* ChoreBits.CLEANUP */) {
6572
6702
  executeCleanup(currentVNode, container);
6573
6703
  }
6574
- else if (currentVNode.dirty & 32 /* ChoreBits.CHILDREN */) {
6704
+ else if (currentVNode.dirty & 64 /* ChoreBits.CHILDREN */) {
6575
6705
  const next = tryDescendDirtyChildren(container, cursorData, currentVNode, cursor);
6576
6706
  if (next !== null) {
6577
6707
  currentVNode = next;
@@ -6579,7 +6709,7 @@ function walkCursor(cursor, until) {
6579
6709
  }
6580
6710
  }
6581
6711
  // Clear dirty bits and move to next node
6582
- currentVNode.dirty &= -512 /* ChoreBits.DIRTY_MASK */;
6712
+ currentVNode.dirty &= -1024 /* ChoreBits.DIRTY_MASK */;
6583
6713
  setCursorPosition(container, cursorData, getNextVNode(currentVNode, cursor, container));
6584
6714
  continue;
6585
6715
  }
@@ -6595,23 +6725,26 @@ function walkCursor(cursor, until) {
6595
6725
  else if (currentVNode.dirty & 4 /* ChoreBits.COMPONENT */) {
6596
6726
  result = executeComponentChore(currentVNode, container, journal, cursor);
6597
6727
  }
6598
- else if (currentVNode.dirty & 128 /* ChoreBits.RECONCILE */) {
6728
+ else if (currentVNode.dirty & 8 /* ChoreBits.INLINE_COMPONENT */) {
6729
+ result = executeInlineComponentChore(currentVNode, container, journal, cursor);
6730
+ }
6731
+ else if (currentVNode.dirty & 256 /* ChoreBits.RECONCILE */) {
6599
6732
  result = executeReconcile(currentVNode, container, journal, cursor);
6600
6733
  }
6601
- else if (currentVNode.dirty & 8 /* ChoreBits.NODE_PROPS */) {
6734
+ else if (currentVNode.dirty & 16 /* ChoreBits.NODE_PROPS */) {
6602
6735
  executeNodeProps(currentVNode, journal);
6603
6736
  }
6604
- else if (currentVNode.dirty & 16 /* ChoreBits.COMPUTE */) {
6737
+ else if (currentVNode.dirty & 32 /* ChoreBits.COMPUTE */) {
6605
6738
  result = executeCompute(currentVNode, container);
6606
6739
  }
6607
- else if (currentVNode.dirty & 32 /* ChoreBits.CHILDREN */) {
6740
+ else if (currentVNode.dirty & 64 /* ChoreBits.CHILDREN */) {
6608
6741
  const next = tryDescendDirtyChildren(container, cursorData, currentVNode, cursor);
6609
6742
  if (next !== null) {
6610
6743
  currentVNode = next;
6611
6744
  continue;
6612
6745
  }
6613
6746
  }
6614
- else if (currentVNode.dirty & 256 /* ChoreBits.ERROR_WRAP */) {
6747
+ else if (currentVNode.dirty & 512 /* ChoreBits.ERROR_WRAP */) {
6615
6748
  // Must run after CHILDREN so that all descendant chores (e.g. signal text
6616
6749
  // NODE_DIFF updates) are flushed before we reparent children into the
6617
6750
  // errored-host wrapper element.
@@ -6625,14 +6758,20 @@ function walkCursor(cursor, until) {
6625
6758
  if (result && isPromise(result)) {
6626
6759
  addCursorBoundary(cursorData, currentVNode);
6627
6760
  // Store promise on cursor and pause
6628
- cursorData.promise = result;
6761
+ const blockingPromise = result;
6762
+ cursorData.promise = blockingPromise;
6629
6763
  pauseCursor(cursor, container);
6630
6764
  const host = currentVNode;
6631
- result
6765
+ blockingPromise
6632
6766
  .catch((error) => {
6633
- container.handleError(error, host);
6767
+ if (cursorData.promise === blockingPromise) {
6768
+ container.handleError(error, host);
6769
+ }
6634
6770
  })
6635
6771
  .finally(() => {
6772
+ if (cursorData.promise !== blockingPromise) {
6773
+ return;
6774
+ }
6636
6775
  cursorData.promise = null;
6637
6776
  resumeCursor(cursor, container);
6638
6777
  triggerCursors();
@@ -6645,11 +6784,11 @@ function walkCursor(cursor, until) {
6645
6784
  }
6646
6785
  }
6647
6786
  isDev &&
6648
- assertFalse(!!(cursor.dirty & 511 /* ChoreBits.DIRTY_MASK */ && !cursorData.position), 'Cursor is still dirty and position is not set after walking');
6787
+ assertFalse(!!(cursor.dirty & 1023 /* ChoreBits.DIRTY_MASK */ && !cursorData.position), 'Cursor is still dirty and position is not set after walking');
6649
6788
  finishWalk(container, cursor, cursorData, isRunningOnServer);
6650
6789
  }
6651
6790
  function finishWalk(container, cursor, cursorData, isServer) {
6652
- if (!(cursor.dirty & 511 /* ChoreBits.DIRTY_MASK */)) {
6791
+ if (!(cursor.dirty & 1023 /* ChoreBits.DIRTY_MASK */)) {
6653
6792
  removeCursorFromQueue(cursor, container);
6654
6793
  if (!isServer) {
6655
6794
  executeFlushPhase(cursor, container);
@@ -6674,7 +6813,7 @@ function resolveCursor(container) {
6674
6813
  function tryDescendDirtyChildren(container, cursorData, currentVNode, cursor) {
6675
6814
  const dirtyChildren = currentVNode.dirtyChildren;
6676
6815
  if (!dirtyChildren || dirtyChildren.length === 0) {
6677
- currentVNode.dirty &= -33 /* ChoreBits.CHILDREN */;
6816
+ currentVNode.dirty &= -65 /* ChoreBits.CHILDREN */;
6678
6817
  clearNearestCursorBoundary(currentVNode);
6679
6818
  return null;
6680
6819
  }
@@ -6706,21 +6845,21 @@ function partitionDirtyChildren(dirtyChildren, parent) {
6706
6845
  /** @returns Next vNode to process, or null if traversal is complete */
6707
6846
  function getNextVNode(vNode, cursor, container) {
6708
6847
  if (vNode === cursor) {
6709
- if (cursor.dirty & 511 /* ChoreBits.DIRTY_MASK */) {
6848
+ if (cursor.dirty & 1023 /* ChoreBits.DIRTY_MASK */) {
6710
6849
  return cursor;
6711
6850
  }
6712
6851
  return null;
6713
6852
  }
6714
6853
  // Prefer slotParent (logical owner) for Projections, fall back to parent
6715
6854
  let parent = null;
6716
- if (vNode.slotParent && vNode.slotParent.dirty & 32 /* ChoreBits.CHILDREN */) {
6855
+ if (vNode.slotParent && vNode.slotParent.dirty & 64 /* ChoreBits.CHILDREN */) {
6717
6856
  parent = vNode.slotParent;
6718
6857
  }
6719
- else if (vNode.parent && vNode.parent.dirty & 32 /* ChoreBits.CHILDREN */) {
6858
+ else if (vNode.parent && vNode.parent.dirty & 64 /* ChoreBits.CHILDREN */) {
6720
6859
  parent = vNode.parent;
6721
6860
  }
6722
6861
  if (!parent) {
6723
- if (cursor.dirty & 511 /* ChoreBits.DIRTY_MASK */) {
6862
+ if (cursor.dirty & 1023 /* ChoreBits.DIRTY_MASK */) {
6724
6863
  return cursor;
6725
6864
  }
6726
6865
  return null;
@@ -6731,7 +6870,7 @@ function getNextVNode(vNode, cursor, container) {
6731
6870
  let count = len;
6732
6871
  while (count-- > 0) {
6733
6872
  const nextVNode = dirtyChildren[index];
6734
- if (nextVNode.dirty & 511 /* ChoreBits.DIRTY_MASK */) {
6873
+ if (nextVNode.dirty & 1023 /* ChoreBits.DIRTY_MASK */) {
6735
6874
  if (container && splitCursorBoundary(container, nextVNode)) {
6736
6875
  index++;
6737
6876
  if (index === len) {
@@ -6748,7 +6887,7 @@ function getNextVNode(vNode, cursor, container) {
6748
6887
  }
6749
6888
  }
6750
6889
  // all array items checked, children are no longer dirty
6751
- parent.dirty &= -33 /* ChoreBits.CHILDREN */;
6890
+ parent.dirty &= -65 /* ChoreBits.CHILDREN */;
6752
6891
  parent.dirtyChildren = null;
6753
6892
  parent.nextDirtyChildIndex = 0;
6754
6893
  __EXPERIMENTAL__.suspense && clearNearestCursorBoundary(parent);
@@ -6824,13 +6963,13 @@ function findCursor(vNode) {
6824
6963
  /** @internal */
6825
6964
  function _executeSsrChores(container, ssrNode) {
6826
6965
  if (!(ssrNode.flags & 1 /* SsrNodeFlags.Updatable */)) {
6827
- if (ssrNode.dirty & 8 /* ChoreBits.NODE_PROPS */) {
6966
+ if (ssrNode.dirty & 16 /* ChoreBits.NODE_PROPS */) {
6828
6967
  executeNodePropChore(container, ssrNode);
6829
6968
  }
6830
- if (ssrNode.dirty & 16 /* ChoreBits.COMPUTE */) {
6969
+ if (ssrNode.dirty & 32 /* ChoreBits.COMPUTE */) {
6831
6970
  executeCompute(ssrNode, container);
6832
6971
  }
6833
- if (isDev && ssrNode.dirty & 511 /* ChoreBits.DIRTY_MASK */) {
6972
+ if (isDev && ssrNode.dirty & 1023 /* ChoreBits.DIRTY_MASK */) {
6834
6973
  // We are running on the server.
6835
6974
  // On server we can't schedule task for a different host!
6836
6975
  // Server is SSR, and therefore scheduling for anything but the current host
@@ -6845,7 +6984,7 @@ function _executeSsrChores(container, ssrNode) {
6845
6984
  This is often caused by modifying a signal in an already rendered component during SSR.`;
6846
6985
  logWarn(warningMessage);
6847
6986
  }
6848
- ssrNode.dirty &= -512 /* ChoreBits.DIRTY_MASK */;
6987
+ ssrNode.dirty &= -1024 /* ChoreBits.DIRTY_MASK */;
6849
6988
  return;
6850
6989
  }
6851
6990
  let promise = null;
@@ -6855,7 +6994,7 @@ function _executeSsrChores(container, ssrNode) {
6855
6994
  promise = result;
6856
6995
  }
6857
6996
  }
6858
- if (ssrNode.dirty & 128 /* ChoreBits.RECONCILE */) {
6997
+ if (ssrNode.dirty & 256 /* ChoreBits.RECONCILE */) {
6859
6998
  const result = executeReconcileChore(container, ssrNode);
6860
6999
  promise = promise ? promise.then(() => result) : result;
6861
7000
  }
@@ -6865,7 +7004,7 @@ function _executeSsrChores(container, ssrNode) {
6865
7004
  // it after $waitOn$ completes and re-execute the component function.
6866
7005
  // executeComponent will clear the bit after re-executing.
6867
7006
  // Clear all dirty bits EXCEPT COMPONENT
6868
- ssrNode.dirty &= -508;
7007
+ ssrNode.dirty &= -1020;
6869
7008
  if (promise) {
6870
7009
  return promise;
6871
7010
  }
@@ -6893,7 +7032,7 @@ function executeTasksChore(container, ssrNode) {
6893
7032
  return promise;
6894
7033
  }
6895
7034
  function executeNodePropChore(container, ssrNode) {
6896
- ssrNode.dirty &= -9 /* ChoreBits.NODE_PROPS */;
7035
+ ssrNode.dirty &= -17 /* ChoreBits.NODE_PROPS */;
6897
7036
  const allPropData = ssrNode.getProp(NODE_PROPS_DATA_KEY);
6898
7037
  if (!allPropData || allPropData.size === 0) {
6899
7038
  return;
@@ -6909,7 +7048,7 @@ function executeNodePropChore(container, ssrNode) {
6909
7048
  }
6910
7049
  }
6911
7050
  async function executeReconcileChore(container, ssrNode) {
6912
- ssrNode.dirty &= -129 /* ChoreBits.RECONCILE */;
7051
+ ssrNode.dirty &= -257 /* ChoreBits.RECONCILE */;
6913
7052
  const host = ssrNode;
6914
7053
  const props = container.getHostProp(host, ELEMENT_PROPS) || null;
6915
7054
  if (!props) {
@@ -6942,7 +7081,7 @@ function propagatePath(target) {
6942
7081
  for (let i = 0; i < reusablePath.length; i++) {
6943
7082
  const child = reusablePath[i];
6944
7083
  const parent = reusablePath[i + 1] || target;
6945
- parent.dirty |= 32 /* ChoreBits.CHILDREN */;
7084
+ parent.dirty |= 64 /* ChoreBits.CHILDREN */;
6946
7085
  parent.dirtyChildren ||= [];
6947
7086
  if (!parent.dirtyChildren.includes(child)) {
6948
7087
  parent.dirtyChildren.push(child);
@@ -6958,7 +7097,7 @@ function propagateToCursorRoot(container, vNode, cursorRoot) {
6958
7097
  let cursorBoundary = getOwnCursorBoundary(container, vNode);
6959
7098
  let current = vNode.slotParent || vNode.parent;
6960
7099
  while (current) {
6961
- const isDirty = current.dirty & 511 /* ChoreBits.DIRTY_MASK */;
7100
+ const isDirty = current.dirty & 1023 /* ChoreBits.DIRTY_MASK */;
6962
7101
  const currentIsCursor = isCursor(current);
6963
7102
  if (__EXPERIMENTAL__.suspense) {
6964
7103
  cursorBoundary ||=
@@ -7052,9 +7191,9 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
7052
7191
  }
7053
7192
  return;
7054
7193
  }
7055
- const isRealDirty = bits & 511 /* ChoreBits.DIRTY_MASK */;
7194
+ const isRealDirty = bits & 1023 /* ChoreBits.DIRTY_MASK */;
7056
7195
  // If already dirty, no need to propagate again
7057
- if ((isRealDirty ? prevDirty & 511 /* ChoreBits.DIRTY_MASK */ : prevDirty) || vNode === cursorRoot) {
7196
+ if ((isRealDirty ? prevDirty & 1023 /* ChoreBits.DIRTY_MASK */ : prevDirty) || vNode === cursorRoot) {
7058
7197
  return;
7059
7198
  }
7060
7199
  const parent = vNode.slotParent || vNode.parent;
@@ -7064,12 +7203,12 @@ function markVNodeDirty(container, vNode, bits, cursorRoot = null) {
7064
7203
  return;
7065
7204
  }
7066
7205
  // We must attach to a cursor subtree if it exists
7067
- if (parent && parent.dirty & 511 /* ChoreBits.DIRTY_MASK */) {
7206
+ if (parent && parent.dirty & 1023 /* ChoreBits.DIRTY_MASK */) {
7068
7207
  // Dirty parent case: this vnode joins an already scheduled subtree, so inherit the parent's
7069
7208
  // nearest boundary unless this vnode owns a boundary itself.
7070
7209
  setNearestCursorBoundary(vNode, getOwnCursorBoundary(container, vNode) || getNearestCursorBoundary(container, parent));
7071
7210
  if (isRealDirty) {
7072
- parent.dirty |= 32 /* ChoreBits.CHILDREN */;
7211
+ parent.dirty |= 64 /* ChoreBits.CHILDREN */;
7073
7212
  }
7074
7213
  parent.dirtyChildren ||= [];
7075
7214
  if (!parent.dirtyChildren.includes(vNode)) {
@@ -7501,14 +7640,10 @@ function vnode_walkVNode(vNode, callback) {
7501
7640
  let vCursor = vNode;
7502
7641
  // Depth first traversal
7503
7642
  if (vnode_isTextVNode(vNode)) {
7504
- callback?.(vNode, null);
7505
7643
  return;
7506
7644
  }
7507
7645
  let vParent = null;
7508
7646
  do {
7509
- if (callback?.(vCursor, vParent)) {
7510
- return;
7511
- }
7512
7647
  const vFirstChild = vnode_getFirstChild(vCursor);
7513
7648
  if (vFirstChild) {
7514
7649
  vCursor = vFirstChild;
@@ -7700,38 +7835,53 @@ const vnode_locate = (rootVNode, id) => {
7700
7835
  ensureElementVNode(rootVNode);
7701
7836
  let vNode = rootVNode;
7702
7837
  const containerElement = rootVNode.node;
7703
- const { qVNodeRefs } = containerElement;
7838
+ const qVNodeRefs = containerElement.qVNodeRefs;
7704
7839
  let elementOffset = -1;
7705
7840
  let refElement;
7841
+ const localId = typeof id === 'string' ? id : '';
7706
7842
  if (typeof id === 'string') {
7707
7843
  isDev && assertDefined(qVNodeRefs, 'Missing qVNodeRefs.');
7708
- elementOffset = parseInt(id);
7844
+ elementOffset = parseInt(localId);
7709
7845
  refElement = qVNodeRefs.get(elementOffset);
7710
7846
  }
7711
7847
  else {
7712
7848
  refElement = id;
7713
- const vNode = refElement.vNode;
7714
- if (vNode) {
7849
+ const qElement = refElement;
7850
+ const cachedVNode = qElement.vNode;
7851
+ if (cachedVNode) {
7852
+ return cachedVNode;
7853
+ }
7854
+ if (__EXPERIMENTAL__.suspense && qElement._qSegment) {
7855
+ vNode = vnode_newUnMaterializedElement(refElement);
7856
+ vnode_ensureElementKeyInflated(vNode);
7857
+ qElement.vNode = vNode;
7715
7858
  return vNode;
7716
7859
  }
7717
7860
  }
7718
- isDev && assertDefined(refElement, 'Missing refElement.');
7861
+ isDev &&
7862
+ assertDefined(refElement, 'Missing refElement ' + (typeof id === 'string' ? `for id ${id}` : ''));
7719
7863
  if (!vnode_isVNode(refElement)) {
7720
7864
  isDev &&
7721
7865
  assertTrue(containerElement.contains(refElement), `Couldn't find the element inside the container while locating the VNode.`);
7722
- // We need to find the vnode.
7723
- let parent = refElement;
7724
- const elementPath = [refElement];
7725
- while (parent && parent !== containerElement && !parent.vNode) {
7726
- parent = parent.parentElement;
7727
- elementPath.push(parent);
7866
+ if (__EXPERIMENTAL__.suspense && refElement._qSegment) {
7867
+ vNode = refElement.vNode || vnode_newUnMaterializedElement(refElement);
7868
+ vnode_ensureElementKeyInflated(vNode);
7728
7869
  }
7729
- if (parent.vNode) {
7730
- vNode = parent.vNode;
7731
- }
7732
- // Start at rootVNode and follow the `elementPath` to find the vnode.
7733
- for (let i = elementPath.length - 2; i >= 0; i--) {
7734
- vNode = vnode_getVNodeForChildNode(vNode, elementPath[i]);
7870
+ else {
7871
+ // We need to find the vnode.
7872
+ let parent = refElement;
7873
+ const elementPath = [refElement];
7874
+ while (parent && parent !== containerElement && !parent.vNode) {
7875
+ parent = parent.parentElement;
7876
+ elementPath.push(parent);
7877
+ }
7878
+ if (parent.vNode) {
7879
+ vNode = parent.vNode;
7880
+ }
7881
+ // Start at rootVNode and follow the `elementPath` to find the vnode.
7882
+ for (let i = elementPath.length - 2; i >= 0; i--) {
7883
+ vNode = vnode_getVNodeForChildNode(vNode, elementPath[i]);
7884
+ }
7735
7885
  }
7736
7886
  if (elementOffset != -1) {
7737
7887
  refElement.vNode = vNode;
@@ -7743,11 +7893,11 @@ const vnode_locate = (rootVNode, id) => {
7743
7893
  }
7744
7894
  if (typeof id === 'string') {
7745
7895
  // process virtual node search.
7746
- const idLength = id.length;
7747
- let idx = indexOfAlphanumeric(id, idLength);
7896
+ const idLength = localId.length;
7897
+ let idx = indexOfAlphanumeric(localId, idLength);
7748
7898
  let childIdx = 0;
7749
7899
  while (idx < idLength) {
7750
- const ch = id.charCodeAt(idx);
7900
+ const ch = localId.charCodeAt(idx);
7751
7901
  childIdx *= 26 /* a-z */;
7752
7902
  if (ch >= 97 /* a */) {
7753
7903
  // is lowercase
@@ -7811,9 +7961,10 @@ const vnode_getVNodeForChildNode = (vNode, childElement) => {
7811
7961
  return child;
7812
7962
  };
7813
7963
  const indexOfAlphanumeric = (id, length) => {
7814
- let idx = 0;
7964
+ let idx = id.charCodeAt(0) === 45 /* - */ ? 1 : 0;
7815
7965
  while (idx < length) {
7816
- if (id.charCodeAt(idx) <= 57 /* 9 */) {
7966
+ const ch = id.charCodeAt(idx);
7967
+ if (ch >= 48 /* 0 */ && ch <= 57 /* 9 */) {
7817
7968
  idx++;
7818
7969
  }
7819
7970
  else {
@@ -8211,6 +8362,12 @@ const vnode_getFirstChild = (vnode) => {
8211
8362
  return null;
8212
8363
  }
8213
8364
  let vFirstChild = vnode.firstChild;
8365
+ if (__EXPERIMENTAL__.suspense &&
8366
+ vFirstChild === undefined &&
8367
+ vnode_isElementVNode(vnode) &&
8368
+ hasOnlySuspensePlaceholder(vnode.node)) {
8369
+ return null;
8370
+ }
8214
8371
  if (vFirstChild === undefined) {
8215
8372
  vFirstChild = ensureMaterialized(vnode);
8216
8373
  }
@@ -8232,6 +8389,7 @@ const splitVNodeData = (vNodeData) => {
8232
8389
  };
8233
8390
  const materialize = (vNode, element, firstChild, vNodeData) => {
8234
8391
  vnode_ensureElementKeyInflated(vNode);
8392
+ __EXPERIMENTAL__.suspense ? element._qSegment || null : null;
8235
8393
  if (vNodeData) {
8236
8394
  if (vNodeData.charCodeAt(0) === VNodeDataChar.SEPARATOR &&
8237
8395
  vNodeData.charCodeAt(1) === VNodeDataChar.SEPARATOR) {
@@ -8259,7 +8417,7 @@ const materialize = (vNode, element, firstChild, vNodeData) => {
8259
8417
  }
8260
8418
  else {
8261
8419
  // Materialize DOM element from HTML only
8262
- return materializeFromDOM(vNode, firstChild);
8420
+ return materializeFromDOM(vNode, firstChild, undefined);
8263
8421
  }
8264
8422
  };
8265
8423
  const ensureMaterialized = (vnode) => {
@@ -8384,13 +8542,13 @@ const fastFirstChild = (node) => {
8384
8542
  node = node && _fastFirstChild.call(node);
8385
8543
  // Handle q:ignore as first child (e.g. qwikify$ Host with reactify$ projections).
8386
8544
  // Navigate depth-first to the first q:container-island and return its first element.
8387
- if (node &&
8388
- fastNodeType(node) === /* Node.COMMENT_NODE */ 8 &&
8389
- node.nodeValue?.startsWith(QIgnore)) {
8545
+ if (node && fastNodeType(node) === /* Node.COMMENT_NODE */ 8) {
8390
8546
  if (!_fastNextSibling) {
8391
8547
  _fastNextSibling = fastGetter(node, 'nextSibling');
8392
8548
  }
8393
- return getNodeAfterCommentNode(node, QContainerIsland, _fastNextSibling, _fastFirstChild);
8549
+ if (node.nodeValue?.startsWith(QIgnore)) {
8550
+ return getNodeAfterCommentNode(node, QContainerIsland, _fastNextSibling, _fastFirstChild);
8551
+ }
8394
8552
  }
8395
8553
  while (node && !fastIsTextOrElement(node)) {
8396
8554
  node = fastNextSibling(node);
@@ -8400,8 +8558,9 @@ const fastFirstChild = (node) => {
8400
8558
  const fastNamespaceURI = createFastGetter('namespaceURI');
8401
8559
  const fastNodeName = createFastGetter('nodeName');
8402
8560
  const fastOwnerDocument = createFastGetter('ownerDocument');
8403
- const materializeFromDOM = (vParent, firstChild, vData) => {
8561
+ const materializeFromDOM = (vParent, firstChild, vData, segmentId) => {
8404
8562
  let vFirstChild = null;
8563
+ let idx = 0;
8405
8564
  const skipElements = () => {
8406
8565
  while (isElement(child) && shouldSkipElement(child)) {
8407
8566
  child = fastNextSibling(child);
@@ -8422,6 +8581,8 @@ const materializeFromDOM = (vParent, firstChild, vData) => {
8422
8581
  vnode_ensureElementKeyInflated(vNextChild);
8423
8582
  }
8424
8583
  if (vNextChild) {
8584
+ vNextChild.flags = (vNextChild.flags & 4095 /* VNodeFlagsIndex.mask */) | (idx << 12 /* VNodeFlagsIndex.shift */);
8585
+ idx++;
8425
8586
  vNextChild.parent = vParent;
8426
8587
  vChild && (vChild.nextSibling = vNextChild);
8427
8588
  vNextChild.previousSibling = vChild;
@@ -8447,7 +8608,7 @@ const materializeFromDOM = (vParent, firstChild, vData) => {
8447
8608
  container = getDomContainer(vParent.node);
8448
8609
  }
8449
8610
  const id = consumeValue();
8450
- container.$setRawState$(parseInt(id), vParent);
8611
+ container.$setRawState$(parseInt(id, 10), vParent);
8451
8612
  isDev && vnode_setProp(vParent, ELEMENT_ID, id);
8452
8613
  }
8453
8614
  else if (peek() === VNodeDataChar.BACK_REFS) {
@@ -8608,6 +8769,9 @@ function vnode_toString(depth = 20, offset = '', materialize = false, siblings =
8608
8769
  attrs.push(' ' + key + '=' + qwikDebugToString(value));
8609
8770
  }
8610
8771
  }
8772
+ if (vnode.slotParent) {
8773
+ attrs.push(' slotParent=(C)');
8774
+ }
8611
8775
  }
8612
8776
  const name = (colorize ? NAME_COL_PREFIX : '') +
8613
8777
  (VirtualTypeName[vnode_getProp(vnode, DEBUG_TYPE, null) || "V" /* VirtualType.Virtual */] ||
@@ -8678,8 +8842,19 @@ function shouldSkipElement(element) {
8678
8842
  (element.nodeName === 'STYLE' &&
8679
8843
  (element.hasAttribute(QScopedStyle) || element.hasAttribute(QStyle))));
8680
8844
  }
8845
+ function hasOnlySuspensePlaceholder(element) {
8846
+ const segmentId = element.getAttribute(QSuspenseResultParent);
8847
+ if (segmentId === null) {
8848
+ return false;
8849
+ }
8850
+ const firstChild = fastFirstChild(element);
8851
+ return (isElement(firstChild) &&
8852
+ firstChild.localName === 'template' &&
8853
+ firstChild.getAttribute(QSuspenseResolved) === segmentId &&
8854
+ fastNextSibling(firstChild) === null);
8855
+ }
8681
8856
  const stack = [];
8682
- function materializeFromVNodeData(vParent, vData, element, child) {
8857
+ function materializeFromVNodeData(vParent, vData, element, child, segmentId) {
8683
8858
  let idx = 0;
8684
8859
  let vFirst = null;
8685
8860
  let vLast = null;
@@ -8748,7 +8923,7 @@ function materializeFromVNodeData(vParent, vData, element, child) {
8748
8923
  container = getDomContainer(element);
8749
8924
  }
8750
8925
  const id = consumeValue();
8751
- container.$setRawState$(parseInt(id), vParent);
8926
+ container.$setRawState$(parseInt(id, 10), vParent);
8752
8927
  isDev && vnode_setProp(vParent, ELEMENT_ID, id);
8753
8928
  }
8754
8929
  else if (peek() === VNodeDataChar.PROPS) {
@@ -8759,7 +8934,7 @@ function materializeFromVNodeData(vParent, vData, element, child) {
8759
8934
  let value;
8760
8935
  if (isEscapedValue) {
8761
8936
  consume();
8762
- value = decodeURI(decodeVNodeDataString(consumeValue()));
8937
+ value = decodeURIComponent(decodeVNodeDataString(consumeValue()));
8763
8938
  consume();
8764
8939
  }
8765
8940
  else {
@@ -8798,8 +8973,19 @@ function materializeFromVNodeData(vParent, vData, element, child) {
8798
8973
  }
8799
8974
  else if (peek() === VNodeDataChar.SEPARATOR) {
8800
8975
  // Custom attribute: |key|value
8801
- const key = decodeVNodeDataString(consumeValue());
8802
- const value = decodeVNodeDataString(consumeValue());
8976
+ const keyValue = consumeValue();
8977
+ const key = decodeVNodeDataString(keyValue);
8978
+ const valueSeparatorIdx = nextToConsumeIdx + keyValue.length + 1;
8979
+ const isEscapedValue = getChar(valueSeparatorIdx + 1) === VNodeDataChar.SEPARATOR;
8980
+ let value;
8981
+ if (isEscapedValue) {
8982
+ consume();
8983
+ value = decodeURI(decodeVNodeDataString(consumeValue()));
8984
+ consume();
8985
+ }
8986
+ else {
8987
+ value = decodeVNodeDataString(consumeValue());
8988
+ }
8803
8989
  vnode_setProp(vParent, key, value);
8804
8990
  }
8805
8991
  else if (peek() === VNodeDataChar.CLOSE) {
@@ -9127,6 +9313,15 @@ const addQrlToSerializationCtx = (effectSubscriber, container) => {
9127
9313
  }
9128
9314
  }
9129
9315
  };
9316
+ const getEffectSerializationContainer = (renderContainer, ownerContainer) => {
9317
+ if (renderContainer &&
9318
+ (!ownerContainer ||
9319
+ renderContainer === ownerContainer ||
9320
+ isOutOfOrderSegmentContainer(renderContainer))) {
9321
+ return renderContainer;
9322
+ }
9323
+ return ownerContainer;
9324
+ };
9130
9325
  const scheduleEffects = (container, signal, effects) => {
9131
9326
  const isRunningOnBrowser = qTest ? !isServerPlatform() : isBrowser$1;
9132
9327
  if (effects) {
@@ -9171,7 +9366,7 @@ const scheduleEffects = (container, signal, effects) => {
9171
9366
  }
9172
9367
  data.set(property, payload);
9173
9368
  }
9174
- markVNodeDirty(container, consumer, 8 /* ChoreBits.NODE_PROPS */);
9369
+ markVNodeDirty(container, consumer, 16 /* ChoreBits.NODE_PROPS */);
9175
9370
  }
9176
9371
  }
9177
9372
  };
@@ -9413,11 +9608,11 @@ class StoreHandler {
9413
9608
  }
9414
9609
  else {
9415
9610
  isDev &&
9416
- assertTrue(!ctx.$container$ || ctx.$container$ === this.$container$, 'Do not use signals across containers');
9611
+ assertTrue(!ctx.$container$ || isSameContainer(ctx.$container$, this.$container$), 'Do not use signals across containers');
9417
9612
  }
9418
9613
  const effectSubscriber = ctx.$effectSubscriber$;
9419
9614
  if (effectSubscriber) {
9420
- addStoreEffect(target, Array.isArray(target) ? STORE_ALL_PROPS : prop, this, effectSubscriber);
9615
+ addStoreEffect(target, Array.isArray(target) ? STORE_ALL_PROPS : prop, this, effectSubscriber, ctx.$container$);
9421
9616
  }
9422
9617
  }
9423
9618
  if (prop === 'toString' && value === Object.prototype.toString) {
@@ -9471,7 +9666,7 @@ class StoreHandler {
9471
9666
  if (ctx) {
9472
9667
  const effectSubscriber = ctx.$effectSubscriber$;
9473
9668
  if (effectSubscriber) {
9474
- addStoreEffect(target, Array.isArray(target) ? STORE_ALL_PROPS : prop, this, effectSubscriber);
9669
+ addStoreEffect(target, Array.isArray(target) ? STORE_ALL_PROPS : prop, this, effectSubscriber, ctx.$container$);
9475
9670
  }
9476
9671
  }
9477
9672
  }
@@ -9481,7 +9676,7 @@ class StoreHandler {
9481
9676
  const ctx = tryGetInvokeContext();
9482
9677
  const effectSubscriber = ctx?.$effectSubscriber$;
9483
9678
  if (effectSubscriber) {
9484
- addStoreEffect(target, STORE_ALL_PROPS, this, effectSubscriber);
9679
+ addStoreEffect(target, STORE_ALL_PROPS, this, effectSubscriber, ctx.$container$);
9485
9680
  }
9486
9681
  return Reflect.ownKeys(target);
9487
9682
  }
@@ -9499,7 +9694,7 @@ class StoreHandler {
9499
9694
  };
9500
9695
  }
9501
9696
  }
9502
- function addStoreEffect(target, prop, store, effectSubscription) {
9697
+ function addStoreEffect(target, prop, store, effectSubscription, renderContainer) {
9503
9698
  const effectsMap = (store.$effects$ ||= new Map());
9504
9699
  let effects = effectsMap.get(prop);
9505
9700
  if (!effects) {
@@ -9509,14 +9704,21 @@ function addStoreEffect(target, prop, store, effectSubscription) {
9509
9704
  // Let's make sure that we have a reference to this effect.
9510
9705
  // Adding reference is essentially adding a subscription, so if the signal
9511
9706
  // changes we know who to notify.
9707
+ const isOnServer = qTest ? isServerPlatform() : isServer;
9708
+ const shouldRecordExternalRootEffect = __EXPERIMENTAL__.suspense && store instanceof StoreHandler && isOnServer;
9512
9709
  ensureContainsSubscription(effects, effectSubscription);
9513
9710
  // But when effect is scheduled in needs to be able to know which signals
9514
9711
  // to unsubscribe from. So we need to store the reference from the effect back
9515
9712
  // to this signal.
9516
9713
  ensureContainsBackRef(effectSubscription, target);
9517
- // TODO is this needed with the preloader?
9518
- (qTest ? !isDomContainer(store.$container$) : isServer) &&
9519
- addQrlToSerializationCtx(effectSubscription, store.$container$);
9714
+ if (isOnServer) {
9715
+ const serializationContainer = getEffectSerializationContainer(renderContainer, store.$container$);
9716
+ if (shouldRecordExternalRootEffect) {
9717
+ serializationContainer?.$recordExternalRootEffect$?.(target, effectSubscription, prop, effectsMap);
9718
+ }
9719
+ // TODO is this needed with the preloader?
9720
+ addQrlToSerializationCtx(effectSubscription, serializationContainer);
9721
+ }
9520
9722
  }
9521
9723
  function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
9522
9724
  target[prop] = value;
@@ -9637,6 +9839,7 @@ const _typeIdNames = [
9637
9839
  'PropsProxy',
9638
9840
  'SubscriptionData',
9639
9841
  'EffectSubscription',
9842
+ 'SubscriptionPatch',
9640
9843
  ];
9641
9844
 
9642
9845
  function qrlToString(serializationContext, qrl, raw) {
@@ -9697,6 +9900,23 @@ function qrlToString(serializationContext, qrl, raw) {
9697
9900
  }
9698
9901
  return qrlStringInline;
9699
9902
  }
9903
+ /** @internal */
9904
+ function qrlToChunks(serializationContext, qrl) {
9905
+ const [chunk, symbol, captures] = qrlToString(serializationContext, qrl, true);
9906
+ const prefix = `${chunk}#${symbol}`;
9907
+ if (!captures) {
9908
+ return prefix;
9909
+ }
9910
+ const chunks = [prefix, '#'];
9911
+ const captureIds = captures.split(' ');
9912
+ for (let i = 0; i < captureIds.length; i++) {
9913
+ if (i > 0) {
9914
+ chunks.push(' ');
9915
+ }
9916
+ chunks.push(Number(captureIds[i]));
9917
+ }
9918
+ return chunks;
9919
+ }
9700
9920
  function createQRLWithBackChannel(chunk, symbol, captures, container) {
9701
9921
  let qrlImporter = null;
9702
9922
  if (isDev && chunk === QRL_RUNTIME_CHUNK) {
@@ -9716,6 +9936,16 @@ function parseQRL(qrl, container) {
9716
9936
  }
9717
9937
  const QRL_RUNTIME_CHUNK = 'mock-chunk';
9718
9938
 
9939
+ /** @internal */
9940
+ class SubscriptionPatch {
9941
+ rootId;
9942
+ subscriptions;
9943
+ constructor(rootId = 0, subscriptions = new Set()) {
9944
+ this.rootId = rootId;
9945
+ this.subscriptions = subscriptions;
9946
+ }
9947
+ }
9948
+
9719
9949
  /**
9720
9950
  * Format:
9721
9951
  *
@@ -9734,6 +9964,7 @@ class Serializer {
9734
9964
  $s11nWeakRefs$ = new Map();
9735
9965
  $parent$;
9736
9966
  $qrlMap$ = new Map();
9967
+ $streamedRootLimit$ = 0;
9737
9968
  $writer$;
9738
9969
  /** We need to determine this at runtime because polyfills may not be loaded a module load time */
9739
9970
  $hasTemporal$ = typeof Temporal !== 'undefined';
@@ -9742,7 +9973,59 @@ class Serializer {
9742
9973
  this.$writer$ = $serializationContext$.$writer$;
9743
9974
  }
9744
9975
  async serialize() {
9745
- await this.outputRoots();
9976
+ const previousStreamedRootLimit = this.$streamedRootLimit$;
9977
+ this.$streamedRootLimit$ = 0;
9978
+ try {
9979
+ await this.outputRoots();
9980
+ }
9981
+ finally {
9982
+ this.$streamedRootLimit$ = previousStreamedRootLimit;
9983
+ }
9984
+ }
9985
+ async serializePatch(rootStart, rootIds, extraRootId, streamedRootLimit = rootStart) {
9986
+ const previousStreamedRootLimit = this.$streamedRootLimit$;
9987
+ this.$streamedRootLimit$ = streamedRootLimit;
9988
+ this.$writer$.write(BRACKET_OPEN);
9989
+ this.$serializationContext$.$serializedForwardRefCount$ = 0;
9990
+ try {
9991
+ this.$writer$.write(String(rootStart));
9992
+ this.$writer$.write(COMMA);
9993
+ this.$writer$.write(BRACKET_OPEN);
9994
+ await this.outputSelectedRoots(rootIds);
9995
+ this.$writer$.write(BRACKET_CLOSE);
9996
+ const forwardRefs = this.getForwardRefsPayload();
9997
+ this.$serializationContext$.$serializedForwardRefCount$ = forwardRefs?.length ?? 0;
9998
+ if (forwardRefs || extraRootId !== undefined) {
9999
+ this.$writer$.write(COMMA);
10000
+ if (forwardRefs) {
10001
+ this.outputForwardRefsArray(forwardRefs);
10002
+ }
10003
+ else {
10004
+ this.$writer$.write('0');
10005
+ }
10006
+ }
10007
+ if (extraRootId !== undefined) {
10008
+ this.$writer$.write(COMMA);
10009
+ if (typeof extraRootId === 'number') {
10010
+ this.writeRootRef(extraRootId);
10011
+ }
10012
+ else if (typeof extraRootId === 'string') {
10013
+ this.outputString(extraRootId);
10014
+ }
10015
+ else {
10016
+ this.$writer$.write(QUOTE);
10017
+ this.writeRootRefPath(extraRootId);
10018
+ this.$writer$.write(QUOTE);
10019
+ }
10020
+ }
10021
+ }
10022
+ finally {
10023
+ this.$streamedRootLimit$ = previousStreamedRootLimit;
10024
+ }
10025
+ this.$writer$.write(BRACKET_CLOSE);
10026
+ }
10027
+ $setWriter$(writer) {
10028
+ this.$writer$ = writer;
9746
10029
  }
9747
10030
  /** Helper to output an array */
9748
10031
  outputArray(value, keepUndefined, writeFn) {
@@ -9803,22 +10086,65 @@ class Serializer {
9803
10086
  }
9804
10087
  return Number(key);
9805
10088
  }
10089
+ outputString(value) {
10090
+ const s = this.stringNeedsJsonEscape$(value) ? JSON.stringify(value) : QUOTE + value + QUOTE;
10091
+ let angleBracketIdx = -1;
10092
+ let lastIdx = 0;
10093
+ while ((angleBracketIdx = s.indexOf(CLOSE_TAG, lastIdx)) !== -1) {
10094
+ this.$writer$.write(s.slice(lastIdx, angleBracketIdx));
10095
+ this.$writer$.write(ESCAPED_CLOSE_TAG);
10096
+ lastIdx = angleBracketIdx + 2;
10097
+ }
10098
+ this.$writer$.write(lastIdx === 0 ? s : s.slice(lastIdx));
10099
+ }
10100
+ writeRootRef(id) {
10101
+ this.$writer$.writeRootRef(id);
10102
+ }
10103
+ writeRootRefPath(path) {
10104
+ this.$writer$.writeRootRefPath(path);
10105
+ }
10106
+ outputStringChunks(chunks) {
10107
+ this.$writer$.write(QUOTE);
10108
+ for (let i = 0; i < chunks.length; i++) {
10109
+ const chunk = chunks[i];
10110
+ if (typeof chunk === 'string') {
10111
+ this.$writer$.write(chunk);
10112
+ }
10113
+ else if (typeof chunk === 'number') {
10114
+ this.writeRootRef(chunk);
10115
+ }
10116
+ else {
10117
+ this.writeRootRefPath(chunk.path);
10118
+ }
10119
+ }
10120
+ this.$writer$.write(QUOTE);
10121
+ }
9806
10122
  /** Output a type,value pair. If the value is an array, it calls writeValue on each item. */
9807
10123
  output(type, value, keepUndefined) {
9808
- if (typeof value === 'number') {
10124
+ if (type === 1 /* TypeIds.RootRef */) {
10125
+ this.$writer$.write(type + COMMA);
10126
+ if (typeof value === 'number') {
10127
+ this.writeRootRef(value);
10128
+ }
10129
+ else if (typeof value === 'string') {
10130
+ this.outputString(value);
10131
+ }
10132
+ else {
10133
+ this.$writer$.write(QUOTE);
10134
+ this.writeRootRefPath(value);
10135
+ this.$writer$.write(QUOTE);
10136
+ }
10137
+ }
10138
+ else if (type === 9 /* TypeIds.QRL */ && Array.isArray(value)) {
10139
+ this.$writer$.write(type + COMMA);
10140
+ this.outputStringChunks(value);
10141
+ }
10142
+ else if (typeof value === 'number') {
9809
10143
  this.$writer$.write(type + COMMA + value);
9810
10144
  }
9811
10145
  else if (typeof value === 'string') {
9812
- const s = this.stringNeedsJsonEscape$(value) ? JSON.stringify(value) : QUOTE + value + QUOTE;
9813
10146
  this.$writer$.write(type + COMMA);
9814
- let angleBracketIdx = -1;
9815
- let lastIdx = 0;
9816
- while ((angleBracketIdx = s.indexOf(CLOSE_TAG, lastIdx)) !== -1) {
9817
- this.$writer$.write(s.slice(lastIdx, angleBracketIdx));
9818
- this.$writer$.write(ESCAPED_CLOSE_TAG);
9819
- lastIdx = angleBracketIdx + 2;
9820
- }
9821
- this.$writer$.write(lastIdx === 0 ? s : s.slice(lastIdx));
10147
+ this.outputString(value);
9822
10148
  }
9823
10149
  else {
9824
10150
  this.$writer$.write(type + COMMA);
@@ -9844,16 +10170,19 @@ class Serializer {
9844
10170
  return this.$serializationContext$.$markSeen$(value, this.$parent$, index);
9845
10171
  }
9846
10172
  }
10173
+ else if (seen.$parent$ && this.isSeenInStreamedRoot(seen)) {
10174
+ seen = this.$serializationContext$.$addDuplicateRoot$(value);
10175
+ }
9847
10176
  // Now that we saw it a second time, make sure it's a root
9848
10177
  if (seen.$parent$) {
9849
10178
  // Note, this means it was output before so we always need a backref
9850
10179
  // Special case: we're a root so instead of adding a backref, we replace ourself
9851
10180
  if (!this.$parent$) {
9852
- this.$serializationContext$.$promoteToRoot$(seen, index);
10181
+ this.$serializationContext$.$promoteToRoot$(seen, value, index);
9853
10182
  value = this.$serializationContext$.$roots$[index];
9854
10183
  }
9855
10184
  else {
9856
- this.$serializationContext$.$promoteToRoot$(seen);
10185
+ this.$serializationContext$.$promoteToRoot$(seen, value);
9857
10186
  }
9858
10187
  }
9859
10188
  // Check if there was a weakref to us
@@ -9869,6 +10198,12 @@ class Serializer {
9869
10198
  }
9870
10199
  this.output(1 /* TypeIds.RootRef */, rootIdx);
9871
10200
  }
10201
+ isSeenInStreamedRoot(ref) {
10202
+ while (ref.$parent$) {
10203
+ ref = ref.$parent$;
10204
+ }
10205
+ return ref.$index$ < this.$streamedRootLimit$;
10206
+ }
9872
10207
  // First check for scalars, then do objects with seen checks
9873
10208
  // Make sure to only get the SeenRef once, it's expensive
9874
10209
  writeValue(value, index) {
@@ -9947,9 +10282,24 @@ class Serializer {
9947
10282
  let data;
9948
10283
  if (chunk !== '') {
9949
10284
  // not a sync QRL, replace all parts with string references
9950
- data = `${this.$serializationContext$.$addRoot$(chunk)}#${this.$serializationContext$.$addRoot$(symbol)}${captures ? '#' + captures : ''}`;
10285
+ data = [
10286
+ this.$serializationContext$.$addRoot$(chunk),
10287
+ '#',
10288
+ this.$serializationContext$.$addRoot$(symbol),
10289
+ ];
10290
+ if (captures) {
10291
+ const captureIds = captures.split(' ');
10292
+ data.push('#');
10293
+ for (let i = 0; i < captureIds.length; i++) {
10294
+ if (i > 0) {
10295
+ data.push(' ');
10296
+ }
10297
+ data.push(Number(captureIds[i]));
10298
+ }
10299
+ }
9951
10300
  // Since we map QRLs to strings, we need to keep track of this secondary mapping
9952
- const existing = this.$qrlMap$.get(data);
10301
+ const qrlKey = data.join('');
10302
+ const existing = this.$qrlMap$.get(qrlKey);
9953
10303
  if (existing) {
9954
10304
  // We encountered the same QRL again, make it a root
9955
10305
  const ref = this.$serializationContext$.$addRoot$(existing);
@@ -9957,7 +10307,7 @@ class Serializer {
9957
10307
  return;
9958
10308
  }
9959
10309
  else {
9960
- this.$qrlMap$.set(data, value);
10310
+ this.$qrlMap$.set(qrlKey, value);
9961
10311
  }
9962
10312
  }
9963
10313
  else {
@@ -10026,6 +10376,9 @@ class Serializer {
10026
10376
  // TODO no data if [null, true]
10027
10377
  this.output(40 /* TypeIds.EffectSubscription */, [value.consumer, value.property, value.data]);
10028
10378
  }
10379
+ else if (value instanceof SubscriptionPatch) {
10380
+ this.output(41 /* TypeIds.SubscriptionPatch */, [value.rootId, value.subscriptions]);
10381
+ }
10029
10382
  else if (isStore(value)) {
10030
10383
  const storeHandler = getStoreHandler(value);
10031
10384
  const storeTarget = getStoreTarget(value);
@@ -10079,7 +10432,7 @@ class Serializer {
10079
10432
  }
10080
10433
  }
10081
10434
  else if (this.$serializationContext$.$isDomRef$(value)) {
10082
- value.$ssrNode$.vnodeData[0] |= 16 /* VNodeDataFlag.SERIALIZE */;
10435
+ this.$serializationContext$.$markSsrNodeForSerialization$(value.$ssrNode$, 16 /* VNodeDataFlag.SERIALIZE */);
10083
10436
  this.output(11 /* TypeIds.RefVNode */, value.$ssrNode$.id);
10084
10437
  }
10085
10438
  else if (value instanceof SignalImpl) {
@@ -10204,23 +10557,22 @@ class Serializer {
10204
10557
  }
10205
10558
  else if (this.$serializationContext$.$isSsrNode$(value)) {
10206
10559
  const rootIndex = this.$serializationContext$.$addRoot$(value);
10207
- this.$serializationContext$.$setProp$(value, ELEMENT_ID, String(rootIndex));
10560
+ this.$serializationContext$.$setProp$(value, ELEMENT_ID, rootIndex);
10208
10561
  // we need to output before the vnode overwrites its values
10209
10562
  this.output(10 /* TypeIds.VNode */, value.id);
10210
10563
  const vNodeData = value.vnodeData;
10211
10564
  if (vNodeData) {
10212
- discoverValuesForVNodeData(vNodeData, (vNodeDataValue) => this.$serializationContext$.$addRoot$(vNodeDataValue));
10213
- vNodeData[0] |= 16 /* VNodeDataFlag.SERIALIZE */;
10565
+ discoverValuesForVNodeData(vNodeData, (vNodeDataValue) => {
10566
+ this.$serializationContext$.$addRoot$(vNodeDataValue);
10567
+ });
10568
+ this.$serializationContext$.$markSsrNodeForSerialization$(value, 16 /* VNodeDataFlag.SERIALIZE */);
10214
10569
  }
10215
10570
  if (value.children) {
10216
10571
  // Mark child vnode data for serialization (structure only, no value discovery needed)
10217
10572
  const childrenLength = value.children.length;
10218
10573
  for (let i = 0; i < childrenLength; i++) {
10219
10574
  const child = value.children[i];
10220
- const childVNodeData = child.vnodeData;
10221
- if (childVNodeData) {
10222
- childVNodeData[0] |= 16 /* VNodeDataFlag.SERIALIZE */;
10223
- }
10575
+ this.$serializationContext$.$markSsrNodeForSerialization$(child, 16 /* VNodeDataFlag.SERIALIZE */);
10224
10576
  }
10225
10577
  }
10226
10578
  }
@@ -10313,7 +10665,7 @@ class Serializer {
10313
10665
  this.$s11nWeakRefs$.set(obj, forwardRefId);
10314
10666
  this.$forwardRefs$[forwardRefId] = -1;
10315
10667
  }
10316
- this.output(2 /* TypeIds.ForwardRef */, forwardRefId);
10668
+ this.output(2 /* TypeIds.ForwardRef */, this.getForwardRefId(forwardRefId));
10317
10669
  }
10318
10670
  }
10319
10671
  else if (vnode_isVNode(value)) {
@@ -10335,16 +10687,16 @@ class Serializer {
10335
10687
  this.$forwardRefs$[forwardRefId] = this.$serializationContext$.$addRoot$(classCreator(false, err));
10336
10688
  });
10337
10689
  this.$promises$.add(promise);
10338
- return forwardRefId;
10690
+ return this.getForwardRefId(forwardRefId);
10339
10691
  }
10340
- async outputRoots() {
10341
- this.$writer$.write(BRACKET_OPEN);
10692
+ getForwardRefId(localId) {
10693
+ return this.$serializationContext$.$forwardRefOffset$ + localId;
10694
+ }
10695
+ async outputPendingRoots() {
10696
+ let rootsWritten = 0;
10342
10697
  const { $roots$ } = this.$serializationContext$;
10343
10698
  while (this.$rootIdx$ < $roots$.length || this.$promises$.size) {
10344
- if (this.$rootIdx$ !== 0) {
10345
- this.$writer$.write(COMMA);
10346
- }
10347
- let separator = false;
10699
+ let separator = rootsWritten > 0;
10348
10700
  for (; this.$rootIdx$ < $roots$.length; this.$rootIdx$++) {
10349
10701
  if (separator) {
10350
10702
  this.$writer$.write(COMMA);
@@ -10353,6 +10705,7 @@ class Serializer {
10353
10705
  separator = true;
10354
10706
  }
10355
10707
  this.writeValue($roots$[this.$rootIdx$], this.$rootIdx$);
10708
+ rootsWritten++;
10356
10709
  }
10357
10710
  if (this.$promises$.size) {
10358
10711
  try {
@@ -10363,24 +10716,77 @@ class Serializer {
10363
10716
  }
10364
10717
  }
10365
10718
  }
10366
- if (this.$forwardRefs$.length) {
10367
- let lastIdx = this.$forwardRefs$.length - 1;
10368
- while (lastIdx >= 0 && this.$forwardRefs$[lastIdx] === -1) {
10369
- lastIdx--;
10719
+ return rootsWritten;
10720
+ }
10721
+ async outputSelectedRoots(rootIds) {
10722
+ let separator = false;
10723
+ let i = 0;
10724
+ while (i < rootIds.length || this.$promises$.size) {
10725
+ if (i < rootIds.length) {
10726
+ if (separator) {
10727
+ this.$writer$.write(COMMA);
10728
+ }
10729
+ else {
10730
+ separator = true;
10731
+ }
10732
+ const rootId = rootIds[i++];
10733
+ this.writeValue(this.$serializationContext$.$roots$[rootId], rootId);
10734
+ continue;
10735
+ }
10736
+ try {
10737
+ await Promise.race(this.$promises$);
10738
+ }
10739
+ catch {
10740
+ // ignore rejections, they will be serialized as rejected promises
10741
+ }
10742
+ }
10743
+ }
10744
+ getForwardRefsPayload() {
10745
+ let lastIdx = this.$forwardRefs$.length - 1;
10746
+ while (lastIdx >= 0 && this.$forwardRefs$[lastIdx] === -1) {
10747
+ lastIdx--;
10748
+ }
10749
+ if (lastIdx < 0) {
10750
+ return null;
10751
+ }
10752
+ return lastIdx === this.$forwardRefs$.length - 1
10753
+ ? this.$forwardRefs$
10754
+ : this.$forwardRefs$.slice(0, lastIdx + 1);
10755
+ }
10756
+ outputForwardRefsArray(forwardRefs) {
10757
+ this.outputArray(forwardRefs, true, (value) => {
10758
+ if (typeof value === 'string') {
10759
+ this.outputString(value);
10760
+ }
10761
+ else if (Array.isArray(value)) {
10762
+ this.$writer$.write(QUOTE);
10763
+ this.writeRootRefPath(value);
10764
+ this.$writer$.write(QUOTE);
10370
10765
  }
10371
- if (lastIdx >= 0) {
10766
+ else {
10767
+ this.writeRootRef(value);
10768
+ }
10769
+ });
10770
+ }
10771
+ async outputRoots() {
10772
+ this.$writer$.write(BRACKET_OPEN);
10773
+ const rootsWritten = await this.outputPendingRoots();
10774
+ const forwardRefs = this.getForwardRefsPayload();
10775
+ this.$serializationContext$.$rootStateRootCount$ = this.$serializationContext$.$roots$.length;
10776
+ this.$serializationContext$.$hasRootStateForwardRefs$ = !!forwardRefs;
10777
+ const forwardRefCount = forwardRefs?.length ?? 0;
10778
+ if (forwardRefs) {
10779
+ if (rootsWritten > 0) {
10372
10780
  this.$writer$.write(COMMA);
10373
- this.$writer$.write(14 /* TypeIds.ForwardRefs */ + COMMA);
10374
- const out = lastIdx === this.$forwardRefs$.length - 1
10375
- ? this.$forwardRefs$
10376
- : this.$forwardRefs$.slice(0, lastIdx + 1);
10377
- // We could also implement RLE of -1 values
10378
- this.outputArray(out, true, (value) => {
10379
- this.$writer$.write(String(value));
10380
- });
10381
10781
  }
10782
+ this.$writer$.write(14 /* TypeIds.ForwardRefs */ + COMMA);
10783
+ this.outputForwardRefsArray(forwardRefs);
10382
10784
  }
10383
10785
  this.$writer$.write(BRACKET_CLOSE);
10786
+ this.$serializationContext$.$serializedRootCount$ =
10787
+ this.$serializationContext$.$roots$.length +
10788
+ (this.$serializationContext$.$hasRootStateForwardRefs$ ? 1 : 0);
10789
+ this.$serializationContext$.$serializedForwardRefCount$ = forwardRefCount;
10384
10790
  }
10385
10791
  }
10386
10792
  class PromiseResult {
@@ -10427,6 +10833,7 @@ const discoverValuesForVNodeData = (vnodeData, callback) => {
10427
10833
  const attrValue = value[key];
10428
10834
  if (attrValue == null ||
10429
10835
  typeof attrValue === 'string' ||
10836
+ (typeof attrValue === 'number' && key === ELEMENT_ID) ||
10430
10837
  (key === ELEMENT_PROPS && isObjectEmpty(attrValue))) {
10431
10838
  continue;
10432
10839
  }
@@ -10492,13 +10899,24 @@ class SerializationContextImpl {
10492
10899
  $writer$;
10493
10900
  $seenObjsMap$ = new Map();
10494
10901
  $syncFnMap$ = new Map();
10902
+ $syncFnOffset$ = 0;
10495
10903
  $syncFns$ = [];
10496
10904
  $roots$ = [];
10905
+ $rootObjs$ = [];
10906
+ $onAddRoot$;
10907
+ $forwardRefOffset$ = 0;
10908
+ $serializedRootCount$ = 0;
10909
+ $serializedForwardRefCount$ = 0;
10910
+ $rootStateRootCount$ = 0;
10911
+ $hasRootStateForwardRefs$ = false;
10497
10912
  $eagerResume$ = new Set();
10498
10913
  $eventQrls$ = new Set();
10499
10914
  $eventNames$ = new Set();
10500
10915
  $renderSymbols$ = new Set();
10501
10916
  $serializer$;
10917
+ $markSsrNodeForSerialization$ = (node, flags) => {
10918
+ node.vnodeData[0] |= flags;
10919
+ };
10502
10920
  constructor(
10503
10921
  /**
10504
10922
  * Node constructor, for instanceof checks.
@@ -10518,7 +10936,14 @@ class SerializationContextImpl {
10518
10936
  this.$serializer$ = new Serializer(this);
10519
10937
  }
10520
10938
  async $serialize$() {
10521
- return await this.$serializer$.serialize();
10939
+ await this.$serializer$.serialize();
10940
+ }
10941
+ async $serializePatch$(rootStart, rootIds, extraRootId, streamedRootLimit) {
10942
+ await this.$serializer$.serializePatch(rootStart, rootIds, extraRootId, streamedRootLimit);
10943
+ }
10944
+ $setWriter$(writer) {
10945
+ this.$writer$ = writer;
10946
+ this.$serializer$.$setWriter$(writer);
10522
10947
  }
10523
10948
  getSeenRef(obj) {
10524
10949
  return this.$seenObjsMap$.get(obj);
@@ -10529,8 +10954,8 @@ class SerializationContextImpl {
10529
10954
  return ref;
10530
10955
  }
10531
10956
  /**
10532
- * Returns a path string representing the path from roots through all parents to the object.
10533
- * Format: "3 2 0" where each number is the index within its parent, from root to leaf.
10957
+ * Returns a path representing the path from roots through all parents to the object. Format: [3,
10958
+ * 2, 0] where each number is the index within its parent, from root to leaf.
10534
10959
  */
10535
10960
  $getObjectPath$(ref) {
10536
10961
  // Traverse up through parent references to build a path
@@ -10541,16 +10966,23 @@ class SerializationContextImpl {
10541
10966
  }
10542
10967
  // Now we are at root, but it could be a backref
10543
10968
  path.unshift(ref.$index$);
10544
- return path.join(' ');
10969
+ return path;
10545
10970
  }
10546
- $promoteToRoot$(ref, index) {
10971
+ $promoteToRoot$(ref, obj, index) {
10547
10972
  const path = this.$getObjectPath$(ref);
10973
+ const isNewRoot = index === undefined;
10548
10974
  if (index === undefined) {
10549
10975
  index = this.$roots$.length;
10550
10976
  }
10551
10977
  this.$roots$[index] = new SerializationBackRef(path);
10978
+ if (isNewRoot) {
10979
+ this.$rootObjs$[index] = obj;
10980
+ }
10552
10981
  ref.$parent$ = null;
10553
10982
  ref.$index$ = index;
10983
+ if (isNewRoot) {
10984
+ this.$onAddRoot$?.(index, this.$roots$[index], obj);
10985
+ }
10554
10986
  }
10555
10987
  $addRoot$(obj, returnRef = false) {
10556
10988
  let seen = this.$seenObjsMap$.get(obj);
@@ -10564,15 +10996,34 @@ class SerializationContextImpl {
10564
10996
  };
10565
10997
  this.$seenObjsMap$.set(obj, seen);
10566
10998
  this.$roots$.push(obj);
10999
+ this.$rootObjs$.push(obj);
11000
+ this.$onAddRoot$?.(index, obj, obj);
10567
11001
  }
10568
11002
  else {
10569
11003
  if (seen.$parent$) {
10570
- this.$promoteToRoot$(seen);
11004
+ this.$promoteToRoot$(seen, obj);
10571
11005
  }
10572
11006
  index = seen.$index$;
10573
11007
  }
10574
11008
  return returnRef ? seen : index;
10575
11009
  }
11010
+ $addDuplicateRoot$(obj) {
11011
+ const index = this.$roots$.length;
11012
+ const ref = { $index$: index };
11013
+ this.$seenObjsMap$.set(obj, ref);
11014
+ this.$roots$.push(obj);
11015
+ this.$rootObjs$.push(obj);
11016
+ this.$onAddRoot$?.(index, obj, obj);
11017
+ return ref;
11018
+ }
11019
+ $commitRoot$(root, obj) {
11020
+ const index = this.$roots$.length;
11021
+ const ref = { $index$: index };
11022
+ this.$seenObjsMap$.set(obj, ref);
11023
+ this.$roots$.push(root);
11024
+ this.$rootObjs$.push(obj);
11025
+ return index;
11026
+ }
10576
11027
  $isSsrNode$(obj) {
10577
11028
  return this.NodeConstructor ? obj instanceof this.NodeConstructor : false;
10578
11029
  }
@@ -10590,7 +11041,7 @@ class SerializationContextImpl {
10590
11041
  }
10591
11042
  let id = this.$syncFnMap$.get(funcStr);
10592
11043
  if (id === undefined) {
10593
- id = this.$syncFns$.length;
11044
+ id = this.$syncFnOffset$ + this.$syncFns$.length;
10594
11045
  this.$syncFnMap$.set(funcStr, id);
10595
11046
  if (isFullFn) {
10596
11047
  this.$syncFns$.push(funcStr);
@@ -10605,6 +11056,15 @@ class SerializationContextImpl {
10605
11056
  }
10606
11057
  return id;
10607
11058
  }
11059
+ $setSyncFnOffset$(offset, existingFns) {
11060
+ this.$syncFnOffset$ = offset;
11061
+ if (existingFns) {
11062
+ this.$syncFnMap$.clear();
11063
+ for (let i = 0; i < existingFns.length; i++) {
11064
+ this.$syncFnMap$.set(existingFns[i], i);
11065
+ }
11066
+ }
11067
+ }
10608
11068
  }
10609
11069
  const createSerializationContext = (
10610
11070
  /**
@@ -10618,12 +11078,11 @@ NodeConstructor,
10618
11078
  DomRefConstructor, symbolToChunkResolver, setProp, storeProxyMap, writer) => {
10619
11079
  if (!writer) {
10620
11080
  const buffer = [];
10621
- writer = {
10622
- write: (text) => {
10623
- buffer.push(text);
10624
- },
11081
+ writer = Object.assign(createStringStreamWriter((text) => {
11082
+ buffer.push(text);
11083
+ }), {
10625
11084
  toString: () => buffer.join(''),
10626
- };
11085
+ });
10627
11086
  }
10628
11087
  isDomRef = (DomRefConstructor ? (obj) => obj instanceof DomRefConstructor : (() => false));
10629
11088
  return new SerializationContextImpl(NodeConstructor, DomRefConstructor, symbolToChunkResolver, setProp, storeProxyMap, writer);
@@ -10881,15 +11340,16 @@ const constantToName = (code) => {
10881
11340
  * @returns The preprocessed state data
10882
11341
  * @internal
10883
11342
  */
10884
- function preprocessState(data, container) {
11343
+ function preprocessState(data, container, segmentId, startIndex = 0) {
10885
11344
  const isRootDeepRef = (type, value) => {
10886
- return type === 1 /* TypeIds.RootRef */ && typeof value === 'string';
11345
+ return type === 1 /* TypeIds.RootRef */ && typeof value === 'string' && value.indexOf(' ') !== -1;
10887
11346
  };
10888
11347
  const isForwardRefsMap = (type) => {
10889
11348
  return type === 14 /* TypeIds.ForwardRefs */;
10890
11349
  };
10891
11350
  const processRootRef = (index) => {
10892
11351
  const rootRefPath = data[index + 1].split(' ');
11352
+ const firstRefIndex = parseInt(rootRefPath[0], 10);
10893
11353
  let object = data;
10894
11354
  let objectType = 1 /* TypeIds.RootRef */;
10895
11355
  let typeIndex = 0;
@@ -10897,12 +11357,13 @@ function preprocessState(data, container) {
10897
11357
  let parent = null;
10898
11358
  for (let i = 0; i < rootRefPath.length; i++) {
10899
11359
  parent = object;
10900
- typeIndex = parseInt(rootRefPath[i], 10) * 2;
11360
+ typeIndex = (i === 0 ? firstRefIndex : parseInt(rootRefPath[i], 10)) * 2;
10901
11361
  valueIndex = typeIndex + 1;
10902
- objectType = object[typeIndex];
10903
- object = object[valueIndex];
11362
+ const objectArray = object;
11363
+ objectType = objectArray[typeIndex];
11364
+ object = objectArray[valueIndex];
10904
11365
  if (objectType === 1 /* TypeIds.RootRef */) {
10905
- const rootRef = object;
11366
+ const rootRef = typeof object === 'string' ? parseInt(object, 10) : object;
10906
11367
  const rootRefTypeIndex = rootRef * 2;
10907
11368
  objectType = data[rootRefTypeIndex];
10908
11369
  object = data[rootRefTypeIndex + 1];
@@ -10915,7 +11376,7 @@ function preprocessState(data, container) {
10915
11376
  data[index] = objectType;
10916
11377
  data[index + 1] = object;
10917
11378
  };
10918
- for (let i = 0; i < data.length; i += 2) {
11379
+ for (let i = startIndex; i < data.length; i += 2) {
10919
11380
  if (isRootDeepRef(data[i], data[i + 1])) {
10920
11381
  processRootRef(i);
10921
11382
  }
@@ -10934,10 +11395,7 @@ const allocate = (container, typeId, value) => {
10934
11395
  case 1 /* TypeIds.RootRef */:
10935
11396
  return container.$getObjectById$(value);
10936
11397
  case 2 /* TypeIds.ForwardRef */:
10937
- if (!container.$forwardRefs$) {
10938
- return _UNINITIALIZED;
10939
- }
10940
- const rootRef = container.$forwardRefs$[value];
11398
+ const rootRef = container.$getForwardRef$(value);
10941
11399
  if (rootRef === -1 || rootRef === undefined) {
10942
11400
  return _UNINITIALIZED;
10943
11401
  }
@@ -11093,6 +11551,8 @@ const allocate = (container, typeId, value) => {
11093
11551
  return new SubscriptionData({});
11094
11552
  case 40 /* TypeIds.EffectSubscription */:
11095
11553
  return new EffectSubscription(null, null, null, null);
11554
+ case 41 /* TypeIds.SubscriptionPatch */:
11555
+ return new SubscriptionPatch();
11096
11556
  default:
11097
11557
  throw qError(18 /* QError.serializeErrorCannotAllocate */, [typeId]);
11098
11558
  }
@@ -11219,6 +11679,8 @@ const _fnSignal = (fn, args, fnStr) => {
11219
11679
  class _SharedContainer {
11220
11680
  $version$;
11221
11681
  $storeProxyMap$;
11682
+ $rootContainer$ = null;
11683
+ $isOutOfOrderSegment$ = false;
11222
11684
  /// Current language locale
11223
11685
  $locale$;
11224
11686
  /// Retrieve Object from paused serialized state.
@@ -11257,6 +11719,24 @@ function isAsyncGenerator(value) {
11257
11719
  return !!value[Symbol.asyncIterator];
11258
11720
  }
11259
11721
 
11722
+ const InternalServerComponentSymbol = Symbol('qInternalServerComponent');
11723
+ /** @internal */
11724
+ const createInternalServerComponent = (handler) => {
11725
+ const component = (() => {
11726
+ throw new Error('Internal server component must be handled by the SSR renderer.');
11727
+ });
11728
+ component[InternalServerComponentSymbol] = handler;
11729
+ return component;
11730
+ };
11731
+ /** @internal */
11732
+ const isInternalServerComponent = (type) => {
11733
+ return typeof type === 'function' && InternalServerComponentSymbol in type;
11734
+ };
11735
+ /** @internal */
11736
+ const getInternalServerComponentHandler = (type) => {
11737
+ return type[InternalServerComponentSymbol];
11738
+ };
11739
+
11260
11740
  const applyInlineComponent = (ssr, componentHost, inlineComponentFunction, jsx) => {
11261
11741
  const host = ssr.getOrCreateLastNode();
11262
11742
  return executeComponent(ssr, host, componentHost, inlineComponentFunction, jsx.props);
@@ -11303,7 +11783,10 @@ async function _walkJSX(ssr, value, options) {
11303
11783
  stack.push(await stack.pop());
11304
11784
  }
11305
11785
  else {
11306
- await value.apply(ssr);
11786
+ const result = value.apply(ssr);
11787
+ if (isPromise(result)) {
11788
+ await result;
11789
+ }
11307
11790
  }
11308
11791
  continue;
11309
11792
  }
@@ -11392,16 +11875,23 @@ function processJSXNode(ssr, enqueue, value, options) {
11392
11875
  else if (type === 'body') {
11393
11876
  enqueue(ssr.additionalBodyNodes);
11394
11877
  }
11395
- else if (!ssr.isHtml && !ssr._didAddQwikLoader && !ssr.$noScriptHere$) {
11396
- ssr.emitQwikLoaderAtTopIfNeeded();
11397
- ssr.emitPreloaderPre();
11398
- ssr._didAddQwikLoader = true;
11878
+ else {
11879
+ const innerSSR = ssr;
11880
+ if (!ssr.isHtml && !innerSSR._didAddQwikLoader && !ssr.$noScriptHere$) {
11881
+ ssr.emitQwikLoaderAtTopIfNeeded();
11882
+ ssr.emitPreloaderPre();
11883
+ innerSSR._didAddQwikLoader = true;
11884
+ }
11399
11885
  }
11400
11886
  const children = jsx.children;
11401
11887
  children != null && enqueue(children);
11402
11888
  }
11403
11889
  else if (isFunction(type)) {
11404
- if (type === Fragment) {
11890
+ if (__EXPERIMENTAL__.suspense && isInternalServerComponent(type)) {
11891
+ enqueue(() => getInternalServerComponentHandler(type)(ssr, jsx, options, enqueue));
11892
+ return;
11893
+ }
11894
+ else if (type === Fragment) {
11405
11895
  const attrs = jsx.key != null ? { [ELEMENT_KEY]: jsx.key } : {};
11406
11896
  if (isDev) {
11407
11897
  attrs[DEBUG_TYPE] = "F" /* VirtualType.Fragment */; // Add debug info.
@@ -11425,7 +11915,7 @@ function processJSXNode(ssr, enqueue, value, options) {
11425
11915
  ssr.openProjection(projectionAttrs);
11426
11916
  const host = componentFrame.componentNode;
11427
11917
  const node = ssr.getOrCreateLastNode();
11428
- const slotName = getSlotName(host, jsx, ssr);
11918
+ const slotName = resolveSlotName(host, jsx, ssr);
11429
11919
  projectionAttrs[QSlot] = slotName;
11430
11920
  enqueue(setParentOptions(options, options.currentStyleScoped, options.parentComponentFrame));
11431
11921
  enqueue(ssr.closeProjection);
@@ -11491,7 +11981,7 @@ function processJSXNode(ssr, enqueue, value, options) {
11491
11981
  componentFrame.distributeChildrenIntoSlots(jsx.children, options.currentStyleScoped, options.parentComponentFrame);
11492
11982
  const jsxOutput = applyQwikComponentBody(ssr, jsx, type);
11493
11983
  enqueue(setParentOptions(options, options.currentStyleScoped, options.parentComponentFrame));
11494
- enqueue(ssr.closeComponent);
11984
+ enqueue(() => ssr.closeComponent());
11495
11985
  if (isPromise(jsxOutput)) {
11496
11986
  // Defer reading QScopedStyle until after the promise resolves
11497
11987
  enqueue(async () => {
@@ -11537,16 +12027,6 @@ function maybeAddPollingAsyncSignalToEagerResume(serializationCtx, signal) {
11537
12027
  }
11538
12028
  }
11539
12029
  }
11540
- function getSlotName(host, jsx, ssr) {
11541
- const constProps = jsx.constProps;
11542
- if (constProps && typeof constProps == 'object' && 'name' in constProps) {
11543
- const constValue = constProps.name;
11544
- if (constValue instanceof WrappedSignalImpl) {
11545
- return trackSignalAndAssignHost(constValue, host, ":" /* EffectProperty.COMPONENT */, ssr);
11546
- }
11547
- }
11548
- return directGetPropsProxyProp(jsx, 'name') || QDefaultSlot;
11549
- }
11550
12030
  function appendQwikInspectorAttribute(jsx, qwikInspectorAttrValue) {
11551
12031
  if (qwikInspectorAttrValue && (!jsx.constProps || !(qwikInspectorAttr in jsx.constProps))) {
11552
12032
  (jsx.constProps ||= {})[qwikInspectorAttr] = qwikInspectorAttrValue;
@@ -11698,7 +12178,19 @@ function setEvent(serializationCtx, key, rawValue, hasMovedCaptures) {
11698
12178
  let value = null;
11699
12179
  const qrls = rawValue;
11700
12180
  const appendToValue = (valueToAppend) => {
11701
- value = (value == null ? '' : value + '|') + valueToAppend;
12181
+ if (value == null) {
12182
+ value = valueToAppend;
12183
+ }
12184
+ else if (typeof value === 'string' && typeof valueToAppend === 'string') {
12185
+ value += '|' + valueToAppend;
12186
+ }
12187
+ else {
12188
+ value = [
12189
+ ...(typeof value === 'string' ? [value] : value),
12190
+ '|',
12191
+ ...(typeof valueToAppend === 'string' ? [valueToAppend] : valueToAppend),
12192
+ ];
12193
+ }
11702
12194
  };
11703
12195
  const getQrlString = (qrl) => {
11704
12196
  /**
@@ -11710,7 +12202,7 @@ function setEvent(serializationCtx, key, rawValue, hasMovedCaptures) {
11710
12202
  if (!qrl.$symbol$.startsWith('_') && (qrl.$captures$?.length || hasMovedCaptures)) {
11711
12203
  qrl = createQRL(null, '_run', _run, null, [qrl]);
11712
12204
  }
11713
- return qrlToString(serializationCtx, qrl);
12205
+ return qrlToChunks(serializationCtx, qrl);
11714
12206
  };
11715
12207
  if (Array.isArray(qrls)) {
11716
12208
  for (let i = 0; i < qrls.length; i++) {
@@ -12284,6 +12776,13 @@ const inflate = (container, target, typeId, data) => {
12284
12776
  restoreEffectBackRefForConsumer(effectSub);
12285
12777
  break;
12286
12778
  }
12779
+ case 41 /* TypeIds.SubscriptionPatch */: {
12780
+ const patch = target;
12781
+ const d = data;
12782
+ patch.rootId = d[0];
12783
+ patch.subscriptions = d[1];
12784
+ break;
12785
+ }
12287
12786
  default:
12288
12787
  throw qError(16 /* QError.serializeErrorNotImplemented */, [typeId]);
12289
12788
  }
@@ -12391,11 +12890,9 @@ const wrapDeserializerProxy = (container, data) => {
12391
12890
  class DeserializationHandler {
12392
12891
  $container$;
12393
12892
  $data$;
12394
- $length$;
12395
12893
  constructor($container$, $data$) {
12396
12894
  this.$container$ = $container$;
12397
12895
  this.$data$ = $data$;
12398
- this.$length$ = this.$data$.length / 2;
12399
12896
  }
12400
12897
  get(target, property, receiver) {
12401
12898
  if (property === SERIALIZER_PROXY_UNWRAP) {
@@ -12407,7 +12904,7 @@ class DeserializationHandler {
12407
12904
  : typeof property === 'string'
12408
12905
  ? parseInt(property, 10)
12409
12906
  : NaN;
12410
- if (Number.isNaN(i) || i < 0 || i >= this.$length$) {
12907
+ if (Number.isNaN(i) || i < 0 || i >= this.$data$.length / 2) {
12411
12908
  return Reflect.get(target, property, receiver);
12412
12909
  }
12413
12910
  // The serialized data is an array with 2 values for each item
@@ -12494,6 +12991,7 @@ function _createDeserializeContainer(stateData) {
12494
12991
  let state;
12495
12992
  const container = {
12496
12993
  $getObjectById$: (id) => getObjectById(id, state),
12994
+ $getForwardRef$: (id) => container.$forwardRefs$?.[Number(id)],
12497
12995
  getSyncFn: (_) => {
12498
12996
  const fn = () => { };
12499
12997
  return fn;
@@ -12886,59 +13384,265 @@ const isRecoverable = (err) => {
12886
13384
  return true;
12887
13385
  };
12888
13386
 
13387
+ /** @internal */
13388
+ const canRevealRegistration = (registration, isPending = (item) => item.boundary.pending.untrackedValue > 0) => {
13389
+ if (registration === null) {
13390
+ return true;
13391
+ }
13392
+ const reveal = registration.reveal;
13393
+ const current = registration.item;
13394
+ const items = reveal.items;
13395
+ switch (reveal.order) {
13396
+ case 'together':
13397
+ for (let i = 0; i < items.length; i++) {
13398
+ if (isPending(items[i])) {
13399
+ return false;
13400
+ }
13401
+ }
13402
+ return true;
13403
+ case 'sequential':
13404
+ for (let i = 0; i < items.length; i++) {
13405
+ const item = items[i];
13406
+ if (item === current) {
13407
+ return true;
13408
+ }
13409
+ if (isPending(item)) {
13410
+ return false;
13411
+ }
13412
+ }
13413
+ return true;
13414
+ case 'reverse':
13415
+ for (let i = items.length - 1; i >= 0; i--) {
13416
+ const item = items[i];
13417
+ if (item === current) {
13418
+ return true;
13419
+ }
13420
+ if (isPending(item)) {
13421
+ return false;
13422
+ }
13423
+ }
13424
+ return true;
13425
+ default:
13426
+ return true;
13427
+ }
13428
+ };
13429
+
13430
+ /** @internal */
13431
+ const SUSPENSE_QRL_SYMBOL = '_suC';
13432
+ const outOfOrderRevealIds = new WeakMap();
13433
+ /** @internal */
13434
+ class OutOfOrderRevealCoordinator {
13435
+ id;
13436
+ collapsed;
13437
+ count = 0;
13438
+ pendingItems = new Set();
13439
+ orderCode;
13440
+ constructor(id, order, collapsed) {
13441
+ this.id = id;
13442
+ this.collapsed = collapsed;
13443
+ this.orderCode = getOutOfOrderRevealOrderCode(order);
13444
+ }
13445
+ register(registration) {
13446
+ this.pendingItems.add(registration.item);
13447
+ const index = this.count++;
13448
+ return {
13449
+ attrs: ` q:g="${this.id}" q:i="${index}" q:o="${this.orderCode}"` + (this.collapsed ? ' q:c' : ''),
13450
+ showFallback: this.canReveal(registration) || !this.collapsed,
13451
+ resolve: () => {
13452
+ this.pendingItems.delete(registration.item);
13453
+ },
13454
+ };
13455
+ }
13456
+ canReveal(registration) {
13457
+ return canRevealRegistration(registration, (item) => this.pendingItems.has(item));
13458
+ }
13459
+ script() {
13460
+ return this.count === 0 ? '' : `qO.g(${this.id},${this.count},"${this.orderCode}");`;
13461
+ }
13462
+ }
13463
+ /** @internal */
13464
+ const createOutOfOrderRevealCoordinator = (order, collapsed) => {
13465
+ if (!isOutOfOrderStreaming()) {
13466
+ return null;
13467
+ }
13468
+ const container = tryGetInvokeContext()?.$container$;
13469
+ let id = 0;
13470
+ if (container) {
13471
+ id = (outOfOrderRevealIds.get(container) || 0) + 1;
13472
+ outOfOrderRevealIds.set(container, id);
13473
+ }
13474
+ return new OutOfOrderRevealCoordinator(id, order, collapsed);
13475
+ };
13476
+ const getOutOfOrderRevealOrderCode = (order) => {
13477
+ switch (order) {
13478
+ case 'sequential':
13479
+ return 's';
13480
+ case 'reverse':
13481
+ return 'r';
13482
+ case 'together':
13483
+ return 't';
13484
+ default:
13485
+ return 'p';
13486
+ }
13487
+ };
13488
+ /** @internal */
13489
+ const isOutOfOrderStreaming = () => {
13490
+ if (!__EXPERIMENTAL__.suspense) {
13491
+ return false;
13492
+ }
13493
+ const container = tryGetInvokeContext()?.$container$;
13494
+ return container?.outOfOrderStreaming === true && !isOutOfOrderSegmentContainer(container);
13495
+ };
13496
+ /** @internal */
13497
+ const nextOutOfOrderSuspenseId = () => {
13498
+ if (!__EXPERIMENTAL__.suspense) {
13499
+ return 0;
13500
+ }
13501
+ const container = tryGetInvokeContext()?.$container$;
13502
+ if (container?.outOfOrderStreaming !== true) {
13503
+ return 0;
13504
+ }
13505
+ return container?.nextOutOfOrderId?.() ?? 0;
13506
+ };
13507
+ /** @internal */
13508
+ const applySubscriptionPatches = (container, patches) => {
13509
+ if (!__EXPERIMENTAL__.suspense || !patches) {
13510
+ return;
13511
+ }
13512
+ for (let i = 0; i < patches.length; i++) {
13513
+ const patch = patches[i];
13514
+ const root = container.$getObjectById$(patch.rootId);
13515
+ const subscriptions = patch.subscriptions;
13516
+ if (root instanceof SignalImpl) {
13517
+ if (subscriptions instanceof Set) {
13518
+ mergeSubscriptionSet(container, root, root, (root.$effects$ ||= new Set()), subscriptions);
13519
+ }
13520
+ }
13521
+ else {
13522
+ if (!(subscriptions instanceof Map)) {
13523
+ continue;
13524
+ }
13525
+ const handler = getStoreHandler(root);
13526
+ const target = getStoreTarget(root);
13527
+ if (!handler || !target) {
13528
+ continue;
13529
+ }
13530
+ const effectsMap = (handler.$effects$ ||= new Map());
13531
+ for (const [storeProp, subscriptionSet] of subscriptions) {
13532
+ let rootEffects = effectsMap.get(storeProp);
13533
+ if (!rootEffects) {
13534
+ rootEffects = new Set();
13535
+ effectsMap.set(storeProp, rootEffects);
13536
+ }
13537
+ mergeSubscriptionSet(container, handler, target, rootEffects, subscriptionSet);
13538
+ }
13539
+ }
13540
+ }
13541
+ };
13542
+ const mergeSubscriptionSet = (container, producer, backRef, rootEffects, patchEffects) => {
13543
+ let newEffects;
13544
+ for (const effect of patchEffects) {
13545
+ if (!rootEffects.has(effect)) {
13546
+ rootEffects.add(effect);
13547
+ (newEffects ||= new Set()).add(effect);
13548
+ }
13549
+ (effect.backRef ||= new Set()).add(backRef);
13550
+ }
13551
+ if (newEffects) {
13552
+ scheduleEffects(container, producer, newEffects);
13553
+ }
13554
+ };
13555
+
13556
+ const processedStatePatchScripts = new WeakMap();
13557
+ /** @internal */
13558
+ const processSegmentStateScripts = (container, segmentId) => {
13559
+ if (!__EXPERIMENTAL__.suspense) {
13560
+ return;
13561
+ }
13562
+ const stateContainer = container;
13563
+ const qwikStates = stateContainer.element.querySelectorAll(`${stateScriptSelector(stateContainer.$instanceHash$)}${QStatePatchAttrSelector}`);
13564
+ const processedScripts = getProcessedStatePatchScripts(container);
13565
+ for (let i = 0; i < qwikStates.length; i++) {
13566
+ const stateScript = qwikStates[i];
13567
+ if (segmentId !== undefined && stateScript.getAttribute(QSuspenseResolved) !== segmentId) {
13568
+ continue;
13569
+ }
13570
+ if (processedScripts.has(stateScript)) {
13571
+ continue;
13572
+ }
13573
+ processedScripts.add(stateScript);
13574
+ processStatePatch(container, stateContainer, stateScript.textContent);
13575
+ }
13576
+ };
13577
+ const getProcessedStatePatchScripts = (container) => {
13578
+ let processedScripts = processedStatePatchScripts.get(container);
13579
+ if (!processedScripts) {
13580
+ processedScripts = new WeakSet();
13581
+ processedStatePatchScripts.set(container, processedScripts);
13582
+ }
13583
+ return processedScripts;
13584
+ };
13585
+ const stateScriptSelector = (instanceHash) => {
13586
+ return `script[type="qwik/state"][q\\:instance="${instanceHash}"]`;
13587
+ };
13588
+ const processStatePatch = (container, stateContainer, textContent) => {
13589
+ if (textContent) {
13590
+ const [rootStart, rawStateData, forwardRefs, subscriptionPatchRootId] = JSON.parse(textContent);
13591
+ appendStatePatchRoots(container, stateContainer, rootStart, rawStateData);
13592
+ mergeForwardRefs(stateContainer, forwardRefs || undefined);
13593
+ applySubscriptionPatches(container, subscriptionPatchRootId === undefined
13594
+ ? undefined
13595
+ : stateContainer.$getObjectById$(subscriptionPatchRootId));
13596
+ }
13597
+ };
13598
+ const appendStatePatchRoots = (container, stateContainer, rootStart, rawStateData) => {
13599
+ const currentRootCount = stateContainer.$rawStateData$.length / 2;
13600
+ if (rootStart !== currentRootCount) {
13601
+ if (qDev) {
13602
+ throw new Error(`Invalid Qwik state patch root start: expected ${currentRootCount}, received ${rootStart}.`);
13603
+ }
13604
+ return;
13605
+ }
13606
+ for (let i = 0; i < rawStateData.length; i++) {
13607
+ stateContainer.$rawStateData$[rootStart * 2 + i] = rawStateData[i];
13608
+ }
13609
+ preprocessState(stateContainer.$rawStateData$, container, undefined, rootStart * 2);
13610
+ stateContainer.$stateData$ = wrapDeserializerProxy(container, stateContainer.$rawStateData$);
13611
+ stateContainer.$stateData$.length = stateContainer.$rawStateData$.length / 2;
13612
+ stateContainer.$rootForwardRefs$ = stateContainer.$forwardRefs$;
13613
+ };
13614
+ const mergeForwardRefs = (stateContainer, forwardRefs) => {
13615
+ if (!forwardRefs) {
13616
+ return;
13617
+ }
13618
+ const rootForwardRefs = (stateContainer.$rootForwardRefs$ ||= []);
13619
+ for (let i = 0; i < forwardRefs.length; i++) {
13620
+ const ref = forwardRefs[i];
13621
+ if (ref !== undefined) {
13622
+ rootForwardRefs.push(ref);
13623
+ }
13624
+ }
13625
+ stateContainer.$forwardRefs$ = rootForwardRefs;
13626
+ };
13627
+
12889
13628
  // NOTE: we want to move this function to qwikloader, and therefore this function should not have any external dependencies
12890
- /**
12891
- * Process the VNodeData script tags and store the VNodeData in the VNodeDataMap.
12892
- *
12893
- * The end result of this function is that each DOM element has the associated `VNodeData` attached
12894
- * to it, to be used later `VNode` materialization. The "attachment" is done through the
12895
- * `VNodeDataMap`.
12896
- *
12897
- * Run this function on startup to process the `<script type="qwik/vnode">` tags. The data in the
12898
- * tags needs to be parsed and attached to the DOM elements. (We do this through `VNodeDataMap`)
12899
- * `VNodeDataMap` is then used to lazily materialize the VNodes.
12900
- *
12901
- * Only one invocation of this function is needed per document/browser session.
12902
- *
12903
- * Below is an example of the kinds of constructs which need to be handled when dealing with
12904
- * VNodeData deserialization.
12905
- *
12906
- * ```
12907
- * <html q:container="paused">
12908
- * <body>
12909
- * <div q:container="paused">
12910
- * <script type="qwik/vnode">...</script>
12911
- * </div>
12912
- * <div q:container="html">...</div>
12913
- * before
12914
- * <!--q:container=ABC-->
12915
- * ...
12916
- * <!--/q:container-->
12917
- * after
12918
- * <!--q:ignore=FOO-->
12919
- * ...
12920
- * <!--q:container-island=BAR-->
12921
- * <div>some interactive island</div>
12922
- * <!--/q:container-island-->
12923
- * ...
12924
- * <!--/q:ignore-->
12925
- * <textarea q:container="text">...</textarea>
12926
- * <script type="qwik/vnode">...</script>
12927
- * </body>
12928
- * </html>
12929
- * ```
12930
- *
12931
- * Each `qwik/vnode` script assumes that the elements are numbered in depth first order. For this
12932
- * reason, whenever the `processVNodeData` comes across a `q:container` it must ignore its
12933
- * children.
12934
- *
12935
- * IMPLEMENTATION:
12936
- *
12937
- * - Stack to keep track of the current `q:container` being processed.
12938
- * - Attach all `qwik/vnode` scripts (not the data contain within them) to the `q:container` element.
12939
- * - Walk the tree and process each `q:container` element.
12940
- */
13629
+ function processOutOfOrderSegmentVNodeData(document, segmentId, contentNode) {
13630
+ if (!__EXPERIMENTAL__.suspense || !contentNode) {
13631
+ return;
13632
+ }
13633
+ const qContainerElement = contentNode.closest('[q\\:container]');
13634
+ const script = qContainerElement?.querySelector(`script[type="qwik/vnode"][q\\:r="${segmentId}"]:not([q\\:patch])`);
13635
+ processVNodeDataImpl(document, segmentId, qContainerElement, contentNode, script?.textContent || undefined);
13636
+ const patches = qContainerElement?.querySelectorAll(`script[type="qwik/vnode"][q\\:r="${segmentId}"][q\\:patch]`);
13637
+ for (let i = 0; patches && i < patches.length; i++) {
13638
+ document.qProcessVNodeDataPatch?.(patches[i]);
13639
+ }
13640
+ }
12941
13641
  function processVNodeData(document) {
13642
+ processVNodeDataImpl(document);
13643
+ document.qVNodeDataProcessed = true;
13644
+ }
13645
+ function processVNodeDataImpl(document, segmentId, segmentContainer, segmentContent, segmentVNodeData) {
12942
13646
  const Q_CONTAINER = 'q:container';
12943
13647
  const Q_CONTAINER_END = '/' + Q_CONTAINER;
12944
13648
  const Q_PROPS_SEPARATOR = ':';
@@ -12947,8 +13651,11 @@ function processVNodeData(document) {
12947
13651
  const Q_IGNORE_END = '/' + Q_IGNORE;
12948
13652
  const Q_CONTAINER_ISLAND = 'q:container-island';
12949
13653
  const Q_CONTAINER_ISLAND_END = '/' + Q_CONTAINER_ISLAND;
13654
+ const Q_SUSPENSE_RESOLVED = 'q:r';
13655
+ const Q_SUSPENSE_RESULT_PARENT = 'q:rp';
13656
+ const Q_PATCH = 'q:patch';
12950
13657
  const qDocument = document;
12951
- const vNodeDataMap = qDocument.qVNodeData || (qDocument.qVNodeData = new WeakMap());
13658
+ const vNodeDataMap = (qDocument.qVNodeData ||= new WeakMap());
12952
13659
  const prototype = document.body;
12953
13660
  const getter = (prototype, name) => {
12954
13661
  let getter;
@@ -12968,9 +13675,20 @@ function processVNodeData(document) {
12968
13675
  const scripts = element.querySelectorAll('script[type="qwik/vnode"]');
12969
13676
  for (let i = 0; i < scripts.length; i++) {
12970
13677
  const script = scripts[i];
12971
- const qContainerElement = script.closest('[q\\:container]');
12972
- qContainerElement.qVnodeData = script.textContent;
12973
- qContainerElement.qVNodeRefs = new Map();
13678
+ const qContainer = script.closest('[q\\:container]');
13679
+ qContainer.qVNodeRefs ||= new Map();
13680
+ if (script.hasAttribute(Q_PATCH)) {
13681
+ qDocument.qProcessVNodeDataPatch?.(script);
13682
+ continue;
13683
+ }
13684
+ const scriptContent = script.textContent;
13685
+ const segment = __EXPERIMENTAL__.suspense && script.getAttribute(Q_SUSPENSE_RESOLVED);
13686
+ if (segment) {
13687
+ (qContainer.qSegmentVnodeData ||= new Map()).set(segment, scriptContent);
13688
+ }
13689
+ else {
13690
+ qContainer.qVnodeData = scriptContent;
13691
+ }
12974
13692
  }
12975
13693
  const shadowRoots = element.querySelectorAll('[q\\:shadowroot]');
12976
13694
  for (let i = 0; i < shadowRoots.length; i++) {
@@ -12979,7 +13697,6 @@ function processVNodeData(document) {
12979
13697
  shadowRoot && attachVnodeDataAndRefs(shadowRoot);
12980
13698
  }
12981
13699
  };
12982
- attachVnodeDataAndRefs(document);
12983
13700
  /**
12984
13701
  * Looks up which type of node this is in a monomorphic way which should be faster.
12985
13702
  *
@@ -12988,17 +13705,16 @@ function processVNodeData(document) {
12988
13705
  const getFastNodeType = (node) => {
12989
13706
  const nodeType = getNodeType.call(node);
12990
13707
  if (nodeType === 1 /* Node.ELEMENT_NODE */) {
12991
- const qContainer = getAttribute.call(node, Q_CONTAINER);
12992
- if (qContainer === null) {
12993
- if (hasAttribute.call(node, Q_SHADOW_ROOT)) {
12994
- return 6 /* NodeType.ELEMENT_SHADOW_ROOT_WRAPPER */;
12995
- }
12996
- const isQElement = hasAttribute.call(node, Q_PROPS_SEPARATOR);
12997
- return isQElement ? 2 /* NodeType.ELEMENT */ : 0 /* NodeType.OTHER */;
12998
- }
12999
- else {
13708
+ if (getAttribute.call(node, Q_CONTAINER) !== null) {
13000
13709
  return 3 /* NodeType.ELEMENT_CONTAINER */;
13001
13710
  }
13711
+ if (hasAttribute.call(node, Q_SHADOW_ROOT)) {
13712
+ return 6 /* NodeType.ELEMENT_SHADOW_ROOT_WRAPPER */;
13713
+ }
13714
+ if (__EXPERIMENTAL__.suspense && getAttribute.call(node, Q_SUSPENSE_RESULT_PARENT) !== null) {
13715
+ return 130 /* NodeType.ELEMENT_SUSPENSE_RESULT_PARENT */;
13716
+ }
13717
+ return hasAttribute.call(node, Q_PROPS_SEPARATOR) ? 2 /* NodeType.ELEMENT */ : 0 /* NodeType.OTHER */;
13002
13718
  }
13003
13719
  else if (nodeType === 8 /* Node.COMMENT_NODE */) {
13004
13720
  const nodeValue = node.nodeValue || ''; // nodeValue is monomorphic so it does not need fast path
@@ -13036,7 +13752,8 @@ function processVNodeData(document) {
13036
13752
  * @param exitNode The node which represents the last node and we should exit.
13037
13753
  * @param qVNodeRefs Place to store the VNodeRefs
13038
13754
  */
13039
- const walkContainer = (walker, containerNode, node, exitNode, vData, qVNodeRefs, prefix) => {
13755
+ const walkContainer = (walker, containerNode, node, exitNode, vData, qVNodeRefs, qContainerElement, segmentId) => {
13756
+ const isSegment = __EXPERIMENTAL__.suspense && segmentId !== undefined;
13040
13757
  const vData_length = vData.length;
13041
13758
  /// Stores the current element index as the TreeWalker traverses the DOM.
13042
13759
  let elementIdx = 0;
@@ -13052,7 +13769,6 @@ function processVNodeData(document) {
13052
13769
  let elementsToSkip = 0;
13053
13770
  while (isSeparator((ch = vData.charCodeAt(vData_start)))) {
13054
13771
  // Keep consuming the separators and incrementing the vNodeIndex
13055
- // console.log('ADVANCE', vNodeElementIndex, ch, ch - 33);
13056
13772
  elementsToSkip += 1 << (ch - VNodeDataSeparator.ADVANCE_1);
13057
13773
  vData_start++;
13058
13774
  if (vData_start >= vData_length) {
@@ -13075,8 +13791,7 @@ function processVNodeData(document) {
13075
13791
  while (cursor && !(nextNode = nextSibling(cursor))) {
13076
13792
  cursor = cursor.parentNode;
13077
13793
  }
13078
- // console.log('EXIT', nextNode?.outerHTML);
13079
- walkContainer(walker, container, node, nextNode, container.qVnodeData || '', container.qVNodeRefs);
13794
+ walkContainer(walker, container, node, nextNode, container.qVnodeData || '', container.qVNodeRefs, container);
13080
13795
  }
13081
13796
  else if (nodeType === 16 /* NodeType.COMMENT_IGNORE_START */) {
13082
13797
  let islandNode = node;
@@ -13086,7 +13801,6 @@ function processVNodeData(document) {
13086
13801
  throw new Error(`Island inside <!--${node?.nodeValue}--> not found!`);
13087
13802
  }
13088
13803
  } while (getFastNodeType(islandNode) !== 65 /* NodeType.COMMENT_ISLAND_START */);
13089
- nextNode = null;
13090
13804
  }
13091
13805
  else if (nodeType === 64 /* NodeType.COMMENT_ISLAND_END */) {
13092
13806
  // Walk forward to find either the next container-island or the end of the q:ignore block.
@@ -13112,8 +13826,7 @@ function processVNodeData(document) {
13112
13826
  throw new Error(`<!--${node?.nodeValue}--> not closed!`);
13113
13827
  }
13114
13828
  } while (getFastNodeType(nextNode) !== 8 /* NodeType.COMMENT_SKIP_END */);
13115
- // console.log('EXIT', nextNode?.outerHTML);
13116
- walkContainer(walker, node, node, nextNode, '', null);
13829
+ walkContainer(walker, node, node, nextNode, '', null, qContainerElement);
13117
13830
  }
13118
13831
  else if (nodeType === 6 /* NodeType.ELEMENT_SHADOW_ROOT_WRAPPER */) {
13119
13832
  // If we are in a shadow root, we need to get the shadow root element.
@@ -13123,10 +13836,14 @@ function processVNodeData(document) {
13123
13836
  if (shadowRoot) {
13124
13837
  walkContainer(
13125
13838
  // we need to create a new walker for the shadow root
13126
- document.createTreeWalker(shadowRoot, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */), null, shadowRoot, null, '', null);
13839
+ document.createTreeWalker(shadowRoot, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */), null, shadowRoot, null, '', null, null);
13127
13840
  }
13128
13841
  }
13129
13842
  if ((nodeType & 2 /* NodeType.ELEMENT */) === 2 /* NodeType.ELEMENT */) {
13843
+ if (isSegment && node !== containerNode) {
13844
+ const element = node;
13845
+ element._qSegment = segmentId;
13846
+ }
13130
13847
  if (vNodeElementIndex < elementIdx) {
13131
13848
  // VNodeData needs to catch up with the elementIdx
13132
13849
  if (vNodeElementIndex === -1) {
@@ -13135,8 +13852,7 @@ function processVNodeData(document) {
13135
13852
  vData_start = vData_end;
13136
13853
  if (vData_start < vData_length) {
13137
13854
  vNodeElementIndex += howManyElementsToSkip();
13138
- const shouldStoreRef = ch === VNodeDataSeparator.REFERENCE;
13139
- if (shouldStoreRef) {
13855
+ if (ch === VNodeDataSeparator.REFERENCE) {
13140
13856
  // if we need to store the ref handle it here.
13141
13857
  needsToStoreRef = vNodeElementIndex;
13142
13858
  vData_start++;
@@ -13154,29 +13870,78 @@ function processVNodeData(document) {
13154
13870
  vNodeElementIndex = Number.MAX_SAFE_INTEGER;
13155
13871
  }
13156
13872
  }
13157
- // console.log(
13158
- // prefix,
13159
- // 'ELEMENT',
13160
- // nodeType,
13161
- // elementIdx,
13162
- // vNodeElementIndex,
13163
- // (node as any).outerHTML,
13164
- // elementIdx === vNodeElementIndex ? vData.substring(vData_start, vData_end) : ''
13165
- // );
13873
+ const contentBoundaryId = __EXPERIMENTAL__.suspense &&
13874
+ !isSegment &&
13875
+ nodeType === 130 /* NodeType.ELEMENT_SUSPENSE_RESULT_PARENT */
13876
+ ? getAttribute.call(node, Q_SUSPENSE_RESULT_PARENT)
13877
+ : null;
13166
13878
  if (elementIdx === vNodeElementIndex) {
13167
- if (needsToStoreRef === elementIdx) {
13168
- qVNodeRefs.set(elementIdx, node);
13879
+ if (needsToStoreRef === elementIdx && !(isSegment && node === containerNode)) {
13880
+ qVNodeRefs.set(isSegment ? getSegmentVNodeRefId(segmentId, elementIdx) : elementIdx, node);
13881
+ }
13882
+ const data = vData.substring(vData_start, vData_end);
13883
+ if (isSegment && node === containerNode) {
13884
+ const existing = vNodeDataMap.get(node);
13885
+ if (existing === undefined || existing === '') {
13886
+ vNodeDataMap.set(node, data);
13887
+ }
13888
+ else if (existing.charCodeAt(0) === VNodeDataChar.SEPARATOR &&
13889
+ existing.charCodeAt(1) === VNodeDataChar.SEPARATOR &&
13890
+ !existing.endsWith(data)) {
13891
+ vNodeDataMap.set(node, existing + data);
13892
+ }
13893
+ }
13894
+ else {
13895
+ vNodeDataMap.set(node, data);
13169
13896
  }
13170
- const instructions = vData.substring(vData_start, vData_end);
13171
- vNodeDataMap.set(node, instructions);
13172
13897
  }
13173
13898
  elementIdx++;
13899
+ if (__EXPERIMENTAL__.suspense && contentBoundaryId !== null) {
13900
+ processSuspenseContentSegment(qContainerElement, node, contentBoundaryId);
13901
+ nextNode = nextSibling(node);
13902
+ if (nextNode) {
13903
+ walker.currentNode = nextNode;
13904
+ }
13905
+ }
13174
13906
  }
13175
13907
  } while ((node = nextNode || walker.nextNode()));
13176
13908
  };
13909
+ const processVNodeDataScope = (qContainerElement, contentNode, vData, scopeSegmentId) => {
13910
+ qContainerElement.qVNodeRefs ||= new Map();
13911
+ const scopeWalker = document.createTreeWalker(document, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */);
13912
+ scopeWalker.currentNode = contentNode;
13913
+ walkContainer(scopeWalker, contentNode, contentNode, nextSibling(contentNode), vData, qContainerElement.qVNodeRefs, qContainerElement, scopeSegmentId);
13914
+ };
13915
+ const processSuspenseContentSegment = __EXPERIMENTAL__.suspense
13916
+ ? (qContainerElement, contentNode, boundaryId, vData) => {
13917
+ vData ||= qContainerElement?.qSegmentVnodeData?.get(boundaryId);
13918
+ if (qContainerElement && vData) {
13919
+ processVNodeDataScope(qContainerElement, contentNode, vData, boundaryId);
13920
+ }
13921
+ }
13922
+ : null;
13923
+ qDocument.qProcessVNodeDataPatch = (script) => {
13924
+ const qContainerElement = script?.closest('[q\\:container]');
13925
+ const patchSegment = script?.getAttribute(Q_SUSPENSE_RESOLVED);
13926
+ const contentNode = qContainerElement &&
13927
+ (patchSegment
13928
+ ? qContainerElement.querySelector(`[q\\:rp="${patchSegment}"]`)
13929
+ : qContainerElement);
13930
+ if (qContainerElement && contentNode) {
13931
+ processVNodeDataScope(qContainerElement, contentNode, script.textContent, patchSegment || undefined);
13932
+ }
13933
+ };
13934
+ if (__EXPERIMENTAL__.suspense && segmentId !== undefined) {
13935
+ if (segmentContainer && segmentContent && segmentVNodeData) {
13936
+ segmentContainer.qVNodeRefs ||= new Map();
13937
+ processSuspenseContentSegment(segmentContainer, segmentContent, segmentId, segmentVNodeData);
13938
+ }
13939
+ return;
13940
+ }
13941
+ attachVnodeDataAndRefs(document);
13177
13942
  // Walk the tree and process each `q:container` element.
13178
13943
  const walker = document.createTreeWalker(document, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */);
13179
- walkContainer(walker, null, walker.firstChild(), null, '', null);
13944
+ walkContainer(walker, null, walker.firstChild(), null, '', null, null);
13180
13945
  }
13181
13946
  const isSeparator = (ch) =>
13182
13947
  /* `!` */ VNodeDataSeparator.ADVANCE_1 <= ch && ch <= VNodeDataSeparator.ADVANCE_8192; /* `.` */
@@ -13228,9 +13993,15 @@ function getDomContainerFromQContainerElement(qContainerElement) {
13228
13993
  function _getQContainerElement(element) {
13229
13994
  return element.closest(QContainerSelector);
13230
13995
  }
13231
- const isDomContainer = (container) => {
13232
- return container instanceof DomContainer;
13233
- };
13996
+ function getOutOfOrderStreamingScript(boundaryId, content) {
13997
+ const segmentId = String(boundaryId);
13998
+ const qContainerElement = content?.closest(QContainerSelector);
13999
+ const qContainer = qContainerElement?.qContainer;
14000
+ if (qContainer) {
14001
+ processOutOfOrderSegmentVNodeData(qContainer.element.ownerDocument, segmentId, content);
14002
+ processSegmentStateScripts(qContainer, segmentId);
14003
+ }
14004
+ }
13234
14005
  /** @internal */
13235
14006
  class DomContainer extends _SharedContainer {
13236
14007
  element;
@@ -13245,6 +14016,7 @@ class DomContainer extends _SharedContainer {
13245
14016
  vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
13246
14017
  $rawStateData$;
13247
14018
  $stateData$;
14019
+ $rootForwardRefs$ = null;
13248
14020
  $styleIds$ = null;
13249
14021
  constructor(element) {
13250
14022
  super({}, element.getAttribute(QLocaleAttr));
@@ -13261,20 +14033,20 @@ class DomContainer extends _SharedContainer {
13261
14033
  this.$rawStateData$ = [];
13262
14034
  this.$stateData$ = [];
13263
14035
  const document = this.element.ownerDocument;
13264
- if (!document.qVNodeData) {
14036
+ if (!document.qVNodeDataProcessed) {
13265
14037
  processVNodeData(document);
13266
14038
  }
14039
+ if (__EXPERIMENTAL__.suspense && document.querySelector('template[q\\:r]')) {
14040
+ document.qProcessOOOS ||= getOutOfOrderStreamingScript;
14041
+ }
13267
14042
  this.$qFuncs$ = getQFuncs(document, this.$instanceHash$) || EMPTY_ARRAY;
13268
14043
  this.$setServerData$();
13269
14044
  element.setAttribute(QContainerAttr, "resumed" /* QContainerValue.RESUMED */);
13270
14045
  element.qContainer = this;
13271
14046
  element.qDestroy = () => this.$destroy$();
13272
- const qwikStates = element.querySelectorAll('script[type="qwik/state"]');
13273
- if (qwikStates.length !== 0) {
13274
- const lastState = qwikStates[qwikStates.length - 1];
13275
- this.$rawStateData$ = JSON.parse(lastState.textContent);
13276
- preprocessState(this.$rawStateData$, this);
13277
- this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
14047
+ this.$processRootStateScript$();
14048
+ if (__EXPERIMENTAL__.suspense) {
14049
+ processSegmentStateScripts(this);
13278
14050
  }
13279
14051
  this.$hoistStyles$();
13280
14052
  if (!qTest && element.isConnected) {
@@ -13291,8 +14063,34 @@ class DomContainer extends _SharedContainer {
13291
14063
  el.qContainer = undefined;
13292
14064
  el.qVnodeData = undefined;
13293
14065
  el.qVNodeRefs = undefined;
14066
+ if (__EXPERIMENTAL__.suspense) {
14067
+ el.qSegmentVnodeData = undefined;
14068
+ }
13294
14069
  el.removeAttribute(QContainerAttr);
13295
- el.ownerDocument.qVNodeData = undefined;
14070
+ const document = el.ownerDocument;
14071
+ const hasContainers = document.querySelector(QContainerSelector) !== null;
14072
+ if (!hasContainers) {
14073
+ document.qVNodeData = undefined;
14074
+ document.qVNodeDataProcessed = undefined;
14075
+ document.qProcessVNodeDataPatch = undefined;
14076
+ }
14077
+ if (__EXPERIMENTAL__.suspense) {
14078
+ if (!hasContainers) {
14079
+ document.qProcessOOOS = undefined;
14080
+ }
14081
+ }
14082
+ }
14083
+ $processRootStateScript$() {
14084
+ const rootState = this.element.querySelector(`${this.$stateScriptSelector$()}:not(${QStatePatchAttrSelector})`);
14085
+ if (rootState) {
14086
+ this.$rawStateData$ = JSON.parse(rootState.textContent);
14087
+ preprocessState(this.$rawStateData$, this);
14088
+ this.$rootForwardRefs$ = this.$forwardRefs$;
14089
+ this.$stateData$ = wrapDeserializerProxy(this, this.$rawStateData$);
14090
+ }
14091
+ }
14092
+ $stateScriptSelector$() {
14093
+ return `script[type="qwik/state"][q\\:instance="${this.$instanceHash$}"]`;
13296
14094
  }
13297
14095
  /**
13298
14096
  * The first time we render we need to hoist the styles. (Meaning we need to move all styles from
@@ -13326,7 +14124,7 @@ class DomContainer extends _SharedContainer {
13326
14124
  if (qDev && host) {
13327
14125
  if (typeof document !== 'undefined') {
13328
14126
  setErrorPayload(host, err);
13329
- markVNodeDirty(this, host, 256 /* ChoreBits.ERROR_WRAP */);
14127
+ markVNodeDirty(this, host, 512 /* ChoreBits.ERROR_WRAP */);
13330
14128
  }
13331
14129
  if (err && err instanceof Error) {
13332
14130
  if (!('hostElement' in err)) {
@@ -13423,6 +14221,9 @@ class DomContainer extends _SharedContainer {
13423
14221
  $getObjectById$ = (id) => {
13424
14222
  return getObjectById(id, this.$stateData$);
13425
14223
  };
14224
+ $getForwardRef$(id) {
14225
+ return this.$rootForwardRefs$?.[id];
14226
+ }
13426
14227
  getSyncFn(id) {
13427
14228
  const fn = this.$qFuncs$[id];
13428
14229
  isDev && assertTrue(typeof fn === 'function', 'Invalid reference: ' + id);
@@ -14562,7 +15363,8 @@ const intToStr = (nu) => {
14562
15363
  return nu.toString(36);
14563
15364
  };
14564
15365
  const getNextUniqueIndex = (container) => {
14565
- return intToStr(container.$currentUniqueId$++);
15366
+ const rootContainer = getRootContainer(container);
15367
+ return intToStr(rootContainer.$currentUniqueId$++);
14566
15368
  };
14567
15369
 
14568
15370
  /** @public */
@@ -15350,7 +16152,7 @@ const eachCmpTask = async ({ track }) => {
15350
16152
  const context = tryGetInvokeContext();
15351
16153
  const host = context.$hostElement$;
15352
16154
  const container = context.$container$;
15353
- markVNodeDirty(container, host, 128 /* ChoreBits.RECONCILE */);
16155
+ markVNodeDirty(container, host, 256 /* ChoreBits.RECONCILE */);
15354
16156
  const isSsr = qTest ? isServerPlatform() : isServer;
15355
16157
  if (isSsr) {
15356
16158
  await container.$renderPromise$;
@@ -15380,49 +16182,17 @@ const createRevealContext = (props) => {
15380
16182
  /** @internal */
15381
16183
  const revealCanReveal = () => {
15382
16184
  const registration = _captures[0];
15383
- if (registration === null) {
15384
- return true;
15385
- }
15386
- const reveal = registration.reveal;
15387
- const current = registration.item;
15388
- const items = reveal.items;
15389
16185
  // `version` is monotonic; the branch keeps the subscription read from being dropped by minifiers.
15390
- if (reveal.version.value < 0) {
16186
+ if (registration !== null && registration.reveal.version.value < 0) {
15391
16187
  return false;
15392
16188
  }
15393
- switch (reveal.order) {
15394
- case 'together':
15395
- for (let i = 0; i < items.length; i++) {
15396
- if (items[i].boundary.pending.untrackedValue > 0) {
15397
- return false;
15398
- }
15399
- }
15400
- return true;
15401
- case 'sequential':
15402
- for (let i = 0; i < items.length; i++) {
15403
- const item = items[i];
15404
- if (item === current) {
15405
- return true;
15406
- }
15407
- if (item.boundary.pending.untrackedValue > 0) {
15408
- return false;
15409
- }
15410
- }
15411
- return true;
15412
- case 'reverse':
15413
- for (let i = items.length - 1; i >= 0; i--) {
15414
- const item = items[i];
15415
- if (item === current) {
15416
- return true;
15417
- }
15418
- if (item.boundary.pending.untrackedValue > 0) {
15419
- return false;
15420
- }
15421
- }
15422
- return true;
15423
- default:
15424
- return true;
16189
+ if (qTest ? isServerPlatform() : !isBrowser$1) {
16190
+ const ooos = registration?.reveal.ooos;
16191
+ if (ooos) {
16192
+ return ooos.canReveal(registration);
16193
+ }
15425
16194
  }
16195
+ return canRevealRegistration(registration);
15426
16196
  };
15427
16197
  /** @internal */
15428
16198
  const revealCleanupTask = ({ cleanup }) => {
@@ -15457,6 +16227,15 @@ const useRevealBoundary = (boundary) => {
15457
16227
  }
15458
16228
  return registration;
15459
16229
  };
16230
+ const getOutOfOrderCoordinator = (reveal) => {
16231
+ const coordinator = reveal.ooos;
16232
+ if (coordinator) {
16233
+ return coordinator;
16234
+ }
16235
+ const nextCoordinator = createOutOfOrderRevealCoordinator(reveal.order, reveal.collapsed);
16236
+ reveal.ooos = noSerialize(nextCoordinator);
16237
+ return nextCoordinator;
16238
+ };
15460
16239
  /** @internal */
15461
16240
  const revealCmp = (props) => {
15462
16241
  if (!__EXPERIMENTAL__.suspense) {
@@ -15464,11 +16243,32 @@ const revealCmp = (props) => {
15464
16243
  }
15465
16244
  const reveal = useConstant(createRevealContext, props);
15466
16245
  useContextProvider(RevealContext, reveal);
16246
+ const isServerEnv = qTest ? isServerPlatform() : !isBrowser$1;
16247
+ if (__EXPERIMENTAL__.suspense && isServerEnv && isOutOfOrderStreaming()) {
16248
+ const coordinator = getOutOfOrderCoordinator(reveal);
16249
+ return /*#__PURE__*/ _jsxSorted(SSRRevealSlot, {
16250
+ coordinator,
16251
+ }, null, null, 0, 'u7_0');
16252
+ }
15467
16253
  return /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 0, 'u7_0');
15468
16254
  };
15469
16255
  /** @public @experimental */
15470
16256
  const Reveal = /*#__PURE__*/ componentQrl(
15471
16257
  /*#__PURE__*/ inlinedQrl(revealCmp, '_reC'));
16258
+ const SSRRevealSlot = __EXPERIMENTAL__.suspense
16259
+ ? /*#__PURE__*/ createInternalServerComponent((ssr, jsx, _options, enqueue) => {
16260
+ const coordinator = jsx.varProps.coordinator;
16261
+ enqueue(() => {
16262
+ const script = coordinator.script();
16263
+ if (!script) {
16264
+ return;
16265
+ }
16266
+ ssr.emitOutOfOrderExecutorIfNeeded();
16267
+ ssr.emitInlineScript(script);
16268
+ });
16269
+ enqueue(/*#__PURE__*/ _jsxSorted(Slot, null, null, null, 0, 'u7_0'));
16270
+ })
16271
+ : null;
15472
16272
 
15473
16273
  const _hf0 = (p0, p1, p2, p3) => ({
15474
16274
  display: p1.value === 'fallback' &&
@@ -15480,9 +16280,9 @@ const _hf0 = (p0, p1, p2, p3) => ({
15480
16280
  });
15481
16281
  const _hf0_str = '{display:p1.value==="fallback"&&p0.fallback!=null&&p0.fallback!==false&&(p2===null||p2.value||!p3.reveal.collapsed)?"contents":"none"}';
15482
16282
  const _hf1 = (p0, p1, p2) => ({
15483
- display: (p2 === null || p2.value) && (p1.value === 'content' || p0.showStale) ? 'contents' : 'none',
16283
+ display: (p1.value === 'content' || p0.showStale) && (p2 === null || p2.value) ? 'contents' : 'none',
15484
16284
  });
15485
- const _hf1_str = '{display:(p2===null||p2.value)&&(p1.value==="content"||p0.showStale)?"contents":"none"}';
16285
+ const _hf1_str = '{display:(p1.value==="content"||p0.showStale)&&(p2===null||p2.value)?"contents":"none"}';
15486
16286
  /** @internal */
15487
16287
  const suspenseTask = ({ track, cleanup }) => {
15488
16288
  const cursorBoundary = _captures[0], props = _captures[1], state = _captures[2], revealRegistration = _captures[3];
@@ -15519,21 +16319,154 @@ const suspenseCmp = (props) => {
15519
16319
  state,
15520
16320
  revealRegistration,
15521
16321
  ]));
15522
- return /*#__PURE__*/ _jsxSorted(Fragment, null, null, [
15523
- /*#__PURE__*/ _jsxSorted('div', {
16322
+ const isServerEnv = qTest ? isServerPlatform() : !isBrowser$1;
16323
+ const isServerOutOfOrder = isServerEnv && isOutOfOrderStreaming();
16324
+ const outOfOrderBoundaryId = isServerOutOfOrder ? nextOutOfOrderSuspenseId() : 0;
16325
+ const outOfOrderRevealBoundary = isServerOutOfOrder
16326
+ ? (revealRegistration?.reveal.ooos?.register(revealRegistration) ?? null)
16327
+ : null;
16328
+ const showOutOfOrderFallback = isServerOutOfOrder && shouldRenderFallback(props.fallback, outOfOrderRevealBoundary);
16329
+ const outOfOrderBoundaryState = showOutOfOrderFallback && isPositiveDelay(props.delay)
16330
+ ? { contentResolved: false, delayTimer: null }
16331
+ : null;
16332
+ const outOfOrderFallbackStyle = isServerOutOfOrder
16333
+ ? /*#__PURE__*/ _fnSignal(_hf0, [props, state, canReveal, revealRegistration], _hf0_str)
16334
+ : null;
16335
+ const contentStyle = /*#__PURE__*/ _fnSignal(_hf1, [props, state, canReveal], _hf1_str);
16336
+ const fallbackHost = (isServerOutOfOrder
16337
+ ? /*#__PURE__*/ _jsxSorted(SSRFallback, {
16338
+ boundary: outOfOrderBoundaryState,
16339
+ delay: props.delay,
16340
+ fallbackStyle: outOfOrderFallbackStyle,
16341
+ showFallback: showOutOfOrderFallback,
16342
+ state,
16343
+ }, null, _wrapProp(props, 'fallback'), 1, null)
16344
+ : /*#__PURE__*/ _jsxSorted('div', {
15524
16345
  style: _fnSignal(_hf0, [props, state, canReveal, revealRegistration], _hf0_str),
15525
- }, null, _wrapProp(props, 'fallback'), 1, null),
15526
- /*#__PURE__*/ _jsxSorted('div', null, {
15527
- style: _fnSignal(_hf1, [props, state, canReveal], _hf1_str),
15528
- },
15529
- /*#__PURE__*/ _jsxSorted(Slot, {
15530
- [QCursorBoundary]: cursorBoundary,
15531
- }, null, null, 3, 'u6_0'), 1, null),
15532
- ], 1, 'u6_1');
16346
+ }, null, _wrapProp(props, 'fallback'), 1, null));
16347
+ return [
16348
+ fallbackHost,
16349
+ /*#__PURE__*/ _jsxSorted('div', null, isServerOutOfOrder
16350
+ ? {
16351
+ [QSuspenseResultParent]: String(outOfOrderBoundaryId),
16352
+ style: contentStyle,
16353
+ }
16354
+ : {
16355
+ style: contentStyle,
16356
+ },
16357
+ /*#__PURE__*/ _jsxSorted(isServerOutOfOrder ? SSRDeferredSlot : Slot, isServerOutOfOrder
16358
+ ? {
16359
+ [QCursorBoundary]: cursorBoundary,
16360
+ boundary: outOfOrderBoundaryState,
16361
+ boundaryId: outOfOrderBoundaryId,
16362
+ contentStyle,
16363
+ reveal: outOfOrderRevealBoundary,
16364
+ }
16365
+ : {
16366
+ [QCursorBoundary]: cursorBoundary,
16367
+ }, null, null, 3, 'u6_0'), 1, null),
16368
+ ];
15533
16369
  };
15534
16370
  /** @public @experimental */
15535
16371
  const Suspense = /*#__PURE__*/ componentQrl(
15536
- /*#__PURE__*/ inlinedQrl(suspenseCmp, '_suC'));
16372
+ /*#__PURE__*/ inlinedQrl(suspenseCmp, SUSPENSE_QRL_SYMBOL));
16373
+ const SSRFallback = __EXPERIMENTAL__.suspense
16374
+ ? /*#__PURE__*/ createInternalServerComponent((ssr, jsx, _options, enqueue) => {
16375
+ const boundaryState = jsx.varProps.boundary;
16376
+ const delay = jsx.varProps.delay;
16377
+ const fallbackStyle = jsx.varProps.fallbackStyle;
16378
+ const showFallback = jsx.varProps.showFallback === true;
16379
+ const state = jsx.varProps.state;
16380
+ if (showFallback && !isPositiveDelay(delay)) {
16381
+ state.value = 'fallback';
16382
+ }
16383
+ else if (boundaryState && showFallback && isPositiveDelay(delay)) {
16384
+ enqueue(() => scheduleOutOfOrderFallbackDelay(ssr, boundaryState, state, delay));
16385
+ }
16386
+ enqueue(
16387
+ /*#__PURE__*/ _jsxSorted('div', {
16388
+ style: fallbackStyle,
16389
+ }, null, jsx.children, 1, null));
16390
+ })
16391
+ : null;
16392
+ const SSRDeferredSlot = __EXPERIMENTAL__.suspense
16393
+ ? /*#__PURE__*/ createInternalServerComponent(async (ssr, jsx, options) => {
16394
+ const boundaryId = jsx.varProps.boundaryId ?? ssr.nextOutOfOrderId();
16395
+ const contentSegment = `${boundaryId}`;
16396
+ const boundaryState = jsx.varProps.boundary;
16397
+ const contentStyle = jsx.varProps.contentStyle;
16398
+ const revealBoundary = jsx.varProps.reveal;
16399
+ const content = ssr.segment(contentSegment, createClaimedDeferredSlot(ssr, jsx, options), options);
16400
+ writeOutOfOrderPlaceholder(ssr, boundaryId);
16401
+ ssr.emitOutOfOrderExecutorIfNeeded();
16402
+ ssr.queueOutOfOrderSegment(content.then((rendered) => emitRenderedOutOfOrderSegment(ssr, boundaryId, contentSegment, rendered, contentStyle, revealBoundary, boundaryState)));
16403
+ })
16404
+ : null;
16405
+ function createClaimedDeferredSlot(ssr, jsx, options) {
16406
+ const componentFrame = options.parentComponentFrame;
16407
+ if (!componentFrame) {
16408
+ return /*#__PURE__*/ _jsxSorted(Slot, jsx.varProps, jsx.constProps, jsx.children, jsx.flags, jsx.key);
16409
+ }
16410
+ const slotName = resolveSlotName(componentFrame.componentNode, jsx, ssr);
16411
+ const slotDefaultChildren = (jsx.children || null);
16412
+ const slotChildren = componentFrame.claimChildrenForSlot(slotName) || slotDefaultChildren;
16413
+ if (slotDefaultChildren && slotChildren !== slotDefaultChildren) {
16414
+ ssr.addUnclaimedProjection(componentFrame, QDefaultSlot, slotDefaultChildren);
16415
+ }
16416
+ return /*#__PURE__*/ _jsxSorted(Slot, jsx.varProps, jsx.constProps, slotChildren, jsx.flags, jsx.key);
16417
+ }
16418
+ async function emitRenderedOutOfOrderSegment(ssr, boundaryId, segmentId, rendered, contentStyle, revealBoundary, boundaryState) {
16419
+ markOutOfOrderContentResolved(boundaryState);
16420
+ revealBoundary?.resolve();
16421
+ await ssr.$runQueuedRender$(async () => {
16422
+ ssr.addRoot(contentStyle);
16423
+ const result = await rendered.container.$finalizeOutOfOrderSegment$(segmentId, rendered);
16424
+ writeOutOfOrderResolvedTemplate(ssr, boundaryId, result.html, revealBoundary);
16425
+ ssr.emitOutOfOrderSegmentScripts(result.scripts);
16426
+ ssr.emitInlineScript(`qO(${boundaryId})`);
16427
+ // qO() is the browser-visible handoff for this segment, so flush it immediately.
16428
+ await ssr.streamHandler.flush();
16429
+ });
16430
+ }
16431
+ function markOutOfOrderContentResolved(boundaryState) {
16432
+ if (!boundaryState) {
16433
+ return;
16434
+ }
16435
+ boundaryState.contentResolved = true;
16436
+ if (boundaryState.delayTimer) {
16437
+ clearTimeout(boundaryState.delayTimer);
16438
+ boundaryState.delayTimer = null;
16439
+ }
16440
+ }
16441
+ function scheduleOutOfOrderFallbackDelay(ssr, boundaryState, state, delay) {
16442
+ boundaryState.delayTimer = setTimeout(() => {
16443
+ boundaryState.delayTimer = null;
16444
+ void ssr.$runQueuedRender$(async () => {
16445
+ if (boundaryState.contentResolved) {
16446
+ return;
16447
+ }
16448
+ state.value = 'fallback';
16449
+ ssr.emitBackpatchDataAndExecutorIfNeeded();
16450
+ await ssr.streamHandler.flush();
16451
+ });
16452
+ }, delay);
16453
+ }
16454
+ function isPositiveDelay(delay) {
16455
+ return typeof delay === 'number' && Number.isFinite(delay) && delay > 0;
16456
+ }
16457
+ function shouldRenderFallback(fallback, revealBoundary) {
16458
+ return (fallback != null &&
16459
+ fallback !== false &&
16460
+ (revealBoundary === null || revealBoundary.showFallback));
16461
+ }
16462
+ function writeOutOfOrderPlaceholder(ssr, boundaryId) {
16463
+ ssr.write(`<template ${QSuspenseResolved}="${boundaryId}"></template>`);
16464
+ }
16465
+ function writeOutOfOrderResolvedTemplate(ssr, boundaryId, html, revealBoundary) {
16466
+ ssr.write(`<template ${QSuspenseResolved}="${boundaryId}"${revealBoundary?.attrs ?? ''}>`);
16467
+ ssr.write(html);
16468
+ ssr.write('</template>');
16469
+ }
15537
16470
 
15538
16471
  // keep this import from core/build so the cjs build works
15539
16472
  /**
@@ -15630,5 +16563,5 @@ if (import.meta.hot) {
15630
16563
  });
15631
16564
  }
15632
16565
 
15633
- export { $, Each, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, Reveal, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, Suspense, _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, _createDeserializeContainer, createQRL as _createQRL, _deserialize, _dumpState, eachCmp as _eaC, eachCmpTask as _eaT, _executeSsrChores, _fnSignal, getAsyncLocalStorage as _getAsyncLocalStorage, _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, _qrlWithChunk, _qrlWithChunkDEV, revealCmp as _reC, revealCanReveal as _reR, revealCleanupTask as _reT, _regSymbol, _removeProjection, _res, _resolveContextWithoutSequentialScope, _restProps, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, suspenseCmp as _suC, suspenseTask as _suT, scheduleTask as _task, _updateProjectionProps, _useHmr, _val, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getElementName as _vnode_getElementName, vnode_getFirstChild as _vnode_getFirstChild, vnode_getProp as _vnode_getProp, vnode_getVNodeForChildNode as _vnode_getVNodeForChildNode, vnode_insertBefore as _vnode_insertBefore, vnode_isElementVNode as _vnode_isElementVNode, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_newVirtual as _vnode_newVirtual, vnode_remove as _vnode_remove, vnode_setProp as _vnode_setProp, 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, getClientManifest, 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 };
16566
+ export { $, Each, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, Reveal, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, Suspense, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, SubscriptionPatch as _SubscriptionPatch, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, _createDeserializeContainer, createQRL as _createQRL, _deserialize, _dumpState, eachCmp as _eaC, eachCmpTask as _eaT, _executeSsrChores, _fnSignal, getAsyncLocalStorage as _getAsyncLocalStorage, _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, _qrlWithChunk, _qrlWithChunkDEV, revealCmp as _reC, revealCanReveal as _reR, revealCleanupTask as _reT, _regSymbol, _removeProjection, _res, _resolveContextWithoutSequentialScope, _restProps, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, suspenseCmp as _suC, suspenseTask as _suT, scheduleTask as _task, _updateProjectionProps, _useHmr, _val, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getElementName as _vnode_getElementName, vnode_getFirstChild as _vnode_getFirstChild, vnode_getProp as _vnode_getProp, vnode_getVNodeForChildNode as _vnode_getVNodeForChildNode, vnode_insertBefore as _vnode_insertBefore, vnode_isElementVNode as _vnode_isElementVNode, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_newVirtual as _vnode_newVirtual, vnode_remove as _vnode_remove, vnode_setProp as _vnode_setProp, 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, getClientManifest, 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 };
15634
16567
  //# sourceMappingURL=core.mjs.map